| /* |
| 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; |
| } |