/* 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. * */ // System dependencies #include <pthread.h> #include <errno.h> #include <fcntl.h> #include <math.h> #define PRCTL_H <SYSTEM_HEADER_PREFIX/prctl.h> #include PRCTL_H #ifdef LOAD_ADSP_RPC_LIB #include <dlfcn.h> #include <stdlib.h> #endif // JPEG dependencies #include "mm_jpeg_dbg.h" #include "mm_jpeg_interface.h" #include "mm_jpeg.h" #include "mm_jpeg_inlines.h" #define ENCODING_MODE_PARALLEL 1 #define META_KEYFILE QCAMERA_DUMP_FRM_LOCATION"metadata.key" /** * minimal resolution needed for normal mode of ops */ #define MM_JPEG_MIN_NOM_RESOLUTION 7680000 /*8MP*/ #ifdef MM_JPEG_USE_PIPELINE #undef MM_JPEG_CONCURRENT_SESSIONS_COUNT #define MM_JPEG_CONCURRENT_SESSIONS_COUNT 1 #endif OMX_ERRORTYPE mm_jpeg_ebd(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); OMX_ERRORTYPE mm_jpeg_fbd(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer); OMX_ERRORTYPE mm_jpeg_event_handler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); static int32_t mm_jpegenc_destroy_job(mm_jpeg_job_session_t *p_session); static void mm_jpegenc_job_done(mm_jpeg_job_session_t *p_session); mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_dst_ptr( mm_jpeg_queue_t* queue, void * dst_ptr); static OMX_ERRORTYPE mm_jpeg_session_configure(mm_jpeg_job_session_t *p_session); /** mm_jpeg_get_comp_name: * * Arguments: * None * * Return: * Encoder component name * * Description: * Get the name of omx component to be used for jpeg encoding * **/ inline char* mm_jpeg_get_comp_name() { #ifdef MM_JPEG_USE_PIPELINE return "OMX.qcom.image.jpeg.encoder_pipeline"; #else return "OMX.qcom.image.jpeg.encoder"; #endif } /** mm_jpeg_session_send_buffers: * * Arguments: * @data: job session * * Return: * OMX error values * * Description: * Send the buffers to OMX layer * **/ OMX_ERRORTYPE mm_jpeg_session_send_buffers(void *data) { uint32_t i = 0; mm_jpeg_job_session_t* p_session = (mm_jpeg_job_session_t *)data; OMX_ERRORTYPE ret = OMX_ErrorNone; QOMX_BUFFER_INFO lbuffer_info; mm_jpeg_encode_params_t *p_params = &p_session->params; memset(&lbuffer_info, 0x0, sizeof(QOMX_BUFFER_INFO)); for (i = 0; i < p_params->num_src_bufs; i++) { LOGD("Source buffer %d", i); lbuffer_info.fd = (OMX_U32)p_params->src_main_buf[i].fd; ret = OMX_UseBuffer(p_session->omx_handle, &(p_session->p_in_omx_buf[i]), 0, &lbuffer_info, p_params->src_main_buf[i].buf_size, p_params->src_main_buf[i].buf_vaddr); if (ret) { LOGE("Error %d", ret); return ret; } } for (i = 0; i < p_params->num_tmb_bufs; i++) { LOGD("Source buffer %d", i); lbuffer_info.fd = (OMX_U32)p_params->src_thumb_buf[i].fd; ret = OMX_UseBuffer(p_session->omx_handle, &(p_session->p_in_omx_thumb_buf[i]), 2, &lbuffer_info, p_params->src_thumb_buf[i].buf_size, p_params->src_thumb_buf[i].buf_vaddr); if (ret) { LOGE("Error %d", ret); return ret; } } for (i = 0; i < p_params->num_dst_bufs; i++) { LOGD("Dest buffer %d", i); ret = OMX_UseBuffer(p_session->omx_handle, &(p_session->p_out_omx_buf[i]), 1, NULL, p_params->dest_buf[i].buf_size, p_params->dest_buf[i].buf_vaddr); if (ret) { LOGE("Error"); return ret; } } return ret; } /** mm_jpeg_session_free_buffers: * * Arguments: * @data: job session * * Return: * OMX error values * * Description: * Free the buffers from OMX layer * **/ OMX_ERRORTYPE mm_jpeg_session_free_buffers(void *data) { OMX_ERRORTYPE ret = OMX_ErrorNone; uint32_t i = 0; mm_jpeg_job_session_t* p_session = (mm_jpeg_job_session_t *)data; mm_jpeg_encode_params_t *p_params = &p_session->params; for (i = 0; i < p_params->num_src_bufs; i++) { LOGD("Source buffer %d", i); ret = OMX_FreeBuffer(p_session->omx_handle, 0, p_session->p_in_omx_buf[i]); if (ret) { LOGE("Error %d", ret); return ret; } } for (i = 0; i < p_params->num_tmb_bufs; i++) { LOGD("Source buffer %d", i); ret = OMX_FreeBuffer(p_session->omx_handle, 2, p_session->p_in_omx_thumb_buf[i]); if (ret) { LOGE("Error %d", ret); return ret; } } for (i = 0; i < p_params->num_dst_bufs; i++) { LOGD("Dest buffer %d", i); ret = OMX_FreeBuffer(p_session->omx_handle, 1, p_session->p_out_omx_buf[i]); if (ret) { LOGE("Error"); return ret; } } return ret; } /** mm_jpeg_session_change_state: * * Arguments: * @p_session: job session * @new_state: new state to be transitioned to * @p_exec: transition function * * Return: * OMX error values * * Description: * This method is used for state transition * **/ OMX_ERRORTYPE mm_jpeg_session_change_state(mm_jpeg_job_session_t* p_session, OMX_STATETYPE new_state, mm_jpeg_transition_func_t p_exec) { OMX_ERRORTYPE ret = OMX_ErrorNone; OMX_STATETYPE current_state; LOGD("new_state %d p_exec %p", new_state, p_exec); pthread_mutex_lock(&p_session->lock); ret = OMX_GetState(p_session->omx_handle, ¤t_state); if (ret) { pthread_mutex_unlock(&p_session->lock); return ret; } if (current_state == new_state) { pthread_mutex_unlock(&p_session->lock); return OMX_ErrorNone; } p_session->state_change_pending = OMX_TRUE; pthread_mutex_unlock(&p_session->lock); ret = OMX_SendCommand(p_session->omx_handle, OMX_CommandStateSet, new_state, NULL); pthread_mutex_lock(&p_session->lock); if (ret) { LOGE("Error %d", ret); pthread_mutex_unlock(&p_session->lock); return OMX_ErrorIncorrectStateTransition; } if ((OMX_ErrorNone != p_session->error_flag) && (OMX_ErrorOverflow != p_session->error_flag)) { LOGE("Error %d", p_session->error_flag); pthread_mutex_unlock(&p_session->lock); return p_session->error_flag; } if (p_exec) { ret = p_exec(p_session); if (ret) { LOGE("Error %d", ret); pthread_mutex_unlock(&p_session->lock); return ret; } } if (p_session->state_change_pending) { LOGL("before wait"); pthread_cond_wait(&p_session->cond, &p_session->lock); LOGL("after wait"); } pthread_mutex_unlock(&p_session->lock); return ret; } /** mm_jpeg_session_create: * * Arguments: * @p_session: job session * * Return: * OMX error types * * Description: * Create a jpeg encode session * **/ OMX_ERRORTYPE mm_jpeg_session_create(mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = OMX_ErrorNone; mm_jpeg_obj *my_obj = (mm_jpeg_obj *) p_session->jpeg_obj; pthread_mutex_init(&p_session->lock, NULL); pthread_cond_init(&p_session->cond, NULL); cirq_reset(&p_session->cb_q); p_session->state_change_pending = OMX_FALSE; p_session->abort_state = MM_JPEG_ABORT_NONE; p_session->error_flag = OMX_ErrorNone; p_session->ebd_count = 0; p_session->fbd_count = 0; p_session->encode_pid = -1; p_session->config = OMX_FALSE; p_session->exif_count_local = 0; p_session->auto_out_buf = OMX_FALSE; p_session->omx_callbacks.EmptyBufferDone = mm_jpeg_ebd; p_session->omx_callbacks.FillBufferDone = mm_jpeg_fbd; p_session->omx_callbacks.EventHandler = mm_jpeg_event_handler; p_session->thumb_from_main = 0; #ifdef MM_JPEG_USE_PIPELINE p_session->thumb_from_main = !p_session->params.thumb_from_postview; #endif rc = OMX_GetHandle(&p_session->omx_handle, mm_jpeg_get_comp_name(), (void *)p_session, &p_session->omx_callbacks); if (OMX_ErrorNone != rc) { LOGE("OMX_GetHandle failed (%d)", rc); return rc; } my_obj->num_sessions++; return rc; } /** mm_jpeg_session_destroy: * * Arguments: * @p_session: job session * * Return: * none * * Description: * Destroy a jpeg encode session * **/ void mm_jpeg_session_destroy(mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = OMX_ErrorNone; OMX_STATETYPE state; mm_jpeg_obj *my_obj = (mm_jpeg_obj *) p_session->jpeg_obj; LOGD("E"); if (NULL == p_session->omx_handle) { LOGE("invalid handle"); return; } rc = OMX_GetState(p_session->omx_handle, &state); //Check state before state transition if ((state == OMX_StateExecuting) || (state == OMX_StatePause)) { rc = mm_jpeg_session_change_state(p_session, OMX_StateIdle, NULL); if (rc) { LOGE("Error"); } } rc = OMX_GetState(p_session->omx_handle, &state); if (state == OMX_StateIdle) { rc = mm_jpeg_session_change_state(p_session, OMX_StateLoaded, mm_jpeg_session_free_buffers); if (rc) { LOGE("Error"); } } /* If current session is the session in progress set session in progress pointer to null*/ p_session->config = OMX_FALSE; if (my_obj->p_session_inprogress == p_session) { my_obj->p_session_inprogress = NULL; } rc = OMX_FreeHandle(p_session->omx_handle); if (0 != rc) { LOGE("OMX_FreeHandle failed (%d)", rc); } p_session->omx_handle = NULL; pthread_mutex_destroy(&p_session->lock); pthread_cond_destroy(&p_session->cond); if (NULL != p_session->meta_enc_key) { free(p_session->meta_enc_key); p_session->meta_enc_key = NULL; } my_obj->num_sessions--; // Destroy next session if (p_session->next_session) { mm_jpeg_session_destroy(p_session->next_session); } LOGD("Session destroy successful. X"); } /** mm_jpeg_session_config_main_buffer_offset: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure the buffer offsets * **/ OMX_ERRORTYPE mm_jpeg_session_config_main_buffer_offset( mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = 0; OMX_INDEXTYPE buffer_index; QOMX_YUV_FRAME_INFO frame_info; size_t totalSize = 0; mm_jpeg_encode_params_t *p_params = &p_session->params; mm_jpeg_buf_t *p_src_buf = &p_params->src_main_buf[0]; memset(&frame_info, 0x0, sizeof(QOMX_YUV_FRAME_INFO)); frame_info.cbcrStartOffset[0] = p_src_buf->offset.mp[0].len; frame_info.cbcrStartOffset[1] = p_src_buf->offset.mp[1].len; frame_info.yOffset = p_src_buf->offset.mp[0].offset; frame_info.cbcrOffset[0] = p_src_buf->offset.mp[1].offset; frame_info.cbcrOffset[1] = p_src_buf->offset.mp[2].offset; totalSize = p_src_buf->buf_size; rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_BUFFER_OFFSET_NAME, &buffer_index); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } LOGD("yOffset = %d, cbcrOffset = (%d %d), totalSize = %zd," "cbcrStartOffset = (%d %d)", (int)frame_info.yOffset, (int)frame_info.cbcrOffset[0], (int)frame_info.cbcrOffset[1], totalSize, (int)frame_info.cbcrStartOffset[0], (int)frame_info.cbcrStartOffset[1]); rc = OMX_SetParameter(p_session->omx_handle, buffer_index, &frame_info); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } return rc; } /** mm_jpeg_encoding_mode: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure the serial or parallel encoding * mode * **/ OMX_ERRORTYPE mm_jpeg_encoding_mode( mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = 0; OMX_INDEXTYPE indextype; QOMX_ENCODING_MODE encoding_mode; rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_ENCODING_MODE_NAME, &indextype); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } if (ENCODING_MODE_PARALLEL) { encoding_mode = OMX_Parallel_Encoding; } else { encoding_mode = OMX_Serial_Encoding; } LOGD("encoding mode = %d ", (int)encoding_mode); rc = OMX_SetParameter(p_session->omx_handle, indextype, &encoding_mode); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } return rc; } /** mm_jpeg_get_speed: * * Arguments: * @p_session: job session * * Return: * ops speed type for jpeg * * Description: * Configure normal or high speed jpeg * **/ QOMX_JPEG_SPEED_MODE mm_jpeg_get_speed( mm_jpeg_job_session_t* p_session) { mm_jpeg_encode_params_t *p_params = &p_session->params; cam_dimension_t *p_dim = &p_params->main_dim.src_dim; if (p_params->burst_mode || (MM_JPEG_MIN_NOM_RESOLUTION < (p_dim->width * p_dim->height))) { return QOMX_JPEG_SPEED_MODE_HIGH; } return QOMX_JPEG_SPEED_MODE_NORMAL; } /** mm_jpeg_speed_mode: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure normal or high speed jpeg * **/ OMX_ERRORTYPE mm_jpeg_speed_mode( mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = 0; OMX_INDEXTYPE indextype; QOMX_JPEG_SPEED jpeg_speed; rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_JPEG_SPEED_NAME, &indextype); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } jpeg_speed.speedMode = mm_jpeg_get_speed(p_session); LOGH("speed %d", jpeg_speed.speedMode); rc = OMX_SetParameter(p_session->omx_handle, indextype, &jpeg_speed); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } return rc; } /** mm_jpeg_get_mem: * * Arguments: * @p_out_buf : jpeg output buffer * @p_jpeg_session: job session * * Return: * 0 for success else failure * * Description: * gets the jpeg output buffer * **/ static int32_t mm_jpeg_get_mem( omx_jpeg_ouput_buf_t *p_out_buf, void* p_jpeg_session) { int32_t rc = 0; mm_jpeg_job_session_t *p_session = (mm_jpeg_job_session_t *)p_jpeg_session; mm_jpeg_encode_params_t *p_params = NULL; mm_jpeg_encode_job_t *p_encode_job = NULL; if (!p_session) { LOGE("Invalid input"); return -1; } p_params = &p_session->params; p_encode_job = &p_session->encode_job; if (!p_params || !p_encode_job || !p_params->get_memory) { LOGE("Invalid jpeg encode params"); return -1; } p_params->get_memory(p_out_buf); p_encode_job->ref_count++; p_encode_job->alloc_out_buffer = p_out_buf; LOGD("ref_count %d p_out_buf %p", p_encode_job->ref_count, p_out_buf); return rc; } /** mm_jpeg_put_mem: * * Arguments: * @p_jpeg_session: job session * * Return: * 0 for success else failure * * Description: * releases the jpeg output buffer * **/ static int32_t mm_jpeg_put_mem(void* p_jpeg_session) { int32_t rc = 0; mm_jpeg_job_session_t *p_session = (mm_jpeg_job_session_t *)p_jpeg_session; mm_jpeg_encode_params_t *p_params = NULL; mm_jpeg_encode_job_t *p_encode_job = NULL; if (!p_session) { LOGE("Invalid input"); return -1; } p_params = &p_session->params; p_encode_job = &p_session->encode_job; if (!p_params->get_memory) { LOGD("get_mem not defined, ignore put mem"); return 0; } if (!p_params || !p_encode_job || !p_params->put_memory) { LOGE("Invalid jpeg encode params"); return -1; } if ((MM_JPEG_ABORT_NONE != p_session->abort_state) && p_encode_job->ref_count) { omx_jpeg_ouput_buf_t *p_out_buf = ( omx_jpeg_ouput_buf_t *) p_encode_job->alloc_out_buffer; p_params->put_memory(p_out_buf); p_encode_job->ref_count--; p_encode_job->alloc_out_buffer = NULL; } else if (p_encode_job->ref_count) { p_encode_job->ref_count--; } else { LOGW("Buffer already released %d", p_encode_job->ref_count); rc = -1; } LOGD("ref_count %d p_out_buf %p", p_encode_job->ref_count, p_encode_job->alloc_out_buffer); return rc; } /** mm_jpeg_mem_ops: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure the serial or parallel encoding * mode * **/ OMX_ERRORTYPE mm_jpeg_mem_ops( mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = 0; OMX_INDEXTYPE indextype; QOMX_MEM_OPS mem_ops; mm_jpeg_encode_params_t *p_params = &p_session->params; if (p_params->get_memory) { mem_ops.get_memory = mm_jpeg_get_mem; } else { mem_ops.get_memory = NULL; LOGH("HAL get_mem handler undefined"); } mem_ops.psession = p_session; rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_MEM_OPS_NAME, &indextype); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } rc = OMX_SetParameter(p_session->omx_handle, indextype, &mem_ops); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } return rc; } /** mm_jpeg_metadata: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Pass meta data * **/ OMX_ERRORTYPE mm_jpeg_metadata( mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = OMX_ErrorNone; OMX_INDEXTYPE indexType; QOMX_METADATA lMeta; mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; mm_jpeg_obj *my_obj = (mm_jpeg_obj *) p_session->jpeg_obj; rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_METADATA_NAME, &indexType); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } lMeta.metadata = (OMX_U8 *)p_jobparams->p_metadata; lMeta.metaPayloadSize = sizeof(*p_jobparams->p_metadata); lMeta.mobicat_mask = p_jobparams->mobicat_mask; lMeta.static_metadata = (OMX_U8 *)my_obj->jpeg_metadata; rc = OMX_SetConfig(p_session->omx_handle, indexType, &lMeta); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } return OMX_ErrorNone; } /** mm_jpeg_meta_enc_key: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Pass metadata encrypt key * **/ OMX_ERRORTYPE mm_jpeg_meta_enc_key( mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE rc = OMX_ErrorNone; OMX_INDEXTYPE indexType; QOMX_META_ENC_KEY lKey; lKey.metaKey = p_session->meta_enc_key; lKey.keyLen = p_session->meta_enc_keylen; if ((!lKey.metaKey) || (!lKey.keyLen)){ LOGD("Key is invalid"); return OMX_ErrorNone; } rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_META_ENC_KEY_NAME, &indexType); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } rc = OMX_SetConfig(p_session->omx_handle, indexType, &lKey); if (rc != OMX_ErrorNone) { LOGE("Failed"); return rc; } return OMX_ErrorNone; } /** map_jpeg_format: * * Arguments: * @color_fmt: color format * * Return: * OMX color format * * Description: * Map mmjpeg color format to OMX color format * **/ int map_jpeg_format(mm_jpeg_color_format color_fmt) { switch (color_fmt) { case MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V2: return (int)OMX_QCOM_IMG_COLOR_FormatYVU420SemiPlanar; case MM_JPEG_COLOR_FORMAT_YCBCRLP_H2V2: return (int)OMX_COLOR_FormatYUV420SemiPlanar; case MM_JPEG_COLOR_FORMAT_YCRCBLP_H2V1: return (int)OMX_QCOM_IMG_COLOR_FormatYVU422SemiPlanar; case MM_JPEG_COLOR_FORMAT_YCBCRLP_H2V1: return (int)OMX_COLOR_FormatYUV422SemiPlanar; case MM_JPEG_COLOR_FORMAT_YCRCBLP_H1V2: return (int)OMX_QCOM_IMG_COLOR_FormatYVU422SemiPlanar_h1v2; case MM_JPEG_COLOR_FORMAT_YCBCRLP_H1V2: return (int)OMX_QCOM_IMG_COLOR_FormatYUV422SemiPlanar_h1v2; case MM_JPEG_COLOR_FORMAT_YCRCBLP_H1V1: return (int)OMX_QCOM_IMG_COLOR_FormatYVU444SemiPlanar; case MM_JPEG_COLOR_FORMAT_YCBCRLP_H1V1: return (int)OMX_QCOM_IMG_COLOR_FormatYUV444SemiPlanar; case MM_JPEG_COLOR_FORMAT_MONOCHROME: return (int)OMX_COLOR_FormatMonochrome; default: LOGW("invalid format %d", color_fmt); return (int)OMX_QCOM_IMG_COLOR_FormatYVU420SemiPlanar; } } /** mm_jpeg_session_config_port: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure OMX ports * **/ OMX_ERRORTYPE mm_jpeg_session_config_ports(mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE ret = OMX_ErrorNone; mm_jpeg_encode_params_t *p_params = &p_session->params; OMX_CONFIG_ROTATIONTYPE rotate; mm_jpeg_buf_t *p_src_buf = &p_params->src_main_buf[0]; p_session->inputPort.nPortIndex = 0; p_session->outputPort.nPortIndex = 1; p_session->inputTmbPort.nPortIndex = 2; ret = OMX_GetParameter(p_session->omx_handle, OMX_IndexParamPortDefinition, &p_session->inputPort); if (ret) { LOGE("failed"); return ret; } ret = OMX_GetParameter(p_session->omx_handle, OMX_IndexParamPortDefinition, &p_session->inputTmbPort); if (ret) { LOGE("failed"); return ret; } ret = OMX_GetParameter(p_session->omx_handle, OMX_IndexParamPortDefinition, &p_session->outputPort); if (ret) { LOGE("failed"); return ret; } p_session->inputPort.format.image.nFrameWidth = (OMX_U32)p_params->main_dim.src_dim.width; p_session->inputPort.format.image.nFrameHeight = (OMX_U32)p_params->main_dim.src_dim.height; p_session->inputPort.format.image.nStride = p_src_buf->offset.mp[0].stride; p_session->inputPort.format.image.nSliceHeight = (OMX_U32)p_src_buf->offset.mp[0].scanline; p_session->inputPort.format.image.eColorFormat = map_jpeg_format(p_params->color_format); p_session->inputPort.nBufferSize = p_params->src_main_buf[0/*p_jobparams->src_index*/].buf_size; p_session->inputPort.nBufferCountActual = (OMX_U32)p_params->num_src_bufs; ret = OMX_SetParameter(p_session->omx_handle, OMX_IndexParamPortDefinition, &p_session->inputPort); if (ret) { LOGE("failed"); return ret; } if (p_session->params.encode_thumbnail) { mm_jpeg_buf_t *p_tmb_buf = &p_params->src_thumb_buf[0]; p_session->inputTmbPort.format.image.nFrameWidth = (OMX_U32)p_params->thumb_dim.src_dim.width; p_session->inputTmbPort.format.image.nFrameHeight = (OMX_U32)p_params->thumb_dim.src_dim.height; p_session->inputTmbPort.format.image.nStride = p_tmb_buf->offset.mp[0].stride; p_session->inputTmbPort.format.image.nSliceHeight = (OMX_U32)p_tmb_buf->offset.mp[0].scanline; p_session->inputTmbPort.format.image.eColorFormat = map_jpeg_format(p_params->thumb_color_format); p_session->inputTmbPort.nBufferSize = p_params->src_thumb_buf[0].buf_size; p_session->inputTmbPort.nBufferCountActual = (OMX_U32)p_params->num_tmb_bufs; ret = OMX_SetParameter(p_session->omx_handle, OMX_IndexParamPortDefinition, &p_session->inputTmbPort); if (ret) { LOGE("failed"); return ret; } // Enable thumbnail port ret = OMX_SendCommand(p_session->omx_handle, OMX_CommandPortEnable, p_session->inputTmbPort.nPortIndex, NULL); if (ret) { LOGE("failed"); return ret; } } else { // Disable thumbnail port ret = OMX_SendCommand(p_session->omx_handle, OMX_CommandPortDisable, p_session->inputTmbPort.nPortIndex, NULL); if (ret) { LOGE("failed"); return ret; } } p_session->outputPort.nBufferSize = p_params->dest_buf[0].buf_size; p_session->outputPort.nBufferCountActual = (OMX_U32)p_params->num_dst_bufs; ret = OMX_SetParameter(p_session->omx_handle, OMX_IndexParamPortDefinition, &p_session->outputPort); if (ret) { LOGE("failed"); return ret; } /* set rotation */ memset(&rotate, 0, sizeof(rotate)); rotate.nPortIndex = 1; rotate.nRotation = (OMX_S32)p_params->rotation; ret = OMX_SetConfig(p_session->omx_handle, OMX_IndexConfigCommonRotate, &rotate); if (OMX_ErrorNone != ret) { LOGE("Error %d", ret); return ret; } LOGD("Set rotation to %d at port_idx = %d", (int)p_params->rotation, (int)rotate.nPortIndex); return ret; } /** mm_jpeg_update_thumbnail_crop * * Arguments: * @p_thumb_dim: thumbnail dimension * @crop_width : flag indicating if width needs to be cropped * * Return: * OMX error values * * Description: * Updates thumbnail crop aspect ratio based on * thumbnail destination aspect ratio. * */ OMX_ERRORTYPE mm_jpeg_update_thumbnail_crop(mm_jpeg_dim_t *p_thumb_dim, uint8_t crop_width) { OMX_ERRORTYPE ret = OMX_ErrorNone; int32_t cropped_width = 0, cropped_height = 0; if (crop_width) { // Keep height constant cropped_height = p_thumb_dim->crop.height; cropped_width = floor((cropped_height * p_thumb_dim->dst_dim.width) / p_thumb_dim->dst_dim.height); if (cropped_width % 2) { cropped_width -= 1; } } else { // Keep width constant cropped_width = p_thumb_dim->crop.width; cropped_height = floor((cropped_width * p_thumb_dim->dst_dim.height) / p_thumb_dim->dst_dim.width); if (cropped_height % 2) { cropped_height -= 1; } } p_thumb_dim->crop.left = p_thumb_dim->crop.left + floor((p_thumb_dim->crop.width - cropped_width) / 2); if (p_thumb_dim->crop.left % 2) { p_thumb_dim->crop.left -= 1; } p_thumb_dim->crop.top = p_thumb_dim->crop.top + floor((p_thumb_dim->crop.height - cropped_height) / 2); if (p_thumb_dim->crop.top % 2) { p_thumb_dim->crop.top -= 1; } p_thumb_dim->crop.width = cropped_width; p_thumb_dim->crop.height = cropped_height; LOGH("New thumbnail crop: left %d, top %d, crop width %d," " crop height %d", p_thumb_dim->crop.left, p_thumb_dim->crop.top, p_thumb_dim->crop.width, p_thumb_dim->crop.height); return ret; } /** mm_jpeg_omx_config_thumbnail: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure OMX ports * **/ OMX_ERRORTYPE mm_jpeg_session_config_thumbnail(mm_jpeg_job_session_t* p_session) { OMX_ERRORTYPE ret = OMX_ErrorNone; QOMX_THUMBNAIL_INFO thumbnail_info; OMX_INDEXTYPE thumb_indextype; mm_jpeg_encode_params_t *p_params = &p_session->params; mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; mm_jpeg_dim_t *p_thumb_dim = &p_jobparams->thumb_dim; mm_jpeg_dim_t *p_main_dim = &p_jobparams->main_dim; QOMX_YUV_FRAME_INFO *p_frame_info = &thumbnail_info.tmbOffset; mm_jpeg_buf_t *p_tmb_buf = &p_params->src_thumb_buf[p_jobparams->thumb_index]; LOGH("encode_thumbnail %u", p_params->encode_thumbnail); if (OMX_FALSE == p_params->encode_thumbnail) { return ret; } if ((p_thumb_dim->dst_dim.width == 0) || (p_thumb_dim->dst_dim.height == 0)) { LOGE("Error invalid output dim for thumbnail"); return OMX_ErrorBadParameter; } if ((p_thumb_dim->src_dim.width == 0) || (p_thumb_dim->src_dim.height == 0)) { LOGE("Error invalid input dim for thumbnail"); return OMX_ErrorBadParameter; } if ((p_thumb_dim->crop.width == 0) || (p_thumb_dim->crop.height == 0)) { p_thumb_dim->crop.width = p_thumb_dim->src_dim.width; p_thumb_dim->crop.height = p_thumb_dim->src_dim.height; } /* check crop boundary */ if ((p_thumb_dim->crop.width + p_thumb_dim->crop.left > p_thumb_dim->src_dim.width) || (p_thumb_dim->crop.height + p_thumb_dim->crop.top > p_thumb_dim->src_dim.height)) { LOGE("invalid crop boundary (%d, %d) offset (%d, %d) out of (%d, %d)", p_thumb_dim->crop.width, p_thumb_dim->crop.height, p_thumb_dim->crop.left, p_thumb_dim->crop.top, p_thumb_dim->src_dim.width, p_thumb_dim->src_dim.height); return OMX_ErrorBadParameter; } memset(&thumbnail_info, 0x0, sizeof(QOMX_THUMBNAIL_INFO)); ret = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_THUMBNAIL_NAME, &thumb_indextype); if (ret) { LOGE("Error %d", ret); return ret; } /* fill thumbnail info */ thumbnail_info.scaling_enabled = 1; thumbnail_info.input_width = (OMX_U32)p_thumb_dim->src_dim.width; thumbnail_info.input_height = (OMX_U32)p_thumb_dim->src_dim.height; thumbnail_info.rotation = (OMX_U32)p_params->thumb_rotation; thumbnail_info.quality = (OMX_U32)p_params->thumb_quality; thumbnail_info.output_width = (OMX_U32)p_thumb_dim->dst_dim.width; thumbnail_info.output_height = (OMX_U32)p_thumb_dim->dst_dim.height; if (p_session->thumb_from_main) { if ((p_session->params.thumb_rotation == 90 || p_session->params.thumb_rotation == 270) && (p_session->params.rotation == 0 || p_session->params.rotation == 180)) { thumbnail_info.output_width = (OMX_U32)p_thumb_dim->dst_dim.height; thumbnail_info.output_height = (OMX_U32)p_thumb_dim->dst_dim.width; thumbnail_info.rotation = p_session->params.rotation; } //Thumb FOV should be within main image FOV if (p_thumb_dim->crop.left < p_main_dim->crop.left) { p_thumb_dim->crop.left = p_main_dim->crop.left; } if (p_thumb_dim->crop.top < p_main_dim->crop.top) { p_thumb_dim->crop.top = p_main_dim->crop.top; } while ((p_thumb_dim->crop.left + p_thumb_dim->crop.width) > (p_main_dim->crop.left + p_main_dim->crop.width)) { if (p_thumb_dim->crop.left == p_main_dim->crop.left) { p_thumb_dim->crop.width = p_main_dim->crop.width; } else { p_thumb_dim->crop.left = p_main_dim->crop.left; } } while ((p_thumb_dim->crop.top + p_thumb_dim->crop.height) > (p_main_dim->crop.top + p_main_dim->crop.height)) { if (p_thumb_dim->crop.top == p_main_dim->crop.top) { p_thumb_dim->crop.height = p_main_dim->crop.height; } else { p_thumb_dim->crop.top = p_main_dim->crop.top; } } } else if ((p_thumb_dim->dst_dim.width > p_thumb_dim->src_dim.width) || (p_thumb_dim->dst_dim.height > p_thumb_dim->src_dim.height)) { LOGE("Incorrect thumbnail dim %dx%d resetting to %dx%d", p_thumb_dim->dst_dim.width, p_thumb_dim->dst_dim.height, p_thumb_dim->src_dim.width, p_thumb_dim->src_dim.height); thumbnail_info.output_width = (OMX_U32)p_thumb_dim->src_dim.width; thumbnail_info.output_height = (OMX_U32)p_thumb_dim->src_dim.height; } // If the thumbnail crop aspect ratio image and thumbnail dest aspect // ratio are different, reset the thumbnail crop double thumbcrop_aspect_ratio = (double)p_thumb_dim->crop.width / (double)p_thumb_dim->crop.height; double thumbdst_aspect_ratio = (double)p_thumb_dim->dst_dim.width / (double)p_thumb_dim->dst_dim.height; if ((thumbdst_aspect_ratio - thumbcrop_aspect_ratio) > ASPECT_TOLERANCE) { mm_jpeg_update_thumbnail_crop(p_thumb_dim, 0); } else if ((thumbcrop_aspect_ratio - thumbdst_aspect_ratio) > ASPECT_TOLERANCE) { mm_jpeg_update_thumbnail_crop(p_thumb_dim, 1); } // Fill thumbnail crop info thumbnail_info.crop_info.nWidth = (OMX_U32)p_thumb_dim->crop.width; thumbnail_info.crop_info.nHeight = (OMX_U32)p_thumb_dim->crop.height; thumbnail_info.crop_info.nLeft = p_thumb_dim->crop.left; thumbnail_info.crop_info.nTop = p_thumb_dim->crop.top; memset(p_frame_info, 0x0, sizeof(*p_frame_info)); p_frame_info->cbcrStartOffset[0] = p_tmb_buf->offset.mp[0].len; p_frame_info->cbcrStartOffset[1] = p_tmb_buf->offset.mp[1].len; p_frame_info->yOffset = p_tmb_buf->offset.mp[0].offset; p_frame_info->cbcrOffset[0] = p_tmb_buf->offset.mp[1].offset; p_frame_info->cbcrOffset[1] = p_tmb_buf->offset.mp[2].offset; ret = OMX_SetConfig(p_session->omx_handle, thumb_indextype, &thumbnail_info); if (ret) { LOGE("Error"); return ret; } return ret; } /** mm_jpeg_session_config_main_crop: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure main image crop * **/ OMX_ERRORTYPE mm_jpeg_session_config_main_crop(mm_jpeg_job_session_t *p_session) { OMX_CONFIG_RECTTYPE rect_type_in, rect_type_out; OMX_ERRORTYPE ret = OMX_ErrorNone; mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; mm_jpeg_dim_t *dim = &p_jobparams->main_dim; if ((dim->crop.width == 0) || (dim->crop.height == 0)) { dim->crop.width = dim->src_dim.width; dim->crop.height = dim->src_dim.height; } /* error check first */ if ((dim->crop.width + dim->crop.left > dim->src_dim.width) || (dim->crop.height + dim->crop.top > dim->src_dim.height)) { LOGE("invalid crop boundary (%d, %d) out of (%d, %d)", dim->crop.width + dim->crop.left, dim->crop.height + dim->crop.top, dim->src_dim.width, dim->src_dim.height); return OMX_ErrorBadParameter; } memset(&rect_type_in, 0, sizeof(rect_type_in)); memset(&rect_type_out, 0, sizeof(rect_type_out)); rect_type_in.nPortIndex = 0; rect_type_out.nPortIndex = 0; if ((dim->src_dim.width != dim->crop.width) || (dim->src_dim.height != dim->crop.height) || (dim->src_dim.width != dim->dst_dim.width) || (dim->src_dim.height != dim->dst_dim.height)) { /* Scaler information */ rect_type_in.nWidth = CEILING2(dim->crop.width); rect_type_in.nHeight = CEILING2(dim->crop.height); rect_type_in.nLeft = dim->crop.left; rect_type_in.nTop = dim->crop.top; if (dim->dst_dim.width && dim->dst_dim.height) { rect_type_out.nWidth = (OMX_U32)dim->dst_dim.width; rect_type_out.nHeight = (OMX_U32)dim->dst_dim.height; } } ret = OMX_SetConfig(p_session->omx_handle, OMX_IndexConfigCommonInputCrop, &rect_type_in); if (OMX_ErrorNone != ret) { LOGE("Error"); return ret; } LOGH("OMX_IndexConfigCommonInputCrop w = %d, h = %d, l = %d, t = %d," " port_idx = %d", (int)rect_type_in.nWidth, (int)rect_type_in.nHeight, (int)rect_type_in.nLeft, (int)rect_type_in.nTop, (int)rect_type_in.nPortIndex); ret = OMX_SetConfig(p_session->omx_handle, OMX_IndexConfigCommonOutputCrop, &rect_type_out); if (OMX_ErrorNone != ret) { LOGE("Error"); return ret; } LOGD("OMX_IndexConfigCommonOutputCrop w = %d, h = %d," " port_idx = %d", (int)rect_type_out.nWidth, (int)rect_type_out.nHeight, (int)rect_type_out.nPortIndex); return ret; } /** mm_jpeg_session_config_main: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure main image * **/ OMX_ERRORTYPE mm_jpeg_session_config_main(mm_jpeg_job_session_t *p_session) { OMX_ERRORTYPE rc = OMX_ErrorNone; /* config port */ LOGD("config port"); rc = mm_jpeg_session_config_ports(p_session); if (OMX_ErrorNone != rc) { LOGE("config port failed"); return rc; } /* config buffer offset */ LOGD("config main buf offset"); rc = mm_jpeg_session_config_main_buffer_offset(p_session); if (OMX_ErrorNone != rc) { LOGE("config buffer offset failed"); return rc; } /* set the encoding mode */ rc = mm_jpeg_encoding_mode(p_session); if (OMX_ErrorNone != rc) { LOGE("config encoding mode failed"); return rc; } /* set the metadata encrypt key */ rc = mm_jpeg_meta_enc_key(p_session); if (OMX_ErrorNone != rc) { LOGE("config session failed"); return rc; } /* set the mem ops */ rc = mm_jpeg_mem_ops(p_session); if (OMX_ErrorNone != rc) { LOGE("config mem ops failed"); return rc; } /* set the jpeg speed mode */ rc = mm_jpeg_speed_mode(p_session); if (OMX_ErrorNone != rc) { LOGE("config speed mode failed"); return rc; } return rc; } /** mm_jpeg_session_config_common: * * Arguments: * @p_session: job session * * Return: * OMX error values * * Description: * Configure common parameters * **/ OMX_ERRORTYPE mm_jpeg_session_config_common(mm_jpeg_job_session_t *p_session) { OMX_ERRORTYPE rc = OMX_ErrorNone; OMX_INDEXTYPE exif_idx; OMX_CONFIG_ROTATIONTYPE rotate; mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; QOMX_EXIF_INFO exif_info; /* set rotation */ memset(&rotate, 0, sizeof(rotate)); rotate.nPortIndex = 1; rotate.nRotation = (OMX_S32)p_jobparams->rotation; rc = OMX_SetConfig(p_session->omx_handle, OMX_IndexConfigCommonRotate, &rotate); if (OMX_ErrorNone != rc) { LOGE("Error %d", rc); return rc; } LOGD("Set rotation to %d at port_idx = %d", (int)p_jobparams->rotation, (int)rotate.nPortIndex); /* Set Exif data*/ memset(&p_session->exif_info_local[0], 0, sizeof(p_session->exif_info_local)); rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_EXIF_NAME, &exif_idx); if (OMX_ErrorNone != rc) { LOGE("Error %d", rc); return rc; } LOGD("Num of exif entries passed from HAL: %d", (int)p_jobparams->exif_info.numOfEntries); if (p_jobparams->exif_info.numOfEntries > 0) { rc = OMX_SetConfig(p_session->omx_handle, exif_idx, &p_jobparams->exif_info); if (OMX_ErrorNone != rc) { LOGE("Error %d", rc); return rc; } } /*parse aditional exif data from the metadata*/ exif_info.numOfEntries = 0; exif_info.exif_data = &p_session->exif_info_local[0]; process_meta_data(p_jobparams->p_metadata, &exif_info, &p_jobparams->cam_exif_params, p_jobparams->hal_version); /* After Parse metadata */ p_session->exif_count_local = (int)exif_info.numOfEntries; if (exif_info.numOfEntries > 0) { /* set exif tags */ LOGD("exif tags from metadata count %d", (int)exif_info.numOfEntries); rc = OMX_SetConfig(p_session->omx_handle, exif_idx, &exif_info); if (OMX_ErrorNone != rc) { LOGE("Error %d", rc); return rc; } } return rc; } /** mm_jpeg_session_abort: * * Arguments: * @p_session: jpeg session * * Return: * OMX_BOOL * * Description: * Abort ongoing job * **/ OMX_BOOL mm_jpeg_session_abort(mm_jpeg_job_session_t *p_session) { OMX_ERRORTYPE ret = OMX_ErrorNone; int rc = 0; LOGD("E"); pthread_mutex_lock(&p_session->lock); if (MM_JPEG_ABORT_NONE != p_session->abort_state) { pthread_mutex_unlock(&p_session->lock); LOGH("**** ALREADY ABORTED"); return 0; } p_session->abort_state = MM_JPEG_ABORT_INIT; if (OMX_TRUE == p_session->encoding) { p_session->state_change_pending = OMX_TRUE; LOGH("**** ABORTING"); pthread_mutex_unlock(&p_session->lock); ret = OMX_SendCommand(p_session->omx_handle, OMX_CommandStateSet, OMX_StateIdle, NULL); if (ret != OMX_ErrorNone) { LOGE("OMX_SendCommand returned error %d", ret); return 1; } rc = mm_jpegenc_destroy_job(p_session); if (rc != 0) { LOGE("Destroy job returned error %d", rc); } pthread_mutex_lock(&p_session->lock); if (MM_JPEG_ABORT_INIT == p_session->abort_state) { LOGL("before wait"); pthread_cond_wait(&p_session->cond, &p_session->lock); } LOGL("after wait"); } p_session->abort_state = MM_JPEG_ABORT_DONE; mm_jpeg_put_mem((void *)p_session); pthread_mutex_unlock(&p_session->lock); // Abort next session if (p_session->next_session) { mm_jpeg_session_abort(p_session->next_session); } LOGD("X"); return 0; } /** mm_jpeg_config_multi_image_info * * Arguments: * @p_session: encode session * * Return: OMX_ERRORTYPE * * Description: * Configure multi image parameters * **/ static OMX_ERRORTYPE mm_jpeg_config_multi_image_info( mm_jpeg_job_session_t *p_session) { OMX_ERRORTYPE ret = OMX_ErrorNone; QOMX_JPEG_MULTI_IMAGE_INFO multi_image_info; OMX_INDEXTYPE multi_image_index; mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; if (p_jobparams->multi_image_info.type == MM_JPEG_TYPE_MPO) { ret = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_MULTI_IMAGE_NAME, &multi_image_index); if (ret) { LOGE("Error getting multi image info extention index %d", ret); return ret; } memset(&multi_image_info, 0, sizeof(multi_image_info)); if (p_jobparams->multi_image_info.type == MM_JPEG_TYPE_MPO) { multi_image_info.image_type = QOMX_JPEG_IMAGE_TYPE_MPO; } else { multi_image_info.image_type = QOMX_JPEG_IMAGE_TYPE_JPEG; } multi_image_info.is_primary_image = p_jobparams->multi_image_info.is_primary; multi_image_info.num_of_images = p_jobparams->multi_image_info.num_of_images; ret = OMX_SetConfig(p_session->omx_handle, multi_image_index, &multi_image_info); if (ret) { LOGE("Error setting multi image config"); return ret; } } return ret; } /** mm_jpeg_configure_params * * Arguments: * @p_session: encode session * * Return: * none * * Description: * Configure the job specific params * **/ static OMX_ERRORTYPE mm_jpeg_configure_job_params( mm_jpeg_job_session_t *p_session) { OMX_ERRORTYPE ret = OMX_ErrorNone; OMX_IMAGE_PARAM_QFACTORTYPE q_factor; QOMX_WORK_BUFFER work_buffer; OMX_INDEXTYPE work_buffer_index; mm_jpeg_encode_params_t *p_params = &p_session->params; mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; int i; /* common config */ ret = mm_jpeg_session_config_common(p_session); if (OMX_ErrorNone != ret) { LOGE("config common failed"); } /* config Main Image crop */ LOGD("config main crop"); ret = mm_jpeg_session_config_main_crop(p_session); if (OMX_ErrorNone != ret) { LOGE("config crop failed"); return ret; } /* set quality */ memset(&q_factor, 0, sizeof(q_factor)); q_factor.nPortIndex = 0; q_factor.nQFactor = p_params->quality; ret = OMX_SetConfig(p_session->omx_handle, OMX_IndexParamQFactor, &q_factor); LOGD("config QFactor: %d", (int)q_factor.nQFactor); if (OMX_ErrorNone != ret) { LOGE("Error setting Q factor %d", ret); return ret; } /* config thumbnail */ ret = mm_jpeg_session_config_thumbnail(p_session); if (OMX_ErrorNone != ret) { LOGE("config thumbnail img failed"); return ret; } //Pass the ION buffer to be used as o/p for HW memset(&work_buffer, 0x0, sizeof(QOMX_WORK_BUFFER)); ret = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_WORK_BUFFER_NAME, &work_buffer_index); if (ret) { LOGE("Error getting work buffer index %d", ret); return ret; } work_buffer.fd = p_session->work_buffer.p_pmem_fd; work_buffer.vaddr = p_session->work_buffer.addr; work_buffer.length = (uint32_t)p_session->work_buffer.size; LOGH("Work buffer info %d %p WorkBufSize: %d invalidate", work_buffer.fd, work_buffer.vaddr, work_buffer.length); buffer_invalidate(&p_session->work_buffer); ret = OMX_SetConfig(p_session->omx_handle, work_buffer_index, &work_buffer); if (ret) { LOGE("Error"); return ret; } /* set metadata */ ret = mm_jpeg_metadata(p_session); if (OMX_ErrorNone != ret) { LOGE("config makernote data failed"); return ret; } /* set QTable */ for (i = 0; i < QTABLE_MAX; i++) { if (p_jobparams->qtable_set[i]) { ret = OMX_SetConfig(p_session->omx_handle, OMX_IndexParamQuantizationTable, &p_jobparams->qtable[i]); if (OMX_ErrorNone != ret) { LOGE("set QTable Error"); return ret; } } } /* Set multi image data*/ ret = mm_jpeg_config_multi_image_info(p_session); if (OMX_ErrorNone != ret) { LOGE("config multi image data failed"); return ret; } return ret; } /** mm_jpeg_session_configure: * * Arguments: * @data: encode session * * Return: * none * * Description: * Configure the session * **/ static OMX_ERRORTYPE mm_jpeg_session_configure(mm_jpeg_job_session_t *p_session) { OMX_ERRORTYPE ret = OMX_ErrorNone; LOGD("E "); MM_JPEG_CHK_ABORT(p_session, ret, error); /* config main img */ ret = mm_jpeg_session_config_main(p_session); if (OMX_ErrorNone != ret) { LOGE("config main img failed"); goto error; } ret = mm_jpeg_session_change_state(p_session, OMX_StateIdle, mm_jpeg_session_send_buffers); if (ret) { LOGE("change state to idle failed %d", ret); goto error; } ret = mm_jpeg_session_change_state(p_session, OMX_StateExecuting, NULL); if (ret) { LOGE("change state to executing failed %d", ret); goto error; } error: LOGD("X ret %d", ret); return ret; } /** mm_jpeg_session_encode: * * Arguments: * @p_session: encode session * * Return: * OMX_ERRORTYPE * * Description: * Start the encoding * **/ static OMX_ERRORTYPE mm_jpeg_session_encode(mm_jpeg_job_session_t *p_session) { OMX_ERRORTYPE ret = OMX_ErrorNone; mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; mm_jpeg_obj *my_obj = (mm_jpeg_obj *) p_session->jpeg_obj; pthread_mutex_lock(&p_session->lock); p_session->abort_state = MM_JPEG_ABORT_NONE; p_session->encoding = OMX_FALSE; pthread_mutex_unlock(&p_session->lock); if (p_session->thumb_from_main) { if (0 > p_jobparams->src_index) { LOGE("Error"); ret = OMX_ErrorUnsupportedIndex; goto error; } p_jobparams->thumb_index = (uint32_t)p_jobparams->src_index; p_jobparams->thumb_dim.crop = p_jobparams->main_dim.crop; } if (OMX_FALSE == p_session->config) { /* If another session in progress clear that sessions configuration */ if (my_obj->p_session_inprogress != NULL) { OMX_STATETYPE state; mm_jpeg_job_session_t *p_session_inprogress = my_obj->p_session_inprogress; OMX_GetState(p_session_inprogress->omx_handle, &state); //Check state before state transition if ((state == OMX_StateExecuting) || (state == OMX_StatePause)) { ret = mm_jpeg_session_change_state(p_session_inprogress, OMX_StateIdle, NULL); if (ret) { LOGE("Error"); goto error; } } OMX_GetState(p_session_inprogress->omx_handle, &state); if (state == OMX_StateIdle) { ret = mm_jpeg_session_change_state(p_session_inprogress, OMX_StateLoaded, mm_jpeg_session_free_buffers); if (ret) { LOGE("Error"); goto error; } } p_session_inprogress->config = OMX_FALSE; my_obj->p_session_inprogress = NULL; } ret = mm_jpeg_session_configure(p_session); if (ret) { LOGE("Error"); goto error; } p_session->config = OMX_TRUE; my_obj->p_session_inprogress = p_session; } ret = mm_jpeg_configure_job_params(p_session); if (ret) { LOGE("Error"); goto error; } pthread_mutex_lock(&p_session->lock); p_session->encoding = OMX_TRUE; pthread_mutex_unlock(&p_session->lock); MM_JPEG_CHK_ABORT(p_session, ret, error); #ifdef MM_JPEG_DUMP_INPUT char filename[256]; snprintf(filename, sizeof(filename), QCAMERA_DUMP_FRM_LOCATION"jpeg/mm_jpeg_int%d.yuv", p_session->ebd_count); DUMP_TO_FILE(filename, p_session->p_in_omx_buf[p_jobparams->src_index]->pBuffer, (size_t)p_session->p_in_omx_buf[p_jobparams->src_index]->nAllocLen); #endif ret = OMX_EmptyThisBuffer(p_session->omx_handle, p_session->p_in_omx_buf[p_jobparams->src_index]); if (ret) { LOGE("Error"); goto error; } if (p_session->params.encode_thumbnail) { #ifdef MM_JPEG_DUMP_INPUT char thumb_filename[FILENAME_MAX]; snprintf(thumb_filename, sizeof(thumb_filename), QCAMERA_DUMP_FRM_LOCATION"jpeg/mm_jpeg_int_t%d.yuv", p_session->ebd_count); DUMP_TO_FILE(filename, p_session->p_in_omx_thumb_buf[p_jobparams->thumb_index]->pBuffer, (size_t)p_session->p_in_omx_thumb_buf[p_jobparams->thumb_index]->nAllocLen); #endif ret = OMX_EmptyThisBuffer(p_session->omx_handle, p_session->p_in_omx_thumb_buf[p_jobparams->thumb_index]); if (ret) { LOGE("Error"); goto error; } } ret = OMX_FillThisBuffer(p_session->omx_handle, p_session->p_out_omx_buf[p_jobparams->dst_index]); if (ret) { LOGE("Error"); goto error; } MM_JPEG_CHK_ABORT(p_session, ret, error); error: LOGD("X "); return ret; } /** mm_jpeg_process_encoding_job: * * Arguments: * @my_obj: jpeg client * @job_node: job node * * Return: * 0 for success -1 otherwise * * Description: * Start the encoding job * **/ int32_t mm_jpeg_process_encoding_job(mm_jpeg_obj *my_obj, mm_jpeg_job_q_node_t* job_node) { mm_jpeg_q_data_t qdata; int32_t rc = 0; OMX_ERRORTYPE ret = OMX_ErrorNone; mm_jpeg_job_session_t *p_session = NULL; uint32_t buf_idx; /* check if valid session */ p_session = mm_jpeg_get_session(my_obj, job_node->enc_info.job_id); if (NULL == p_session) { LOGE("invalid job id %x", job_node->enc_info.job_id); return -1; } LOGD("before dequeue session %d", ret); /* dequeue available omx handle */ qdata = mm_jpeg_queue_deq(p_session->session_handle_q); p_session = qdata.p; if (NULL == p_session) { LOGH("No available sessions %d", ret); /* No available handles */ qdata.p = job_node; mm_jpeg_queue_enq_head(&my_obj->job_mgr.job_queue, qdata); LOGH("end enqueue %d", ret); return rc; } p_session->auto_out_buf = OMX_FALSE; if (job_node->enc_info.encode_job.dst_index < 0) { /* dequeue available output buffer idx */ qdata = mm_jpeg_queue_deq(p_session->out_buf_q); buf_idx = qdata.u32; if (0U == buf_idx) { LOGE("No available output buffers %d", ret); return OMX_ErrorUndefined; } buf_idx--; job_node->enc_info.encode_job.dst_index = (int32_t)buf_idx; p_session->auto_out_buf = OMX_TRUE; } /* sent encode cmd to OMX, queue job into ongoing queue */ qdata.p = job_node; rc = mm_jpeg_queue_enq(&my_obj->ongoing_job_q, qdata); if (rc) { LOGE("jpeg enqueue failed %d", ret); goto error; } p_session->encode_job = job_node->enc_info.encode_job; p_session->jobId = job_node->enc_info.job_id; ret = mm_jpeg_session_encode(p_session); if (ret) { LOGE("encode session failed"); goto error; } LOGH("Success X "); return rc; error: if ((OMX_ErrorNone != ret) && (NULL != p_session->params.jpeg_cb)) { p_session->job_status = JPEG_JOB_STATUS_ERROR; LOGE("send jpeg error callback %d", p_session->job_status); p_session->params.jpeg_cb(p_session->job_status, p_session->client_hdl, p_session->jobId, NULL, p_session->params.userdata); } /*remove the job*/ mm_jpegenc_job_done(p_session); LOGD("Error X "); return rc; } /** mm_jpeg_jobmgr_thread: * * Arguments: * @my_obj: jpeg object * * Return: * 0 for success else failure * * Description: * job manager thread main function * **/ static void *mm_jpeg_jobmgr_thread(void *data) { mm_jpeg_q_data_t qdata; 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 = &my_obj->job_mgr; mm_jpeg_job_q_node_t* node = NULL; prctl(PR_SET_NAME, (unsigned long)"mm_jpeg_thread", 0, 0, 0); do { do { rc = cam_sem_wait(&cmd_thread->job_sem); if (rc != 0 && errno != EINVAL) { LOGE("cam_sem_wait error (%s)", strerror(errno)); return NULL; } } while (rc != 0); /* check ongoing q size */ num_ongoing_jobs = mm_jpeg_queue_get_size(&my_obj->ongoing_job_q); LOGD("ongoing job %d %d", num_ongoing_jobs, MM_JPEG_CONCURRENT_SESSIONS_COUNT); if (num_ongoing_jobs >= MM_JPEG_CONCURRENT_SESSIONS_COUNT) { LOGE("ongoing job already reach max %d", num_ongoing_jobs); continue; } pthread_mutex_lock(&my_obj->job_lock); /* can go ahead with new work */ qdata = mm_jpeg_queue_deq(&cmd_thread->job_queue); node = (mm_jpeg_job_q_node_t*)qdata.p; if (node != NULL) { switch (node->type) { case MM_JPEG_CMD_TYPE_JOB: rc = mm_jpeg_process_encoding_job(my_obj, node); break; case MM_JPEG_CMD_TYPE_DECODE_JOB: rc = mm_jpegdec_process_decoding_job(my_obj, 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; } /** mm_jpeg_jobmgr_thread_launch: * * Arguments: * @my_obj: jpeg object * * Return: * 0 for success else failure * * Description: * launches the job manager thread * **/ 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; cam_sem_init(&job_mgr->job_sem, 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); pthread_setname_np(job_mgr->pid, "CAM_jpeg_jobmgr"); return rc; } /** mm_jpeg_jobmgr_thread_release: * * Arguments: * @my_obj: jpeg object * * Return: * 0 for success else failure * * Description: * Releases the job manager thread * **/ int32_t mm_jpeg_jobmgr_thread_release(mm_jpeg_obj * my_obj) { mm_jpeg_q_data_t qdata; 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) { LOGE("No memory for mm_jpeg_job_q_node_t"); return -1; } memset(node, 0, sizeof(mm_jpeg_job_q_node_t)); node->type = MM_JPEG_CMD_TYPE_EXIT; qdata.p = node; mm_jpeg_queue_enq(&cmd_thread->job_queue, qdata); cam_sem_post(&cmd_thread->job_sem); /* wait until cmd thread exits */ if (pthread_join(cmd_thread->pid, NULL) != 0) { LOGD("pthread dead already"); } mm_jpeg_queue_deinit(&cmd_thread->job_queue); cam_sem_destroy(&cmd_thread->job_sem); memset(cmd_thread, 0, sizeof(mm_jpeg_job_cmd_thread_t)); return rc; } /** mm_jpeg_alloc_workbuffer: * * Arguments: * @my_obj: jpeg object * @work_bufs_need: number of work buffers required * @work_buf_size: size of the work buffer * * Return: * greater or equal to 0 for success else failure * * Description: * Allocates work buffer * **/ int32_t mm_jpeg_alloc_workbuffer(mm_jpeg_obj *my_obj, uint32_t work_bufs_need, uint32_t work_buf_size) { int32_t rc = 0; uint32_t i; LOGH("work_bufs_need %d work_buf_cnt %d", work_bufs_need, my_obj->work_buf_cnt); for (i = my_obj->work_buf_cnt; i < work_bufs_need; i++) { my_obj->ionBuffer[i].size = CEILING32(work_buf_size); LOGH("Max picture size %d x %d, WorkBufSize = %zu", my_obj->max_pic_w, my_obj->max_pic_h, my_obj->ionBuffer[i].size); my_obj->ionBuffer[i].addr = (uint8_t *)buffer_allocate(&my_obj->ionBuffer[i], 1); if (NULL == my_obj->ionBuffer[i].addr) { LOGE("Ion allocation failed"); while (i--) { buffer_deallocate(&my_obj->ionBuffer[i]); my_obj->work_buf_cnt--; } return -1; } my_obj->work_buf_cnt++; rc = i; } LOGH("rc %d ", rc); return rc; } /** mm_jpeg_release_workbuffer: * * Arguments: * @my_obj: jpeg object * @work_bufs_need: number of work buffers allocated * * Return: * 0 for success else failure * * Description: * Releases the allocated work buffer * **/ int32_t mm_jpeg_release_workbuffer(mm_jpeg_obj *my_obj, uint32_t work_bufs_need) { int32_t rc = 0; uint32_t i; LOGH("release work_bufs %d ", work_bufs_need); for (i = my_obj->work_buf_cnt; i < work_bufs_need; i++) { buffer_deallocate(&my_obj->ionBuffer[i]); } return rc; } /** mm_jpeg_init: * * Arguments: * @my_obj: jpeg object * * Return: * 0 for success else failure * * Description: * Initializes the jpeg client * **/ int32_t mm_jpeg_init(mm_jpeg_obj *my_obj) { int32_t rc = 0; uint32_t work_buf_size; unsigned int initial_workbufs_cnt = 1; /* init locks */ pthread_mutex_init(&my_obj->job_lock, NULL); /* init ongoing job queue */ rc = mm_jpeg_queue_init(&my_obj->ongoing_job_q); if (0 != rc) { LOGE("Error"); pthread_mutex_destroy(&my_obj->job_lock); return -1; } /* init job semaphore and launch jobmgr thread */ LOGD("Launch jobmgr thread rc %d", rc); rc = mm_jpeg_jobmgr_thread_launch(my_obj); if (0 != rc) { LOGE("Error"); mm_jpeg_queue_deinit(&my_obj->ongoing_job_q); pthread_mutex_destroy(&my_obj->job_lock); return -1; } /* set work buf size from max picture size */ if (my_obj->max_pic_w <= 0 || my_obj->max_pic_h <= 0) { LOGE("Width and height are not valid " "dimensions, cannot calc work buf size"); mm_jpeg_jobmgr_thread_release(my_obj); mm_jpeg_queue_deinit(&my_obj->ongoing_job_q); pthread_mutex_destroy(&my_obj->job_lock); return -1; } /* allocate work buffer if reproc source buffer is not supposed to be used */ if (!my_obj->reuse_reproc_buffer) { work_buf_size = CEILING64((uint32_t)my_obj->max_pic_w) * CEILING64((uint32_t)my_obj->max_pic_h) * 3U / 2U; rc = mm_jpeg_alloc_workbuffer(my_obj, initial_workbufs_cnt, work_buf_size); if (rc == -1) { LOGE("Work buffer allocation failure"); return rc; } } /* load OMX */ if (OMX_ErrorNone != OMX_Init()) { /* roll back in error case */ LOGE("OMX_Init failed (%d)", rc); if (!my_obj->reuse_reproc_buffer) { mm_jpeg_release_workbuffer(my_obj, initial_workbufs_cnt); } mm_jpeg_jobmgr_thread_release(my_obj); mm_jpeg_queue_deinit(&my_obj->ongoing_job_q); pthread_mutex_destroy(&my_obj->job_lock); } #ifdef LOAD_ADSP_RPC_LIB my_obj->adsprpc_lib_handle = dlopen("libadsprpc.so", RTLD_NOW); if (NULL == my_obj->adsprpc_lib_handle) { LOGE("Cannot load the library"); /* not returning error here bcoz even if this loading fails we can go ahead with SW JPEG enc */ } #endif // create dummy OMX handle to avoid dlopen latency OMX_GetHandle(&my_obj->dummy_handle, mm_jpeg_get_comp_name(), NULL, NULL); return rc; } /** mm_jpeg_deinit: * * Arguments: * @my_obj: jpeg object * * Return: * 0 for success else failure * * Description: * Deinits the jpeg client * **/ int32_t mm_jpeg_deinit(mm_jpeg_obj *my_obj) { int32_t rc = 0; uint32_t i = 0; /* release jobmgr thread */ rc = mm_jpeg_jobmgr_thread_release(my_obj); if (0 != rc) { LOGE("Error"); } if (my_obj->dummy_handle) { OMX_FreeHandle(my_obj->dummy_handle); } /* unload OMX engine */ OMX_Deinit(); /* deinit ongoing job and cb queue */ rc = mm_jpeg_queue_deinit(&my_obj->ongoing_job_q); if (0 != rc) { LOGE("Error"); } for (i = 0; i < my_obj->work_buf_cnt; i++) { /*Release the ION buffer*/ rc = buffer_deallocate(&my_obj->ionBuffer[i]); if (0 != rc) { LOGE("Error releasing ION buffer"); } } my_obj->work_buf_cnt = 0; my_obj->jpeg_metadata = NULL; /* destroy locks */ pthread_mutex_destroy(&my_obj->job_lock); return rc; } /** mm_jpeg_new_client: * * Arguments: * @my_obj: jpeg object * * Return: * 0 for success else failure * * Description: * Create new jpeg client * **/ uint32_t mm_jpeg_new_client(mm_jpeg_obj *my_obj) { uint32_t client_hdl = 0; uint8_t idx; int i = 0; if (my_obj->num_clients >= MAX_JPEG_CLIENT_NUM) { LOGE("num of clients reached limit"); 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 session avail */ /* generate client handler by index */ client_hdl = mm_jpeg_util_generate_handler(idx); /* update client session */ my_obj->clnt_mgr[idx].is_used = 1; my_obj->clnt_mgr[idx].client_handle = client_hdl; pthread_mutex_init(&my_obj->clnt_mgr[idx].lock, NULL); for (i = 0; i < MM_JPEG_MAX_SESSION; i++) { memset(&my_obj->clnt_mgr[idx].session[i], 0x0, sizeof(mm_jpeg_job_session_t)); } /* increse client count */ my_obj->num_clients++; } return client_hdl; } /** mm_jpeg_start_job: * * Arguments: * @my_obj: jpeg object * @client_hdl: client handle * @job: pointer to encode job * @jobId: job id * * Return: * 0 for success else failure * * Description: * Start the encoding job * **/ int32_t mm_jpeg_start_job(mm_jpeg_obj *my_obj, mm_jpeg_job_t *job, uint32_t *job_id) { mm_jpeg_q_data_t qdata; int32_t rc = -1; uint8_t session_idx = 0; uint8_t client_idx = 0; mm_jpeg_job_q_node_t* node = NULL; mm_jpeg_job_session_t *p_session = NULL; mm_jpeg_encode_job_t *p_jobparams = NULL; uint32_t work_bufs_need; uint32_t work_buf_size; *job_id = 0; if (!job) { LOGE("invalid job !!!"); return rc; } p_jobparams = &job->encode_job; /* check if valid session */ session_idx = GET_SESSION_IDX(p_jobparams->session_id); client_idx = GET_CLIENT_IDX(p_jobparams->session_id); LOGD("session_idx %d client idx %d", session_idx, client_idx); if ((session_idx >= MM_JPEG_MAX_SESSION) || (client_idx >= MAX_JPEG_CLIENT_NUM)) { LOGE("invalid session id %x", job->encode_job.session_id); return rc; } p_session = &my_obj->clnt_mgr[client_idx].session[session_idx]; if (my_obj->reuse_reproc_buffer) { p_session->work_buffer.addr = p_jobparams->work_buf.buf_vaddr; p_session->work_buffer.size = p_jobparams->work_buf.buf_size; p_session->work_buffer.ion_info_fd.fd = p_jobparams->work_buf.fd; p_session->work_buffer.p_pmem_fd = p_jobparams->work_buf.fd; work_bufs_need = my_obj->num_sessions + 1; if (work_bufs_need > MM_JPEG_CONCURRENT_SESSIONS_COUNT) { work_bufs_need = MM_JPEG_CONCURRENT_SESSIONS_COUNT; } if (p_session->work_buffer.addr) { work_bufs_need--; LOGD("HAL passed the work buffer of size = %d; don't alloc internally", p_session->work_buffer.size); } else { p_session->work_buffer = my_obj->ionBuffer[0]; } LOGD(">>>> Work bufs need %d, %d", work_bufs_need, my_obj->work_buf_cnt); if (work_bufs_need) { work_buf_size = CEILING64(my_obj->max_pic_w) * CEILING64(my_obj->max_pic_h) * 3 / 2; rc = mm_jpeg_alloc_workbuffer(my_obj, work_bufs_need, work_buf_size); if (rc == -1) { LOGE("Work buffer allocation failure"); return rc; } else { p_session->work_buffer = my_obj->ionBuffer[rc]; } } } if (OMX_FALSE == p_session->active) { LOGE("session not active %x", job->encode_job.session_id); return rc; } if ((p_jobparams->src_index >= (int32_t)p_session->params.num_src_bufs) || (p_jobparams->dst_index >= (int32_t)p_session->params.num_dst_bufs)) { LOGE("invalid buffer indices"); return rc; } /* 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) { LOGE("No memory for mm_jpeg_job_q_node_t"); return -1; } KPI_ATRACE_INT("Camera:JPEG", (int32_t)((uint32_t)session_idx<<16 | ++p_session->job_index)); *job_id = job->encode_job.session_id | (((uint32_t)p_session->job_hist++ % JOB_HIST_MAX) << 16); memset(node, 0, sizeof(mm_jpeg_job_q_node_t)); node->enc_info.encode_job = job->encode_job; if (p_session->thumb_from_main) { node->enc_info.encode_job.thumb_dim.src_dim = node->enc_info.encode_job.main_dim.src_dim; node->enc_info.encode_job.thumb_dim.crop = node->enc_info.encode_job.main_dim.crop; } node->enc_info.job_id = *job_id; node->enc_info.client_handle = p_session->client_hdl; node->type = MM_JPEG_CMD_TYPE_JOB; qdata.p = node; rc = mm_jpeg_queue_enq(&my_obj->job_mgr.job_queue, qdata); if (0 == rc) { cam_sem_post(&my_obj->job_mgr.job_sem); } LOGH("session_idx %u client_idx %u job_id %d X", session_idx, client_idx, *job_id); return rc; } /** mm_jpeg_abort_job: * * Arguments: * @my_obj: jpeg object * @client_hdl: client handle * @jobId: job id * * Return: * 0 for success else failure * * Description: * Abort the encoding session * **/ int32_t mm_jpeg_abort_job(mm_jpeg_obj *my_obj, uint32_t jobId) { int32_t rc = -1; mm_jpeg_job_q_node_t *node = NULL; mm_jpeg_job_session_t *p_session = NULL; pthread_mutex_lock(&my_obj->job_lock); /* 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) { free(node); goto abort_done; } /* 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 */ p_session = mm_jpeg_get_session(my_obj, node->enc_info.job_id); if (p_session) { mm_jpeg_session_abort(p_session); } else { LOGE("Invalid job id 0x%x", node->enc_info.job_id); } free(node); goto abort_done; } abort_done: pthread_mutex_unlock(&my_obj->job_lock); return rc; } #ifdef MM_JPEG_READ_META_KEYFILE static int32_t mm_jpeg_read_meta_keyfile(mm_jpeg_job_session_t *p_session, const char *filename) { int rc = 0; FILE *fp = NULL; size_t file_size = 0; fp = fopen(filename, "r"); if (!fp) { LOGE("Key not present"); return -1; } fseek(fp, 0, SEEK_END); file_size = (size_t)ftell(fp); fseek(fp, 0, SEEK_SET); p_session->meta_enc_key = (uint8_t *) malloc((file_size + 1) * sizeof(uint8_t)); if (!p_session->meta_enc_key) { LOGE("error"); return -1; } fread(p_session->meta_enc_key, 1, file_size, fp); fclose(fp); p_session->meta_enc_keylen = file_size; return rc; } #endif // MM_JPEG_READ_META_KEYFILE /** mm_jpeg_create_session: * * Arguments: * @my_obj: jpeg object * @client_hdl: client handle * @p_params: pointer to encode params * @p_session_id: session id * * Return: * 0 for success else failure * * Description: * Start the encoding session * **/ int32_t mm_jpeg_create_session(mm_jpeg_obj *my_obj, uint32_t client_hdl, mm_jpeg_encode_params_t *p_params, uint32_t* p_session_id) { mm_jpeg_q_data_t qdata; int32_t rc = 0; OMX_ERRORTYPE ret = OMX_ErrorNone; uint8_t clnt_idx = 0; int session_idx = -1; mm_jpeg_job_session_t *p_session = NULL; mm_jpeg_job_session_t * p_prev_session = NULL; *p_session_id = 0; uint32_t i = 0; uint32_t num_omx_sessions = 1; uint32_t work_buf_size; mm_jpeg_queue_t *p_session_handle_q, *p_out_buf_q; uint32_t work_bufs_need; char trace_tag[32]; /* validate the parameters */ if ((p_params->num_src_bufs > MM_JPEG_MAX_BUF) || (p_params->num_dst_bufs > MM_JPEG_MAX_BUF)) { LOGE("invalid num buffers"); return -1; } /* check if valid client */ clnt_idx = mm_jpeg_util_get_index_by_handler(client_hdl); if (clnt_idx >= MAX_JPEG_CLIENT_NUM) { LOGE("invalid client with handler (%d)", client_hdl); return -1; } if (p_params->burst_mode) { num_omx_sessions = MM_JPEG_CONCURRENT_SESSIONS_COUNT; } if (!my_obj->reuse_reproc_buffer) { work_bufs_need = num_omx_sessions; if (work_bufs_need > MM_JPEG_CONCURRENT_SESSIONS_COUNT) { work_bufs_need = MM_JPEG_CONCURRENT_SESSIONS_COUNT; } LOGD(">>>> Work bufs need %d", work_bufs_need); work_buf_size = CEILING64(my_obj->max_pic_w) * CEILING64(my_obj->max_pic_h) * 3 / 2; rc = mm_jpeg_alloc_workbuffer(my_obj, work_bufs_need, work_buf_size); if (rc == -1) { LOGE("Work buffer allocation failure"); return rc; } } /* init omx handle queue */ p_session_handle_q = (mm_jpeg_queue_t *) malloc(sizeof(*p_session_handle_q)); if (NULL == p_session_handle_q) { LOGE("Error"); goto error1; } rc = mm_jpeg_queue_init(p_session_handle_q); if (0 != rc) { LOGE("Error"); free(p_session_handle_q); goto error1; } /* init output buf queue */ p_out_buf_q = (mm_jpeg_queue_t *) malloc(sizeof(*p_out_buf_q)); if (NULL == p_out_buf_q) { LOGE("Error: Cannot allocate memory\n"); return -1; } /* init omx handle queue */ rc = mm_jpeg_queue_init(p_out_buf_q); if (0 != rc) { LOGE("Error"); free(p_out_buf_q); goto error1; } for (i = 0; i < num_omx_sessions; i++) { uint32_t buf_idx = 0U; session_idx = mm_jpeg_get_new_session_idx(my_obj, clnt_idx, &p_session); if (session_idx < 0 || NULL == p_session) { LOGE("invalid session id (%d)", session_idx); goto error2; } snprintf(trace_tag, sizeof(trace_tag), "Camera:JPEGsession%d", session_idx); ATRACE_INT(trace_tag, 1); p_session->job_index = 0; p_session->next_session = NULL; if (p_prev_session) { p_prev_session->next_session = p_session; } p_prev_session = p_session; buf_idx = i; if (buf_idx < MM_JPEG_CONCURRENT_SESSIONS_COUNT) { p_session->work_buffer = my_obj->ionBuffer[buf_idx]; } else { LOGE("Invalid Index, Setting buffer add to null"); p_session->work_buffer.addr = NULL; p_session->work_buffer.ion_fd = -1; p_session->work_buffer.p_pmem_fd = -1; } p_session->jpeg_obj = (void*)my_obj; /* save a ptr to jpeg_obj */ /*copy the params*/ p_session->params = *p_params; ret = mm_jpeg_session_create(p_session); if (OMX_ErrorNone != ret) { p_session->active = OMX_FALSE; LOGE("jpeg session create failed"); goto error2; } uint32_t session_id = (JOB_ID_MAGICVAL << 24) | ((uint32_t)session_idx << 8) | clnt_idx; if (!*p_session_id) { *p_session_id = session_id; } if (p_session->thumb_from_main) { memcpy(p_session->params.src_thumb_buf, p_session->params.src_main_buf, sizeof(p_session->params.src_thumb_buf)); p_session->params.num_tmb_bufs = p_session->params.num_src_bufs; if (!p_session->params.encode_thumbnail) { p_session->params.num_tmb_bufs = 0; } p_session->params.thumb_dim.src_dim = p_session->params.main_dim.src_dim; p_session->params.thumb_dim.crop = p_session->params.main_dim.crop; } p_session->client_hdl = client_hdl; p_session->sessionId = session_id; p_session->session_handle_q = p_session_handle_q; p_session->out_buf_q = p_out_buf_q; qdata.p = p_session; mm_jpeg_queue_enq(p_session_handle_q, qdata); p_session->meta_enc_key = NULL; p_session->meta_enc_keylen = 0; #ifdef MM_JPEG_READ_META_KEYFILE mm_jpeg_read_meta_keyfile(p_session, META_KEYFILE); #endif pthread_mutex_lock(&my_obj->job_lock); /* Configure session if not already configured and if no other session configured*/ if ((OMX_FALSE == p_session->config) && (my_obj->p_session_inprogress == NULL)) { rc = mm_jpeg_session_configure(p_session); if (rc) { LOGE("Error"); pthread_mutex_unlock(&my_obj->job_lock); goto error2; } p_session->config = OMX_TRUE; my_obj->p_session_inprogress = p_session; } pthread_mutex_unlock(&my_obj->job_lock); p_session->num_omx_sessions = num_omx_sessions; LOGH("session id %x thumb_from_main %d", session_id, p_session->thumb_from_main); } // Queue the output buf indexes for (i = 0; i < p_params->num_dst_bufs; i++) { qdata.u32 = i + 1; mm_jpeg_queue_enq(p_out_buf_q, qdata); } return rc; error1: rc = -1; error2: if (NULL != p_session) { ATRACE_INT(trace_tag, 0); } return rc; } /** mm_jpegenc_destroy_job * * Arguments: * @p_session: Session obj * * Return: * 0 for success else failure * * Description: * Destroy the job based paramenters * **/ static int32_t mm_jpegenc_destroy_job(mm_jpeg_job_session_t *p_session) { mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job; int i = 0, rc = 0; LOGD("Exif entry count %d %d", (int)p_jobparams->exif_info.numOfEntries, (int)p_session->exif_count_local); for (i = 0; i < p_session->exif_count_local; i++) { rc = releaseExifEntry(&p_session->exif_info_local[i]); if (rc) { LOGE("Exif release failed (%d)", rc); } } p_session->exif_count_local = 0; return rc; } /** mm_jpeg_session_encode: * * Arguments: * @p_session: encode session * * Return: * OMX_ERRORTYPE * * Description: * Start the encoding * **/ static void mm_jpegenc_job_done(mm_jpeg_job_session_t *p_session) { mm_jpeg_q_data_t qdata; mm_jpeg_obj *my_obj = (mm_jpeg_obj *)p_session->jpeg_obj; mm_jpeg_job_q_node_t *node = NULL; /*Destroy job related params*/ mm_jpegenc_destroy_job(p_session); /*remove the job*/ node = mm_jpeg_queue_remove_job_by_job_id(&my_obj->ongoing_job_q, p_session->jobId); if (node) { free(node); } p_session->encoding = OMX_FALSE; // Queue to available sessions qdata.p = p_session; mm_jpeg_queue_enq(p_session->session_handle_q, qdata); if (p_session->auto_out_buf) { //Queue out buf index qdata.u32 = (uint32_t)(p_session->encode_job.dst_index + 1); mm_jpeg_queue_enq(p_session->out_buf_q, qdata); } /* wake up jobMgr thread to work on new job if there is any */ cam_sem_post(&my_obj->job_mgr.job_sem); } /** mm_jpeg_destroy_session: * * Arguments: * @my_obj: jpeg object * @session_id: session index * * Return: * 0 for success else failure * * Description: * Destroy the encoding session * **/ int32_t mm_jpeg_destroy_session(mm_jpeg_obj *my_obj, mm_jpeg_job_session_t *p_session) { mm_jpeg_q_data_t qdata; int32_t rc = 0; mm_jpeg_job_q_node_t *node = NULL; uint32_t session_id = 0; mm_jpeg_job_session_t *p_cur_sess; char trace_tag[32]; if (NULL == p_session) { LOGE("invalid session"); return rc; } session_id = p_session->sessionId; pthread_mutex_lock(&my_obj->job_lock); /* abort job if in todo queue */ LOGD("abort todo jobs"); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->job_mgr.job_queue, session_id); while (NULL != node) { free(node); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->job_mgr.job_queue, session_id); } /* abort job if in ongoing queue */ LOGD("abort ongoing jobs"); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->ongoing_job_q, session_id); while (NULL != node) { free(node); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->ongoing_job_q, session_id); } /* abort the current session */ mm_jpeg_session_abort(p_session); mm_jpeg_session_destroy(p_session); p_cur_sess = p_session; do { mm_jpeg_remove_session_idx(my_obj, p_cur_sess->sessionId); } while (NULL != (p_cur_sess = p_cur_sess->next_session)); pthread_mutex_unlock(&my_obj->job_lock); while (1) { qdata = mm_jpeg_queue_deq(p_session->session_handle_q); if (NULL == qdata.p) break; } mm_jpeg_queue_deinit(p_session->session_handle_q); free(p_session->session_handle_q); p_session->session_handle_q = NULL; while (1) { qdata = mm_jpeg_queue_deq(p_session->out_buf_q); if (0U == qdata.u32) break; } mm_jpeg_queue_deinit(p_session->out_buf_q); free(p_session->out_buf_q); p_session->out_buf_q = NULL; /* wake up jobMgr thread to work on new job if there is any */ cam_sem_post(&my_obj->job_mgr.job_sem); snprintf(trace_tag, sizeof(trace_tag), "Camera:JPEGsession%d", GET_SESSION_IDX(session_id)); ATRACE_INT(trace_tag, 0); LOGH("destroy session successful. X"); return rc; } /** mm_jpeg_destroy_session: * * Arguments: * @my_obj: jpeg object * @session_id: session index * * Return: * 0 for success else failure * * Description: * Destroy the encoding session * **/ int32_t mm_jpeg_destroy_session_unlocked(mm_jpeg_obj *my_obj, mm_jpeg_job_session_t *p_session) { int32_t rc = -1; mm_jpeg_job_q_node_t *node = NULL; uint32_t session_id = 0; if (NULL == p_session) { LOGE("invalid session"); return rc; } session_id = p_session->sessionId; /* abort job if in todo queue */ LOGD("abort todo jobs"); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->job_mgr.job_queue, session_id); while (NULL != node) { free(node); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->job_mgr.job_queue, session_id); } /* abort job if in ongoing queue */ LOGD("abort ongoing jobs"); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->ongoing_job_q, session_id); while (NULL != node) { free(node); node = mm_jpeg_queue_remove_job_by_session_id(&my_obj->ongoing_job_q, session_id); } /* abort the current session */ mm_jpeg_session_abort(p_session); //mm_jpeg_remove_session_idx(my_obj, session_id); return rc; } /** mm_jpeg_destroy_session: * * Arguments: * @my_obj: jpeg object * @session_id: session index * * Return: * 0 for success else failure * * Description: * Destroy the encoding session * **/ int32_t mm_jpeg_destroy_session_by_id(mm_jpeg_obj *my_obj, uint32_t session_id) { mm_jpeg_job_session_t *p_session = mm_jpeg_get_session(my_obj, session_id); return mm_jpeg_destroy_session(my_obj, p_session); } /** mm_jpeg_close: * * Arguments: * @my_obj: jpeg object * @client_hdl: client handle * * Return: * 0 for success else failure * * Description: * Close the jpeg client * **/ int32_t mm_jpeg_close(mm_jpeg_obj *my_obj, uint32_t client_hdl) { int32_t rc = -1; uint8_t clnt_idx = 0; int i = 0; /* check if valid client */ clnt_idx = mm_jpeg_util_get_index_by_handler(client_hdl); if (clnt_idx >= MAX_JPEG_CLIENT_NUM) { LOGE("invalid client with handler (%d)", client_hdl); return rc; } LOGD("E"); /* abort all jobs from the client */ pthread_mutex_lock(&my_obj->job_lock); for (i = 0; i < MM_JPEG_MAX_SESSION; i++) { if (OMX_TRUE == my_obj->clnt_mgr[clnt_idx].session[i].active) mm_jpeg_destroy_session_unlocked(my_obj, &my_obj->clnt_mgr[clnt_idx].session[i]); } #ifdef LOAD_ADSP_RPC_LIB if (NULL != my_obj->adsprpc_lib_handle) { dlclose(my_obj->adsprpc_lib_handle); my_obj->adsprpc_lib_handle = NULL; } #endif pthread_mutex_unlock(&my_obj->job_lock); /* invalidate client session */ pthread_mutex_destroy(&my_obj->clnt_mgr[clnt_idx].lock); memset(&my_obj->clnt_mgr[clnt_idx], 0, sizeof(mm_jpeg_client_t)); rc = 0; LOGD("X"); return rc; } OMX_ERRORTYPE mm_jpeg_ebd(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE *pBuffer) { mm_jpeg_job_session_t *p_session = (mm_jpeg_job_session_t *) pAppData; LOGH("count %d ", p_session->ebd_count); pthread_mutex_lock(&p_session->lock); p_session->ebd_count++; pthread_mutex_unlock(&p_session->lock); return 0; } OMX_ERRORTYPE mm_jpeg_fbd(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_BUFFERHEADERTYPE *pBuffer) { OMX_ERRORTYPE ret = OMX_ErrorNone; mm_jpeg_job_session_t *p_session = (mm_jpeg_job_session_t *) pAppData; mm_jpeg_output_t output_buf; LOGI("count %d ", p_session->fbd_count); LOGI("KPI Perf] : PROFILE_JPEG_FBD"); pthread_mutex_lock(&p_session->lock); KPI_ATRACE_INT("Camera:JPEG", (int32_t)((uint32_t)GET_SESSION_IDX( p_session->sessionId)<<16 | --p_session->job_index)); if (MM_JPEG_ABORT_NONE != p_session->abort_state) { pthread_mutex_unlock(&p_session->lock); return ret; } #ifdef MM_JPEG_DUMP_OUT_BS char filename[256]; static int bsc; snprintf(filename, sizeof(filename), QCAMERA_DUMP_FRM_LOCATION"jpeg/mm_jpeg_bs%d.jpg", bsc++); DUMP_TO_FILE(filename, pBuffer->pBuffer, (size_t)(uint32_t)pBuffer->nFilledLen); #endif p_session->fbd_count++; if (NULL != p_session->params.jpeg_cb) { p_session->job_status = JPEG_JOB_STATUS_DONE; output_buf.buf_filled_len = (uint32_t)pBuffer->nFilledLen; output_buf.buf_vaddr = pBuffer->pBuffer; output_buf.fd = -1; LOGH("send jpeg callback %d buf 0x%p len %u JobID %u", p_session->job_status, pBuffer->pBuffer, (unsigned int)pBuffer->nFilledLen, p_session->jobId); p_session->params.jpeg_cb(p_session->job_status, p_session->client_hdl, p_session->jobId, &output_buf, p_session->params.userdata); mm_jpegenc_job_done(p_session); mm_jpeg_put_mem((void *)p_session); } pthread_mutex_unlock(&p_session->lock); return ret; } OMX_ERRORTYPE mm_jpeg_event_handler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData) { mm_jpeg_job_session_t *p_session = (mm_jpeg_job_session_t *) pAppData; LOGD("%d %d %d state %d", eEvent, (int)nData1, (int)nData2, p_session->abort_state); pthread_mutex_lock(&p_session->lock); if (MM_JPEG_ABORT_INIT == p_session->abort_state) { p_session->abort_state = MM_JPEG_ABORT_DONE; pthread_cond_signal(&p_session->cond); pthread_mutex_unlock(&p_session->lock); return OMX_ErrorNone; } if (eEvent == OMX_EventError) { p_session->error_flag = nData2; if (p_session->encoding == OMX_TRUE) { LOGE("Error during encoding"); /* send jpeg callback */ if (NULL != p_session->params.jpeg_cb) { p_session->job_status = JPEG_JOB_STATUS_ERROR; LOGE("send jpeg error callback %d", p_session->job_status); p_session->params.jpeg_cb(p_session->job_status, p_session->client_hdl, p_session->jobId, NULL, p_session->params.userdata); } /* remove from ready queue */ mm_jpegenc_job_done(p_session); } pthread_cond_signal(&p_session->cond); } else if (eEvent == OMX_EventCmdComplete) { if (p_session->state_change_pending == OMX_TRUE) { p_session->state_change_pending = OMX_FALSE; pthread_cond_signal(&p_session->cond); } } pthread_mutex_unlock(&p_session->lock); return OMX_ErrorNone; } /* 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.p; if (data && (data->enc_info.client_handle == client_hdl)) { LOGH("found matching client handle"); job_node = data; cam_list_del_node(&node->list); queue->size--; free(node); LOGH("queue size = %d", queue->size); break; } pos = pos->next; } pthread_mutex_unlock(&queue->lock); return job_node; } /* remove the first job from the queue with matching session id */ mm_jpeg_job_q_node_t* mm_jpeg_queue_remove_job_by_session_id( mm_jpeg_queue_t* queue, uint32_t session_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.p; if (data && (data->enc_info.encode_job.session_id == session_id)) { LOGH("found matching session id"); job_node = data; cam_list_del_node(&node->list); queue->size--; free(node); LOGH("queue size = %d", 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; uint32_t lq_job_id; 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.p; if(NULL == data) { LOGE("Data is NULL"); pthread_mutex_unlock(&queue->lock); return NULL; } if (data->type == MM_JPEG_CMD_TYPE_DECODE_JOB) { lq_job_id = data->dec_info.job_id; } else { lq_job_id = data->enc_info.job_id; } if (data && (lq_job_id == job_id)) { LOGD("found matching job id"); job_node = data; cam_list_del_node(&node->list); queue->size--; free(node); 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_unlk( 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; 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.p; if (data && (data->enc_info.job_id == job_id)) { job_node = data; cam_list_del_node(&node->list); queue->size--; free(node); break; } pos = pos->next; } return job_node; }