blob: 48ac06244d36c9686957542f06557c8c0d12a9e1 [file] [log] [blame]
/*
Copyright (c) 2012, Code Aurora Forum. 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 Code Aurora Forum, Inc. 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.
*/
#include <pthread.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <semaphore.h>
#include "mm_jpeg_dbg.h"
#include "mm_jpeg_interface.h"
#include "mm_jpeg.h"
/* define max num of supported concurrent jpeg jobs by OMX engine.
* Current, only one per time */
#define NUM_MAX_JPEG_CNCURRENT_JOBS 1
#define INPUT_PORT_MAIN 0
#define INPUT_PORT_THUMBNAIL 2
#define OUTPUT_PORT 1
void mm_jpeg_job_wait_for_event(mm_jpeg_obj *my_obj, uint32_t evt_mask);
void mm_jpeg_job_wait_for_cmd_complete(mm_jpeg_obj *my_obj,
int cmd,
int status);
OMX_ERRORTYPE mm_jpeg_etbdone(OMX_HANDLETYPE hComponent,
OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE mm_jpeg_ftbdone(OMX_HANDLETYPE hComponent,
OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE mm_jpeg_handle_omx_event(OMX_HANDLETYPE hComponent,
OMX_PTR pAppData,
OMX_EVENTTYPE eEvent,
OMX_U32 nData1,
OMX_U32 nData2,
OMX_PTR pEventData);
/* special queue functions for job queue */
int32_t mm_jpeg_queue_update_flag(mm_jpeg_queue_t* queue, uint32_t job_id, uint8_t flag);
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_client_id(mm_jpeg_queue_t* queue, uint32_t client_hdl);
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_job_id(mm_jpeg_queue_t* queue, uint32_t job_id);
int32_t mm_jpeg_omx_load(mm_jpeg_obj* my_obj)
{
int32_t rc = 0;
OMX_CALLBACKTYPE callbacks;
callbacks.EmptyBufferDone = mm_jpeg_etbdone;
callbacks.FillBufferDone = mm_jpeg_ftbdone;
callbacks.EventHandler = mm_jpeg_handle_omx_event;
rc = OMX_GetHandle(&my_obj->omx_handle,
"OMX.qcom.image.jpeg.encoder",
(void*)my_obj,
&callbacks);
if (0 != rc) {
CDBG_ERROR("%s : OMX_GetHandle failed (%d)",__func__, rc);
return rc;
}
rc = OMX_Init();
if (0 != rc) {
CDBG_ERROR("%s : OMX_Init failed (%d)",__func__, rc);
OMX_FreeHandle(my_obj->omx_handle);
}
return rc;
}
int32_t mm_jpeg_omx_unload(mm_jpeg_obj *my_obj)
{
int32_t rc = 0;
rc = OMX_Deinit();
OMX_FreeHandle(my_obj->omx_handle);
return rc;
}
int32_t mm_jpeg_omx_abort_job(mm_jpeg_obj *my_obj, mm_jpeg_job_entry* job_entry)
{
int32_t rc = 0;
uint8_t i, j;
OMX_SendCommand(my_obj->omx_handle, OMX_CommandFlush, 0, NULL);
mm_jpeg_job_wait_for_event(my_obj,
MM_JPEG_EVENT_MASK_JPEG_DONE|MM_JPEG_EVENT_MASK_JPEG_ABORT|MM_JPEG_EVENT_MASK_JPEG_ERROR);
CDBG("%s:waitForEvent: OMX_CommandFlush: DONE", __func__);
OMX_SendCommand(my_obj->omx_handle, OMX_CommandStateSet, OMX_StateIdle, NULL);
OMX_SendCommand(my_obj->omx_handle, OMX_CommandStateSet, OMX_StateLoaded, NULL);
/* free buffers used in OMX processing */
for (i = 0; i < JPEG_SRC_IMAGE_TYPE_MAX; i++) {
for (j = 0; j < job_entry->src_bufs[i].num_bufs; j++) {
if (NULL != job_entry->src_bufs[i].bufs[j].buf_header) {
OMX_FreeBuffer(my_obj->omx_handle,
job_entry->src_bufs[i].bufs[j].portIdx,
job_entry->src_bufs[i].bufs[j].buf_header);
}
}
}
OMX_FreeBuffer(my_obj->omx_handle,
job_entry->sink_buf.portIdx,
job_entry->sink_buf.buf_header);
return rc;
}
/* TODO: needs revisit after omx lib supports multi src buffers */
int32_t mm_jpeg_omx_config_main_buffer_offset(mm_jpeg_obj* my_obj, src_image_buffer_info *src_buf)
{
int32_t rc = 0;
uint8_t i;
OMX_INDEXTYPE buf_offset_idx;
omx_jpeg_buffer_offset buffer_offset;
for (i = 0; i < src_buf->num_bufs; i++) {
OMX_GetExtensionIndex(my_obj->omx_handle,
"omx.qcom.jpeg.exttype.buffer_offset",
&buf_offset_idx);
memset(&buffer_offset, 0, sizeof(buffer_offset));
switch (src_buf->img_fmt) {
case JPEG_SRC_IMAGE_FMT_YUV:
if (1 == src_buf->src_image[i].offset.num_planes) {
buffer_offset.yOffset =
src_buf->src_image[i].offset.sp.y_offset;
buffer_offset.cbcrOffset =
src_buf->src_image[i].offset.sp.cbcr_offset;
buffer_offset.totalSize =
src_buf->src_image[i].offset.sp.len;
} else {
buffer_offset.yOffset =
src_buf->src_image[i].offset.mp[0].offset;
buffer_offset.cbcrOffset =
src_buf->src_image[i].offset.mp[1].offset;
buffer_offset.totalSize =
src_buf->src_image[i].offset.frame_len;
}
CDBG("%s: idx=%d, yOffset =%d, cbcrOffset =%d, totalSize = %d\n",
__func__, i, buffer_offset.yOffset, buffer_offset.cbcrOffset, buffer_offset.totalSize);
OMX_SetParameter(my_obj->omx_handle, buf_offset_idx, &buffer_offset);
break;
case JPEG_SRC_IMAGE_FMT_BITSTREAM:
/* TODO: need visit here when bit stream is supported */
buffer_offset.yOffset =
src_buf->bit_stream[i].data_offset;
buffer_offset.totalSize =
src_buf->bit_stream[i].buf_size;
CDBG("%s: idx=%d, yOffset =%d, cbcrOffset =%d, totalSize = %d\n",
__func__, i, buffer_offset.yOffset, buffer_offset.cbcrOffset, buffer_offset.totalSize);
OMX_SetParameter(my_obj->omx_handle, buf_offset_idx, &buffer_offset);
break;
default:
break;
}
}
return rc;
}
/* TODO: needs revisit after omx lib supports multi src buffers */
int32_t mm_jpeg_omx_config_port(mm_jpeg_obj* my_obj, src_image_buffer_info *src_buf, int port_idx)
{
int32_t rc = 0;
uint8_t i;
OMX_PARAM_PORTDEFINITIONTYPE input_port;
for (i = 0; i < src_buf->num_bufs; i++) {
memset(&input_port, 0, sizeof(input_port));
input_port.nPortIndex = port_idx;
OMX_GetParameter(my_obj->omx_handle, OMX_IndexParamPortDefinition, &input_port);
input_port.format.image.nFrameWidth = src_buf->src_dim.width;
input_port.format.image.nFrameHeight =src_buf->src_dim.height;
input_port.format.image.nStride = src_buf->src_dim.width;
input_port.format.image.nSliceHeight = src_buf->src_dim.height;
switch (src_buf->img_fmt) {
case JPEG_SRC_IMAGE_FMT_YUV:
input_port.nBufferSize = src_buf->src_image[i].offset.frame_len;
break;
case JPEG_SRC_IMAGE_FMT_BITSTREAM:
input_port.nBufferSize = src_buf->bit_stream[i].buf_size;
break;
}
OMX_SetParameter(my_obj->omx_handle, OMX_IndexParamPortDefinition, &input_port);
}
return rc;
}
omx_jpeg_color_format map_jpeg_format(mm_jpeg_color_format color_fmt)
{
switch (color_fmt) {
case MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V2:
return OMX_YCRCBLP_H2V2;
case MM_JPEG_COLOR_FORMAT_YCBCRLP_H2V2:
return OMX_YCBCRLP_H2V2;
case MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V1:
return OMX_YCRCBLP_H2V1;
case MM_JPEG_COLOR_FORMAT_YCBCRLP_H2V1:
return OMX_YCBCRLP_H2V1;
case MM_JPEG_COLOR_FORMAT_YCRCBLP_H1V2:
return OMX_YCRCBLP_H1V2;
case MM_JPEG_COLOR_FORMAT_YCBCRLP_H1V2:
return OMX_YCBCRLP_H1V2;
case MM_JPEG_COLOR_FORMAT_YCRCBLP_H1V1:
return OMX_YCRCBLP_H1V1;
case MM_JPEG_COLOR_FORMAT_YCBCRLP_H1V1:
return OMX_YCBCRLP_H1V1;
case MM_JPEG_COLOR_FORMAT_RGB565:
return OMX_RGB565;
case MM_JPEG_COLOR_FORMAT_RGB888:
return OMX_RGB888;
case MM_JPEG_COLOR_FORMAT_RGBa:
return OMX_RGBa;
case MM_JPEG_COLOR_FORMAT_BITSTREAM:
/* TODO: need to change to new bitstream value once omx interface changes */
return OMX_JPEG_BITSTREAM_H2V2;
default:
return OMX_JPEG_COLOR_FORMAT_MAX;
}
}
int32_t mm_jpeg_omx_config_user_preference(mm_jpeg_obj* my_obj, mm_jpeg_encode_job* job)
{
int32_t rc = 0;
OMX_INDEXTYPE user_pref_idx;
omx_jpeg_user_preferences user_preferences;
memset(&user_preferences, 0, sizeof(user_preferences));
user_preferences.color_format =
map_jpeg_format(job->encode_parm.buf_info.src_imgs.src_img[JPEG_SRC_IMAGE_TYPE_MAIN].color_format);
if (job->encode_parm.buf_info.src_imgs.src_img_num > 1) {
user_preferences.thumbnail_color_format =
map_jpeg_format(job->encode_parm.buf_info.src_imgs.src_img[JPEG_SRC_IMAGE_TYPE_THUMB].color_format);
}
OMX_GetExtensionIndex(my_obj->omx_handle,
"omx.qcom.jpeg.exttype.user_preferences",
&user_pref_idx);
CDBG("%s:User Preferences: color_format %d, thumbnail_color_format = %d",
__func__, user_preferences.color_format, user_preferences.thumbnail_color_format);
OMX_SetParameter(my_obj->omx_handle, user_pref_idx, &user_preferences);
return rc;
}
int32_t mm_jpeg_omx_config_thumbnail(mm_jpeg_obj* my_obj, mm_jpeg_encode_job* job)
{
int32_t rc = -1;
OMX_INDEXTYPE thumb_idx, q_idx;
omx_jpeg_thumbnail thumbnail;
omx_jpeg_thumbnail_quality quality;
src_image_buffer_info *src_buf =
&(job->encode_parm.buf_info.src_imgs.src_img[JPEG_SRC_IMAGE_TYPE_THUMB]);
/* config port */
rc = mm_jpeg_omx_config_port(my_obj, src_buf, INPUT_PORT_THUMBNAIL);
if (0 != rc) {
CDBG_ERROR("%s: config port failed", __func__);
return rc;
}
/* check crop boundary */
if ((src_buf->crop.width == 0) || (src_buf->crop.height == 0) ||
(src_buf->crop.width + src_buf->crop.offset_x > src_buf->src_dim.width) ||
(src_buf->crop.height + src_buf->crop.offset_y > src_buf->src_dim.height)) {
CDBG_ERROR("%s: invalid crop boundary (%d, %d) offset (%d, %d) out of (%d, %d)",
__func__,
src_buf->crop.width,
src_buf->crop.height,
src_buf->crop.offset_x,
src_buf->crop.offset_y,
src_buf->src_dim.width,
src_buf->src_dim.height);
return rc;
}
memset(&thumbnail, 0, sizeof(thumbnail));
/* thumbnail crop info */
thumbnail.cropWidth = CEILING2(src_buf->crop.width);
thumbnail.cropHeight = CEILING2(src_buf->crop.height);
thumbnail.left = src_buf->crop.offset_x;
thumbnail.top = src_buf->crop.offset_y;
/* thumbnail output dimention */
thumbnail.width = src_buf->out_dim.width;
thumbnail.height = src_buf->out_dim.height;
/* set scaling flag */
if (thumbnail.left > 0 || thumbnail.top > 0 ||
src_buf->crop.width != src_buf->out_dim.width ||
src_buf->crop.height != src_buf->out_dim.height) {
thumbnail.scaling = 1;
}
/* set omx thumbnail info */
OMX_GetExtensionIndex(my_obj->omx_handle, "omx.qcom.jpeg.exttype.thumbnail", &thumb_idx);
OMX_SetParameter(my_obj->omx_handle, thumb_idx, &thumbnail);
OMX_GetExtensionIndex(my_obj->omx_handle, "omx.qcom.jpeg.exttype.thumbnail_quality", &q_idx);
OMX_GetParameter(my_obj->omx_handle, q_idx, &quality);
quality.nQFactor = src_buf->quality;
OMX_SetParameter(my_obj->omx_handle, q_idx, &quality);
rc = 0;
return rc;
}
int32_t mm_jpeg_omx_config_main_crop(mm_jpeg_obj* my_obj, src_image_buffer_info *src_buf)
{
int32_t rc = 0;
OMX_CONFIG_RECTTYPE rect_type;
/* error check first */
if (src_buf->crop.width + src_buf->crop.offset_x > src_buf->src_dim.width ||
src_buf->crop.height + src_buf->crop.offset_y > src_buf->src_dim.height) {
CDBG_ERROR("%s: invalid crop boundary (%d, %d) out of (%d, %d)", __func__,
src_buf->crop.width + src_buf->crop.offset_x,
src_buf->crop.height + src_buf->crop.offset_y,
src_buf->src_dim.width,
src_buf->src_dim.height);
return -1;
}
if (src_buf->crop.width && src_buf->crop.width) {
/* Scaler information */
memset(&rect_type, 0, sizeof(rect_type));
rect_type.nWidth = CEILING2(src_buf->crop.width);
rect_type.nHeight = CEILING2(src_buf->crop.width);
rect_type.nLeft = src_buf->crop.offset_x;
rect_type.nTop = src_buf->crop.offset_y;
rect_type.nPortIndex = OUTPUT_PORT;
OMX_SetConfig(my_obj->omx_handle, OMX_IndexConfigCommonInputCrop, &rect_type);
if (src_buf->out_dim.width && src_buf->out_dim.height) {
memset(&rect_type, 0, sizeof(rect_type));
rect_type.nWidth = src_buf->out_dim.width;
rect_type.nHeight = src_buf->out_dim.height;
rect_type.nPortIndex = OUTPUT_PORT;
OMX_SetConfig(my_obj->omx_handle, OMX_IndexConfigCommonOutputCrop, &rect_type);
}
} else {
CDBG_ERROR("%s: There is no main image scaling information", __func__);
rc = -1;
}
return rc;
}
int32_t mm_jpeg_omx_config_main(mm_jpeg_obj* my_obj, mm_jpeg_encode_job* job)
{
int32_t rc = 0;
src_image_buffer_info *src_buf =
&(job->encode_parm.buf_info.src_imgs.src_img[JPEG_SRC_IMAGE_TYPE_MAIN]);
OMX_IMAGE_PARAM_QFACTORTYPE q_factor;
/* config port */
rc = mm_jpeg_omx_config_port(my_obj, src_buf, INPUT_PORT_MAIN);
if (0 != rc) {
CDBG_ERROR("%s: config port failed", __func__);
return rc;
}
/* config buffer offset */
rc = mm_jpeg_omx_config_main_buffer_offset(my_obj, src_buf);
if (0 != rc) {
CDBG_ERROR("%s: config buffer offset failed", __func__);
return rc;
}
/* config crop */
rc = mm_jpeg_omx_config_main_crop(my_obj, src_buf);
if (0 != rc) {
CDBG_ERROR("%s: config crop failed", __func__);
return rc;
}
/* set quality */
memset(&q_factor, 0, sizeof(q_factor));
q_factor.nPortIndex = INPUT_PORT_MAIN;
OMX_GetParameter(my_obj->omx_handle, OMX_IndexParamQFactor, &q_factor);
q_factor.nQFactor = src_buf->quality;
OMX_SetParameter(my_obj->omx_handle, OMX_IndexParamQFactor, &q_factor);
return rc;
}
int32_t mm_jpeg_omx_config_common(mm_jpeg_obj* my_obj, mm_jpeg_encode_job* job)
{
int32_t rc;
int i;
OMX_INDEXTYPE exif_idx;
omx_jpeg_exif_info_tag tag;
OMX_CONFIG_ROTATIONTYPE rotate;
/* config user prefernces */
rc = mm_jpeg_omx_config_user_preference(my_obj, job);
if (0 != rc) {
CDBG_ERROR("%s: config user preferences failed", __func__);
return rc;
}
/* set rotation */
memset(&rotate, 0, sizeof(rotate));
rotate.nPortIndex = OUTPUT_PORT;
rotate.nRotation = job->encode_parm.rotation;
OMX_SetConfig(my_obj->omx_handle, OMX_IndexConfigCommonRotate, &rotate);
CDBG("%s: Set rotation to %d\n", __func__, job->encode_parm.rotation);
/* set exif tags */
OMX_GetExtensionIndex(my_obj->omx_handle, "omx.qcom.jpeg.exttype.exif", &exif_idx);
for(i = 0; i < job->encode_parm.exif_numEntries; i++) {
memcpy(&tag, job->encode_parm.exif_data + i, sizeof(omx_jpeg_exif_info_tag));
OMX_SetParameter(my_obj->omx_handle, exif_idx, &tag);
}
return rc;
}
int32_t mm_jpeg_omx_use_buf(mm_jpeg_obj* my_obj,
src_image_buffer_info *src_buf,
mm_jpeg_omx_src_buf* omx_src_buf,
int port_idx)
{
int32_t rc = 0;
uint8_t i;
omx_jpeg_pmem_info pmem_info;
omx_src_buf->num_bufs = src_buf->num_bufs;
for (i = 0; i < src_buf->num_bufs; i++) {
memset(&pmem_info, 0, sizeof(pmem_info));
switch (src_buf->img_fmt) {
case JPEG_SRC_IMAGE_FMT_YUV:
pmem_info.fd = src_buf->src_image[i].fd;
pmem_info.offset = 0;
omx_src_buf->bufs[i].portIdx = port_idx;
OMX_UseBuffer(my_obj->omx_handle,
&omx_src_buf->bufs[i].buf_header,
port_idx,
&pmem_info,
src_buf->src_image[i].offset.frame_len,
(void *)src_buf->src_image[i].buf_vaddr);
break;
case JPEG_SRC_IMAGE_FMT_BITSTREAM:
pmem_info.fd = src_buf->bit_stream[i].fd;
pmem_info.offset = 0;
omx_src_buf->bufs[i].portIdx = port_idx;
OMX_UseBuffer(my_obj->omx_handle,
&omx_src_buf->bufs[i].buf_header,
port_idx,
&pmem_info,
src_buf->bit_stream[i].buf_size,
(void *)src_buf->bit_stream[i].buf_vaddr);
break;
default:
rc = -1;
break;
}
}
return rc;
}
int32_t mm_jpeg_omx_encode(mm_jpeg_obj* my_obj, mm_jpeg_job_entry* job_entry)
{
int32_t rc = 0;
uint8_t i;
mm_jpeg_encode_job* job = &job_entry->job.encode_job;
uint8_t has_thumbnail = job->encode_parm.buf_info.src_imgs.src_img_num > 1? 1 : 0;
src_image_buffer_info *src_buf[JPEG_SRC_IMAGE_TYPE_MAX];
mm_jpeg_omx_src_buf *omx_src_buf[JPEG_SRC_IMAGE_TYPE_MAX];
/* config main img */
rc = mm_jpeg_omx_config_main(my_obj, job);
if (0 != rc) {
CDBG_ERROR("%s: config main img failed", __func__);
return rc;
}
/* config thumbnail */
rc = mm_jpeg_omx_config_thumbnail(my_obj, job);
if (0 != rc) {
CDBG_ERROR("%s: config thumbnail img failed", __func__);
return rc;
}
/* common config */
rc = mm_jpeg_omx_config_common(my_obj, job);
if (0 != rc) {
CDBG_ERROR("%s: config common failed", __func__);
return rc;
}
/* config input omx buffer for main input */
src_buf[JPEG_SRC_IMAGE_TYPE_MAIN] = &(job->encode_parm.buf_info.src_imgs.src_img[JPEG_SRC_IMAGE_TYPE_MAIN]);
omx_src_buf[JPEG_SRC_IMAGE_TYPE_MAIN] = &job_entry->src_bufs[JPEG_SRC_IMAGE_TYPE_MAIN];
rc = mm_jpeg_omx_use_buf(my_obj,
src_buf[JPEG_SRC_IMAGE_TYPE_MAIN],
omx_src_buf[JPEG_SRC_IMAGE_TYPE_MAIN],
INPUT_PORT_MAIN);
if (0 != rc) {
CDBG_ERROR("%s: config main input omx buffer failed", __func__);
return rc;
}
/* config input omx buffer for thumbnail input if there is thumbnail */
if (has_thumbnail) {
src_buf[JPEG_SRC_IMAGE_TYPE_THUMB] = &(job->encode_parm.buf_info.src_imgs.src_img[JPEG_SRC_IMAGE_TYPE_THUMB]);
omx_src_buf[JPEG_SRC_IMAGE_TYPE_THUMB] = &job_entry->src_bufs[JPEG_SRC_IMAGE_TYPE_THUMB];
rc = mm_jpeg_omx_use_buf(my_obj,
src_buf[JPEG_SRC_IMAGE_TYPE_THUMB],
omx_src_buf[JPEG_SRC_IMAGE_TYPE_THUMB],
INPUT_PORT_THUMBNAIL);
if (0 != rc) {
CDBG_ERROR("%s: config thumbnail input omx buffer failed", __func__);
return rc;
}
}
/* config output omx buffer */
job_entry->sink_buf.portIdx = OUTPUT_PORT;
OMX_UseBuffer(my_obj->omx_handle,
&job_entry->sink_buf.buf_header,
job_entry->sink_buf.portIdx,
NULL,
job->encode_parm.buf_info.sink_img.buf_len,
(void *)job->encode_parm.buf_info.sink_img.buf_vaddr);
/* wait for OMX state ready */
mm_jpeg_job_wait_for_cmd_complete(my_obj, OMX_CommandStateSet, OMX_StateIdle);
CDBG("%s: State changed to OMX_StateIdle\n", __func__);
/* start OMX encoding by sending executing cmd */
OMX_SendCommand(my_obj->omx_handle, OMX_CommandStateSet, OMX_StateExecuting, NULL);
mm_jpeg_job_wait_for_cmd_complete(my_obj, OMX_CommandStateSet, OMX_StateExecuting);
/* start input fedding and output writing */
for (i = 0; i < job_entry->src_bufs[JPEG_SRC_IMAGE_TYPE_MAIN].num_bufs; i++) {
OMX_EmptyThisBuffer(my_obj->omx_handle,
job_entry->src_bufs[JPEG_SRC_IMAGE_TYPE_MAIN].bufs[i].buf_header);
}
if (has_thumbnail) {
for (i = 0; i < job_entry->src_bufs[JPEG_SRC_IMAGE_TYPE_THUMB].num_bufs; i++) {
OMX_EmptyThisBuffer(my_obj->omx_handle,
job_entry->src_bufs[JPEG_SRC_IMAGE_TYPE_THUMB].bufs[i].buf_header);
}
}
OMX_FillThisBuffer(my_obj->omx_handle, job_entry->sink_buf.buf_header);
return rc;
}
static void *mm_jpeg_notify_thread(void *data)
{
mm_jpeg_job_q_node_t* job_node = (mm_jpeg_job_q_node_t *)data;
mm_jpeg_job_entry * job_entry = &job_node->entry;
mm_jpeg_obj* my_obj = (mm_jpeg_obj *)job_entry->jpeg_obj;
void* node = NULL;
int32_t rc = 0;
if (NULL == my_obj) {
CDBG_ERROR("%s: jpeg obj is NULL", __func__);
return NULL;
}
/* Add to cb queue */
rc = mm_jpeg_queue_enq(&my_obj->cb_q, data);
if (0 != rc) {
CDBG_ERROR("%s: enqueue into cb_q failed", __func__);
return NULL;
}
/* call cb */
if (NULL != job_entry->job.encode_job.jpeg_cb) {
/* has callback, send CB */
job_entry->job.encode_job.jpeg_cb(job_entry->job_status,
job_entry->thumbnail_dropped,
job_entry->client_hdl,
job_entry->jobId,
job_entry->job.encode_job.encode_parm.buf_info.sink_img.buf_vaddr,
job_entry->jpeg_size,
job_entry->job.encode_job.userdata);
} else {
CDBG_ERROR("%s: no cb provided, no action", __func__);
}
/* Remove from cb queue */
node = mm_jpeg_queue_remove_job_by_job_id(&my_obj->cb_q, job_entry->jobId);
if (NULL != node) {
free(node);
}
return NULL;
}
/* process encoding job */
int32_t mm_jpeg_process_encoding_job(mm_jpeg_obj *my_obj, mm_jpeg_job_q_node_t* job_node)
{
int32_t rc = 0;
mm_jpeg_job_entry* job_entry = &job_node->entry;
/* call OMX_Encode */
rc = mm_jpeg_omx_encode(my_obj, job_entry);
if (0 == rc) {
/* sent encode cmd to OMX, queue job into ongoing queue */
rc = mm_jpeg_queue_enq(&my_obj->ongoing_job_q, job_node);
} else {
/* OMX encode failed, notify error through callback */
job_entry->job_status = JPEG_JOB_STATUS_ERROR;
if (NULL != job_entry->job.encode_job.jpeg_cb) {
/* has callback, create a thread to send CB */
pthread_create(&job_entry->cb_pid,
NULL,
mm_jpeg_notify_thread,
(void *)job_node);
} else {
CDBG_ERROR("%s: no cb provided, return here", __func__);
free(job_node);
}
}
return rc;
}
/* process job (encoding/decoding) */
int32_t mm_jpeg_process_job(mm_jpeg_obj *my_obj, mm_jpeg_job_q_node_t* job_node)
{
int32_t rc = 0;
switch (job_node->entry.job.job_type) {
case JPEG_JOB_TYPE_ENCODE:
rc = mm_jpeg_process_encoding_job(my_obj, job_node);
break;
default:
CDBG_ERROR("%s: job type not supported (%d)",
__func__, job_node->entry.job.job_type);
rc = -1;
break;
}
return rc;
}
static void *mm_jpeg_jobmgr_thread(void *data)
{
int rc = 0;
int running = 1;
uint32_t num_ongoing_jobs = 0;
mm_jpeg_obj *my_obj = (mm_jpeg_obj*)data;
mm_jpeg_job_cmd_thread_t *cmd_thread =
(mm_jpeg_job_cmd_thread_t *)data;
mm_jpeg_job_q_node_t* node = NULL;
do {
do {
rc = sem_wait(&cmd_thread->job_sem);
if (rc != 0 && errno != EINVAL) {
CDBG_ERROR("%s: sem_wait error (%s)",
__func__, strerror(errno));
return NULL;
}
} while (rc != 0);
/* check ongoing q size */
num_ongoing_jobs = mm_jpeg_queue_get_size(&my_obj->ongoing_job_q);
if (num_ongoing_jobs >= NUM_MAX_JPEG_CNCURRENT_JOBS) {
continue;
}
pthread_mutex_lock(&my_obj->job_lock);
/* can go ahead with new work */
node = (mm_jpeg_job_q_node_t*)mm_jpeg_queue_deq(&cmd_thread->job_queue);
if (node != NULL) {
switch (node->type) {
case MM_JPEG_CMD_TYPE_JOB:
rc = mm_jpeg_process_job(my_obj, node);
if (0 != rc) {
/* free node in error case */
free(node);
}
break;
case MM_JPEG_CMD_TYPE_EXIT:
default:
/* free node */
free(node);
/* set running flag to false */
running = 0;
break;
}
}
pthread_mutex_unlock(&my_obj->job_lock);
} while (running);
return NULL;
}
int32_t mm_jpeg_jobmgr_thread_launch(mm_jpeg_obj * my_obj)
{
int32_t rc = 0;
mm_jpeg_job_cmd_thread_t * job_mgr = &my_obj->job_mgr;
sem_init(&job_mgr->job_sem, 0, 0);
mm_jpeg_queue_init(&job_mgr->job_queue);
/* launch the thread */
pthread_create(&job_mgr->pid,
NULL,
mm_jpeg_jobmgr_thread,
(void *)my_obj);
return rc;
}
int32_t mm_jpeg_jobmgr_thread_release(mm_jpeg_obj * my_obj)
{
int32_t rc = 0;
mm_jpeg_job_cmd_thread_t * cmd_thread = &my_obj->job_mgr;
mm_jpeg_job_q_node_t* node =
(mm_jpeg_job_q_node_t *)malloc(sizeof(mm_jpeg_job_q_node_t));
if (NULL == node) {
CDBG_ERROR("%s: No memory for mm_jpeg_job_q_node_t", __func__);
return -1;
}
memset(node, 0, sizeof(mm_jpeg_job_q_node_t));
node->type = MM_JPEG_CMD_TYPE_EXIT;
mm_jpeg_queue_enq(&cmd_thread->job_queue, node);
sem_post(&cmd_thread->job_sem);
/* wait until cmd thread exits */
if (pthread_join(cmd_thread->pid, NULL) != 0) {
CDBG("%s: pthread dead already\n", __func__);
}
mm_jpeg_queue_deinit(&cmd_thread->job_queue);
sem_destroy(&cmd_thread->job_sem);
memset(cmd_thread, 0, sizeof(mm_jpeg_job_cmd_thread_t));
return rc;
}
int32_t mm_jpeg_init(mm_jpeg_obj *my_obj)
{
int32_t rc = 0;
/* init locks */
pthread_mutex_init(&my_obj->job_lock, NULL);
pthread_mutex_init(&my_obj->omx_evt_lock, NULL);
pthread_cond_init(&my_obj->omx_evt_cond, NULL);
/* init ongoing job queue */
rc = mm_jpeg_queue_init(&my_obj->ongoing_job_q);
/* init job semaphore and launch jobmgr thread */
CDBG("%s : Launch jobmgr thread",__func__);
rc = mm_jpeg_jobmgr_thread_launch(my_obj);
/* load OMX */
rc = mm_jpeg_omx_load(my_obj);
if (0 != rc) {
/* roll back in error case */
mm_jpeg_jobmgr_thread_release(my_obj);
mm_jpeg_queue_deinit(&my_obj->ongoing_job_q);
pthread_mutex_destroy(&my_obj->job_lock);
pthread_mutex_destroy(&my_obj->omx_evt_lock);
pthread_cond_destroy(&my_obj->omx_evt_cond);
}
return rc;
}
int32_t mm_jpeg_deinit(mm_jpeg_obj *my_obj)
{
int32_t rc = 0;
/* unload OMX engine */
rc = mm_jpeg_omx_unload(my_obj);
/* release jobmgr thread */
rc = mm_jpeg_jobmgr_thread_release(my_obj);
/* deinit ongoing job queue */
rc = mm_jpeg_queue_deinit(&my_obj->ongoing_job_q);
/* destroy locks */
pthread_mutex_destroy(&my_obj->job_lock);
pthread_mutex_destroy(&my_obj->omx_evt_lock);
pthread_cond_destroy(&my_obj->omx_evt_cond);
return rc;
}
uint32_t mm_jpeg_new_client(mm_jpeg_obj *my_obj)
{
uint32_t client_hdl = 0;
uint8_t idx;
if (my_obj->num_clients >= MAX_JPEG_CLIENT_NUM) {
CDBG_ERROR("%s: num of clients reached limit", __func__);
return client_hdl;
}
for (idx = 0; idx < MAX_JPEG_CLIENT_NUM; idx++) {
if (0 == my_obj->clnt_mgr[idx].is_used) {
break;
}
}
if (idx < MAX_JPEG_CLIENT_NUM) {
/* client entry avail */
/* generate client handler by index */
client_hdl = mm_jpeg_util_generate_handler(idx);
/* update client entry */
my_obj->clnt_mgr[idx].is_used = 1;
my_obj->clnt_mgr[idx].client_handle = client_hdl;
/* increse client count */
my_obj->num_clients++;
}
return client_hdl;
}
int32_t mm_jpeg_start_job(mm_jpeg_obj *my_obj,
uint32_t client_hdl,
mm_jpeg_job* job,
uint32_t* jobId)
{
int32_t rc = -1;
uint8_t clnt_idx = 0;
uint32_t job_id = 0;
mm_jpeg_job_q_node_t* node = NULL;
*jobId = 0;
/* check if valid client */
clnt_idx = mm_jpeg_util_get_index_by_handler(client_hdl);
if (clnt_idx >= MAX_JPEG_CLIENT_NUM ) {
CDBG_ERROR("%s: invalid client with handler (%d)", __func__, client_hdl);
return rc;
}
/* generate client handler by index */
job_id = mm_jpeg_util_generate_handler(clnt_idx);
/* enqueue new job into todo job queue */
node = (mm_jpeg_job_q_node_t *)malloc(sizeof(mm_jpeg_job_q_node_t));
if (NULL == node) {
CDBG_ERROR("%s: No memory for mm_jpeg_job_q_node_t", __func__);
return -rc;
}
memset(node, 0, sizeof(mm_jpeg_job_q_node_t));
node->type = MM_JPEG_CMD_TYPE_JOB;
node->entry.client_hdl = client_hdl;
node->entry.jobId = job_id;
memcpy(&node->entry.job, job, sizeof(mm_jpeg_job));
node->entry.jpeg_obj = (void*)my_obj; /* save a ptr to jpeg_obj */
rc = mm_jpeg_queue_enq(&my_obj->job_mgr.job_queue, node);
if (0 == rc) {
sem_post(&my_obj->job_mgr.job_sem);
*jobId = job_id;
}
return rc;
}
int32_t mm_jpeg_abort_job(mm_jpeg_obj *my_obj,
uint32_t client_hdl,
uint32_t jobId)
{
int32_t rc = -1;
uint8_t clnt_idx = 0;
void * node = NULL;
mm_jpeg_job_entry* job_entry = NULL;
/* check if valid client */
clnt_idx = mm_jpeg_util_get_index_by_handler(client_hdl);
if (clnt_idx >= MAX_JPEG_CLIENT_NUM ) {
CDBG_ERROR("%s: invalid client with handler (%d)", __func__, client_hdl);
return rc;
}
pthread_mutex_lock(&my_obj->job_lock);
/* abort job if in ongoing queue */
node = mm_jpeg_queue_remove_job_by_job_id(&my_obj->ongoing_job_q, jobId);
if (NULL != node) {
/* find job that is OMX ongoing, ask OMX to abort the job */
job_entry = &(((mm_jpeg_job_q_node_t *)node)->entry);
rc = mm_jpeg_omx_abort_job(my_obj, job_entry);
free(node);
goto abort_done;
}
/* abort job if in todo queue */
node = mm_jpeg_queue_remove_job_by_job_id(&my_obj->job_mgr.job_queue, jobId);
if (NULL != node) {
/* simply delete it */
free(node);
goto abort_done;
}
/* abort job if in cb queue */
node = mm_jpeg_queue_remove_job_by_job_id(&my_obj->cb_q, jobId);
if (NULL != node) {
/* join cb thread */
job_entry = &(((mm_jpeg_job_q_node_t *)node)->entry);
if (pthread_join(job_entry->cb_pid, NULL) != 0) {
CDBG("%s: pthread dead already\n", __func__);
}
free(node);
}
abort_done:
pthread_mutex_unlock(&my_obj->job_lock);
/* wake up jobMgr thread to work on new job if there is any */
sem_post(&my_obj->job_mgr.job_sem);
return rc;
}
int32_t mm_jpeg_close(mm_jpeg_obj *my_obj, uint32_t client_hdl)
{
int32_t rc = -1;
uint8_t clnt_idx = 0;
void* node = NULL;
mm_jpeg_job_entry* job_entry = NULL;
/* check if valid client */
clnt_idx = mm_jpeg_util_get_index_by_handler(client_hdl);
if (clnt_idx >= MAX_JPEG_CLIENT_NUM ) {
CDBG_ERROR("%s: invalid client with handler (%d)", __func__, client_hdl);
return rc;
}
/* abort all jobs from the client */
pthread_mutex_lock(&my_obj->job_lock);
/* abort job if in ongoing queue */
node = mm_jpeg_queue_remove_job_by_client_id(&my_obj->ongoing_job_q, client_hdl);
while (NULL != node) {
/* find job that is OMX ongoing, ask OMX to abort the job */
job_entry = &(((mm_jpeg_job_q_node_t *)node)->entry);
rc = mm_jpeg_omx_abort_job(my_obj, job_entry);
free(node);
/* find next job from ongoing queue that belongs to this client */
node = mm_jpeg_queue_remove_job_by_client_id(&my_obj->ongoing_job_q, client_hdl);
}
/* abort job if in todo queue */
node = mm_jpeg_queue_remove_job_by_client_id(&my_obj->job_mgr.job_queue, client_hdl);
while (NULL != node) {
/* simply delete the job if in todo queue */
free(node);
/* find next job from todo queue that belongs to this client */
node = mm_jpeg_queue_remove_job_by_client_id(&my_obj->job_mgr.job_queue, client_hdl);
}
/* abort job if in cb queue */
node = mm_jpeg_queue_remove_job_by_client_id(&my_obj->cb_q, client_hdl);
while (NULL != node) {
/* join cb thread */
job_entry = &(((mm_jpeg_job_q_node_t *)node)->entry);
if (pthread_join(job_entry->cb_pid, NULL) != 0) {
CDBG("%s: pthread dead already\n", __func__);
}
free(node);
/* find next job from cb queue that belongs to this client */
node = mm_jpeg_queue_remove_job_by_client_id(&my_obj->cb_q, client_hdl);
}
pthread_mutex_unlock(&my_obj->job_lock);
/* invalidate client entry */
memset(&my_obj->clnt_mgr[clnt_idx], 0, sizeof(mm_jpeg_client_t));
return rc;
}
void mm_jpeg_job_wait_for_event(mm_jpeg_obj *my_obj, uint32_t evt_mask)
{
pthread_mutex_lock(&my_obj->omx_evt_lock);
while (!(my_obj->omx_evt_rcvd.evt & evt_mask)) {
pthread_cond_wait(&my_obj->omx_evt_cond, &my_obj->omx_evt_lock);
}
CDBG("%s:done", __func__);
pthread_mutex_unlock(&my_obj->omx_evt_lock);
}
void mm_jpeg_job_wait_for_cmd_complete(mm_jpeg_obj *my_obj,
int cmd,
int status)
{
pthread_mutex_lock(&my_obj->omx_evt_lock);
while (!((my_obj->omx_evt_rcvd.evt & MM_JPEG_EVENT_MASK_CMD_COMPLETE) &&
(my_obj->omx_evt_rcvd.omx_value1 == cmd) &&
(my_obj->omx_evt_rcvd.omx_value2 == status))) {
pthread_cond_wait(&my_obj->omx_evt_cond, &my_obj->omx_evt_lock);
}
CDBG("%s:done", __func__);
pthread_mutex_unlock(&my_obj->omx_evt_lock);
}
OMX_ERRORTYPE mm_jpeg_etbdone(OMX_HANDLETYPE hComponent,
OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE* pBuffer)
{
/* no process needed for etbdone, return here */
return 0;
}
OMX_ERRORTYPE mm_jpeg_ftbdone(OMX_HANDLETYPE hComponent,
OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE* pBuffer)
{
int rc = 0;
void* node = NULL;
mm_jpeg_job_entry* job_entry = NULL;
mm_jpeg_obj * my_obj = (mm_jpeg_obj*)pAppData;
if (NULL != my_obj) {
CDBG_ERROR("%s: pAppData is NULL, return here", __func__);
return rc;
}
/* signal JPEG_DONE event */
pthread_mutex_lock(&my_obj->omx_evt_lock);
my_obj->omx_evt_rcvd.evt = MM_JPEG_EVENT_MASK_JPEG_DONE;
pthread_cond_signal(&my_obj->omx_evt_cond);
pthread_mutex_unlock(&my_obj->omx_evt_lock);
/* If OMX can support multi encoding, it should provide a way to pass jobID.
* then we can find job by jobID from ongoing queue.
* For now, since OMX only support one job per time, we simply dequeue it. */
pthread_mutex_lock(&my_obj->job_lock);
node = mm_jpeg_queue_deq(&my_obj->ongoing_job_q);
if (NULL != node) {
job_entry = &(((mm_jpeg_job_q_node_t *)node)->entry);;
/* find job that is OMX ongoing */
job_entry->jpeg_size = pBuffer->nFilledLen;
job_entry->job_status = JPEG_JOB_STATUS_DONE;
CDBG("%s:filled len = %u, status = %d",
__func__, job_entry->jpeg_size, job_entry->job_status);
if (NULL != job_entry->job.encode_job.jpeg_cb) {
/* has callback, create a thread to send CB */
pthread_create(&job_entry->cb_pid,
NULL,
mm_jpeg_notify_thread,
node);
} else {
CDBG_ERROR("%s: no cb provided, return here", __func__);
free(node);
}
}
pthread_mutex_unlock(&my_obj->job_lock);
/* Wake up jobMgr thread to work on next job if there is any */
sem_post(&my_obj->job_mgr.job_sem);
return rc;
}
OMX_ERRORTYPE mm_jpeg_handle_omx_event(OMX_HANDLETYPE hComponent,
OMX_PTR pAppData,
OMX_EVENTTYPE eEvent,
OMX_U32 nData1,
OMX_U32 nData2,
OMX_PTR pEventData)
{
int rc = 0;
void* node = NULL;
mm_jpeg_job_entry* job_entry = NULL;
mm_jpeg_obj * my_obj = (mm_jpeg_obj*)pAppData;
uint32_t jobId = 0;
if (NULL != my_obj) {
CDBG_ERROR("%s: pAppData is NULL, return here", __func__);
return rc;
}
/* signal event */
switch (eEvent) {
case OMX_EVENT_JPEG_ABORT:
{
/* signal error evt */
pthread_mutex_lock(&my_obj->omx_evt_lock);
my_obj->omx_evt_rcvd.evt = MM_JPEG_EVENT_MASK_JPEG_ABORT;
pthread_cond_signal(&my_obj->omx_evt_cond);
pthread_mutex_unlock(&my_obj->omx_evt_lock);
}
break;
case OMX_EventError:
{
switch (nData1) {
case OMX_EVENT_THUMBNAIL_DROPPED:
{
uint8_t thumbnail_dropped_flag = 1;
mm_jpeg_queue_update_flag(&my_obj->ongoing_job_q,
jobId,
thumbnail_dropped_flag);
}
break;
case OMX_EVENT_JPEG_ERROR:
{
/* signal error evt */
pthread_mutex_lock(&my_obj->omx_evt_lock);
my_obj->omx_evt_rcvd.evt = MM_JPEG_EVENT_MASK_JPEG_ERROR;
pthread_cond_signal(&my_obj->omx_evt_cond);
pthread_mutex_unlock(&my_obj->omx_evt_lock);
/* send CB for error case */
/* If OMX can support multi encoding, it should provide a way to pass jobID.
* then we can find job by jobID from ongoing queue.
* For now, since OMX only support one job per time, we simply dequeue it. */
pthread_mutex_lock(&my_obj->job_lock);
node = mm_jpeg_queue_deq(&my_obj->ongoing_job_q);
if (NULL != node) {
job_entry = &(((mm_jpeg_job_q_node_t *)node)->entry);;
/* find job that is OMX ongoing */
job_entry->job_status = JPEG_JOB_STATUS_ERROR;
if (NULL != job_entry->job.encode_job.jpeg_cb) {
/* has callback, create a thread to send CB */
pthread_create(&job_entry->cb_pid,
NULL,
mm_jpeg_notify_thread,
node);
} else {
CDBG_ERROR("%s: no cb provided, return here", __func__);
free(node);
}
}
pthread_mutex_unlock(&my_obj->job_lock);
/* Wake up jobMgr thread to work on next job if there is any */
sem_post(&my_obj->job_mgr.job_sem);
}
break;
default:
break;
}
}
break;
case OMX_EventCmdComplete:
{
/* signal cmd complete evt */
pthread_mutex_lock(&my_obj->omx_evt_lock);
my_obj->omx_evt_rcvd.evt = MM_JPEG_EVENT_MASK_CMD_COMPLETE;
my_obj->omx_evt_rcvd.omx_value1 = nData1;
my_obj->omx_evt_rcvd.omx_value2 = nData2;
pthread_cond_signal(&my_obj->omx_evt_cond);
pthread_mutex_unlock(&my_obj->omx_evt_lock);
}
break;
default:
break;
}
return rc;
}
/* remove the first job from the queue with matching client handle */
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_client_id(mm_jpeg_queue_t* queue, uint32_t client_hdl)
{
mm_jpeg_q_node_t* node = NULL;
mm_jpeg_job_q_node_t* data = NULL;
mm_jpeg_job_q_node_t* job_node = NULL;
struct cam_list *head = NULL;
struct cam_list *pos = NULL;
pthread_mutex_lock(&queue->lock);
head = &queue->head.list;
pos = head->next;
while(pos != head) {
node = member_of(pos, mm_jpeg_q_node_t, list);
data = (mm_jpeg_job_q_node_t *)node->data;
if (data && (data->entry.client_hdl == client_hdl)) {
job_node = data;
cam_list_del_node(&node->list);
queue->size--;
break;
}
pos = pos->next;
}
pthread_mutex_unlock(&queue->lock);
return job_node;
}
/* remove job from the queue with matching job id */
mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_job_id(mm_jpeg_queue_t* queue, uint32_t job_id)
{
mm_jpeg_q_node_t* node = NULL;
mm_jpeg_job_q_node_t* data = NULL;
mm_jpeg_job_q_node_t* job_node = NULL;
struct cam_list *head = NULL;
struct cam_list *pos = NULL;
pthread_mutex_lock(&queue->lock);
head = &queue->head.list;
pos = head->next;
while(pos != head) {
node = member_of(pos, mm_jpeg_q_node_t, list);
data = (mm_jpeg_job_q_node_t *)node->data;
if (data && (data->entry.jobId == job_id)) {
job_node = data;
cam_list_del_node(&node->list);
queue->size--;
break;
}
pos = pos->next;
}
pthread_mutex_unlock(&queue->lock);
return job_node;
}
/* update thumbnail dropped flag in job queue */
int32_t mm_jpeg_queue_update_flag(mm_jpeg_queue_t* queue, uint32_t job_id, uint8_t flag)
{
int32_t rc = 0;
mm_jpeg_q_node_t* node = NULL;
mm_jpeg_job_q_node_t* data = NULL;
mm_jpeg_job_q_node_t* job_node = NULL;
struct cam_list *head = NULL;
struct cam_list *pos = NULL;
pthread_mutex_lock(&queue->lock);
head = &queue->head.list;
pos = head->next;
while(pos != head) {
node = member_of(pos, mm_jpeg_q_node_t, list);
data = (mm_jpeg_job_q_node_t *)node->data;
if (data && (data->entry.jobId == job_id)) {
job_node = data;
break;
}
pos = pos->next;
}
if (job_node) {
/* find matching job for its job id */
job_node->entry.thumbnail_dropped = flag;
} else {
CDBG_ERROR("%s: No entry for jobId = %d", __func__, job_id);
rc = -1;
}
pthread_mutex_unlock(&queue->lock);
return rc;
}