| /* Copyright (c) 2015-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. |
| * |
| */ |
| |
| // To remove |
| #include <utils/Log.h> |
| |
| // System dependencies |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <pthread.h> |
| #include <sys/ioctl.h> |
| #include <sys/prctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| // Camera dependencies |
| #include "img_common.h" |
| #include "img_comp.h" |
| #include "img_comp_factory.h" |
| #include "img_buffer.h" |
| #include "lib2d.h" |
| #include "mm_lib2d.h" |
| |
| /** lib2d_job_private_info |
| * @jobid: Job id of this process request |
| * @userdata: Client userdata that will be passed on callback |
| * @lib2d_client_cb: Application's callback function pointer |
| * which will be called upon completion of current job. |
| **/ |
| typedef struct lib2d_job_private_info_t { |
| int jobid; |
| void *userdata; |
| lib2d_error (*lib2d_client_cb) (void *userdata, int jobid); |
| } lib2d_job_private_info; |
| |
| /** img_lib_t |
| * @ptr: handle to imglib library |
| * @img_core_get_comp: function pointer for img_core_get_comp |
| * @img_wait_for_completion: function pointer for img_wait_for_completion |
| **/ |
| typedef struct { |
| void *ptr; |
| int (*img_core_get_comp) (img_comp_role_t role, char *name, |
| img_core_ops_t *p_ops); |
| int (*img_wait_for_completion) (pthread_cond_t *p_cond, |
| pthread_mutex_t *p_mutex, int32_t ms); |
| } img_lib_t; |
| |
| /** mm_lib2d_obj |
| * @core_ops: image core ops structure handle |
| * @comp: component structure handle |
| * @comp_mode: underlying component mode |
| * @lib2d_mode: lib2d mode requested by client |
| * @img_lib: imglib library, function ptrs handle |
| * @mutex: lib2d mutex used for synchronization |
| * @cond: librd cond used for synchronization |
| **/ |
| typedef struct mm_lib2d_obj_t { |
| img_core_ops_t core_ops; |
| img_component_ops_t comp; |
| img_comp_mode_t comp_mode; |
| lib2d_mode lib2d_mode; |
| img_lib_t img_lib; |
| pthread_mutex_t mutex; |
| pthread_cond_t cond; |
| } mm_lib2d_obj; |
| |
| |
| /** |
| * Function: lib2d_event_handler |
| * |
| * Description: Event handler. All the component events |
| * are received here. |
| * |
| * Input parameters: |
| * p_appdata - lib2d test object |
| * p_event - pointer to the event |
| * |
| * Return values: |
| * IMG_SUCCESS |
| * IMG_ERR_INVALID_INPUT |
| * |
| * Notes: none |
| **/ |
| int lib2d_event_handler(void* p_appdata, img_event_t *p_event) |
| { |
| mm_lib2d_obj *lib2d_obj = (mm_lib2d_obj *)p_appdata; |
| |
| if ((NULL == p_event) || (NULL == p_appdata)) { |
| LOGE("invalid event"); |
| return IMG_ERR_INVALID_INPUT; |
| } |
| |
| LOGD("type %d", p_event->type); |
| |
| switch (p_event->type) { |
| case QIMG_EVT_DONE: |
| pthread_cond_signal(&lib2d_obj->cond); |
| break; |
| default:; |
| } |
| return IMG_SUCCESS; |
| } |
| |
| /** |
| * Function: lib2d_callback_handler |
| * |
| * Description: Callback handler. Registered with Component |
| * on IMG_COMP_INIT. Will be called when processing |
| * of current request is completed. If component running in |
| * async mode, this is where client will know the execution |
| * is finished for in, out frames. |
| * |
| * Input parameters: |
| * p_appdata - lib2d test object |
| * p_in_frame - pointer to input frame |
| * p_out_frame - pointer to output frame |
| * |
| * Return values: |
| * IMG_SUCCESS |
| * IMG_ERR_GENERAL |
| * |
| * Notes: none |
| **/ |
| int lib2d_callback_handler(void *userdata, img_frame_t *p_in_frame, |
| img_frame_t *p_out_frame) |
| { |
| mm_lib2d_obj *lib2d_obj = (mm_lib2d_obj *)userdata; |
| lib2d_job_private_info *job_info = NULL; |
| |
| if (NULL == userdata) { |
| LOGE("invalid event"); |
| return IMG_ERR_INVALID_INPUT; |
| } |
| |
| // assert(p_in_frame->private_data == p_out_frame->private_data); |
| |
| job_info = (lib2d_job_private_info *)p_in_frame->private_data; |
| if (job_info->lib2d_client_cb != NULL) { |
| job_info->lib2d_client_cb(job_info->userdata, job_info->jobid); |
| } |
| |
| free(p_in_frame->private_data); |
| free(p_in_frame); |
| free(p_out_frame); |
| |
| return IMG_SUCCESS; |
| } |
| |
| /** |
| * Function: lib2d_fill_img_frame |
| * |
| * Description: Setup img_frame_t for given buffer |
| * |
| * Input parameters: |
| * p_frame - pointer to img_frame_t that needs to be setup |
| * lib2d_buffer - pointer to input buffer |
| * jobid - job id |
| * |
| * Return values: |
| * MM_LIB2D_SUCCESS |
| * MM_LIB2D_ERR_GENERAL |
| * |
| * Notes: none |
| **/ |
| lib2d_error lib2d_fill_img_frame(img_frame_t *p_frame, |
| mm_lib2d_buffer* lib2d_buffer, int jobid) |
| { |
| // use job id for now |
| p_frame->frame_cnt = jobid; |
| p_frame->idx = jobid; |
| p_frame->frame_id = jobid; |
| |
| if (lib2d_buffer->buffer_type == MM_LIB2D_BUFFER_TYPE_RGB) { |
| mm_lib2d_rgb_buffer *rgb_buffer = &lib2d_buffer->rgb_buffer; |
| |
| p_frame->info.num_planes = 1; |
| p_frame->info.width = rgb_buffer->width; |
| p_frame->info.height = rgb_buffer->height; |
| |
| p_frame->frame[0].plane_cnt = 1; |
| p_frame->frame[0].plane[0].plane_type = PLANE_ARGB; |
| p_frame->frame[0].plane[0].addr = rgb_buffer->buffer; |
| p_frame->frame[0].plane[0].stride = rgb_buffer->stride; |
| p_frame->frame[0].plane[0].length = (rgb_buffer->stride * |
| rgb_buffer->height); |
| p_frame->frame[0].plane[0].fd = rgb_buffer->fd; |
| p_frame->frame[0].plane[0].height = rgb_buffer->height; |
| p_frame->frame[0].plane[0].width = rgb_buffer->width; |
| p_frame->frame[0].plane[0].offset = 0; |
| p_frame->frame[0].plane[0].scanline = rgb_buffer->height; |
| } else if (lib2d_buffer->buffer_type == MM_LIB2D_BUFFER_TYPE_YUV) { |
| mm_lib2d_yuv_buffer *yuv_buffer = &lib2d_buffer->yuv_buffer; |
| |
| p_frame->info.num_planes = 2; |
| p_frame->info.width = yuv_buffer->width; |
| p_frame->info.height = yuv_buffer->height; |
| |
| p_frame->frame[0].plane_cnt = 2; |
| p_frame->frame[0].plane[0].plane_type = PLANE_Y; |
| p_frame->frame[0].plane[0].addr = yuv_buffer->plane0; |
| p_frame->frame[0].plane[0].stride = yuv_buffer->stride0; |
| p_frame->frame[0].plane[0].length = (yuv_buffer->stride0 * |
| yuv_buffer->height); |
| p_frame->frame[0].plane[0].fd = yuv_buffer->fd; |
| p_frame->frame[0].plane[0].height = yuv_buffer->height; |
| p_frame->frame[0].plane[0].width = yuv_buffer->width; |
| p_frame->frame[0].plane[0].offset = 0; |
| p_frame->frame[0].plane[0].scanline = yuv_buffer->height; |
| |
| if (yuv_buffer->format == CAM_FORMAT_YUV_420_NV12) { |
| p_frame->frame[0].plane[1].plane_type = PLANE_CB_CR; |
| } else if(yuv_buffer->format == CAM_FORMAT_YUV_420_NV21) { |
| p_frame->frame[0].plane[1].plane_type = PLANE_CR_CB; |
| } |
| p_frame->frame[0].plane[1].addr = yuv_buffer->plane1; |
| p_frame->frame[0].plane[1].stride = yuv_buffer->stride1; |
| p_frame->frame[0].plane[1].length = (yuv_buffer->stride1 * |
| yuv_buffer->height / 2); |
| p_frame->frame[0].plane[1].fd = yuv_buffer->fd; |
| p_frame->frame[0].plane[1].height = yuv_buffer->height; |
| p_frame->frame[0].plane[1].width = yuv_buffer->width; |
| p_frame->frame[0].plane[1].offset = 0; |
| p_frame->frame[0].plane[1].scanline = yuv_buffer->height; |
| } else { |
| return MM_LIB2D_ERR_GENERAL; |
| } |
| |
| return MM_LIB2D_SUCCESS; |
| } |
| |
| /** |
| * Function: mm_lib2d_init |
| * |
| * Description: Initialization function for Lib2D. src_format, dst_format |
| * are hints to the underlying component to initialize. |
| * |
| * Input parameters: |
| * mode - Mode (sync/async) in which App wants lib2d to run. |
| * src_format - source surface format |
| * dst_format - Destination surface format |
| * my_obj - handle that will be returned on succesful Init. App has to |
| * call other lib2d functions by passing this handle. |
| * |
| * Return values: |
| * MM_LIB2D_SUCCESS |
| * MM_LIB2D_ERR_MEMORY |
| * MM_LIB2D_ERR_BAD_PARAM |
| * MM_LIB2D_ERR_GENERAL |
| * |
| * Notes: none |
| **/ |
| lib2d_error mm_lib2d_init(lib2d_mode mode, cam_format_t src_format, |
| cam_format_t dst_format, void **my_obj) |
| { |
| int32_t rc = IMG_SUCCESS; |
| mm_lib2d_obj *lib2d_obj = NULL; |
| img_core_ops_t *p_core_ops = NULL; |
| img_component_ops_t *p_comp = NULL; |
| |
| if (my_obj == NULL) { |
| return MM_LIB2D_ERR_BAD_PARAM; |
| } |
| |
| // validate src_format, dst_format to check whether we support these. |
| // Currently support NV21 to ARGB conversions only. Others not tested. |
| if ((src_format != CAM_FORMAT_YUV_420_NV21) || |
| (dst_format != CAM_FORMAT_8888_ARGB)) { |
| LOGE("Formats conversion from %d to %d not supported", |
| src_format, dst_format); |
| } |
| |
| lib2d_obj = malloc(sizeof(mm_lib2d_obj)); |
| if (lib2d_obj == NULL) { |
| return MM_LIB2D_ERR_MEMORY; |
| } |
| |
| // Open libmmcamera_imglib |
| lib2d_obj->img_lib.ptr = dlopen("libmmcamera_imglib.so", RTLD_NOW); |
| if (!lib2d_obj->img_lib.ptr) { |
| LOGE("ERROR: couldn't dlopen libmmcamera_imglib.so: %s", |
| dlerror()); |
| goto FREE_LIB2D_OBJ; |
| } |
| |
| /* Get function pointer for functions supported by C2D */ |
| *(void **)&lib2d_obj->img_lib.img_core_get_comp = |
| dlsym(lib2d_obj->img_lib.ptr, "img_core_get_comp"); |
| *(void **)&lib2d_obj->img_lib.img_wait_for_completion = |
| dlsym(lib2d_obj->img_lib.ptr, "img_wait_for_completion"); |
| |
| /* Validate function pointers */ |
| if ((lib2d_obj->img_lib.img_core_get_comp == NULL) || |
| (lib2d_obj->img_lib.img_wait_for_completion == NULL)) { |
| LOGE(" ERROR mapping symbols from libc2d2.so"); |
| goto FREE_LIB2D_OBJ; |
| } |
| |
| p_core_ops = &lib2d_obj->core_ops; |
| p_comp = &lib2d_obj->comp; |
| |
| pthread_mutex_init(&lib2d_obj->mutex, NULL); |
| pthread_cond_init(&lib2d_obj->cond, NULL); |
| |
| rc = lib2d_obj->img_lib.img_core_get_comp(IMG_COMP_LIB2D, |
| "qti.lib2d", p_core_ops); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto FREE_LIB2D_OBJ; |
| } |
| |
| rc = IMG_COMP_LOAD(p_core_ops, NULL); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto FREE_LIB2D_OBJ; |
| } |
| |
| rc = IMG_COMP_CREATE(p_core_ops, p_comp); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto COMP_UNLOAD; |
| } |
| |
| rc = IMG_COMP_INIT(p_comp, (void *)lib2d_obj, lib2d_callback_handler); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto COMP_UNLOAD; |
| } |
| |
| rc = IMG_COMP_SET_CB(p_comp, lib2d_event_handler); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto COMP_DEINIT; |
| } |
| |
| lib2d_obj->lib2d_mode = mode; |
| img_comp_mode_t comp_mode; |
| if (lib2d_obj->lib2d_mode == MM_LIB2D_SYNC_MODE) { |
| comp_mode = IMG_SYNC_MODE; |
| } else { |
| comp_mode = IMG_ASYNC_MODE; |
| } |
| |
| // Set source format |
| rc = IMG_COMP_SET_PARAM(p_comp, QLIB2D_SOURCE_FORMAT, (void *)&src_format); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto COMP_DEINIT; |
| } |
| |
| // Set destination format |
| rc = IMG_COMP_SET_PARAM(p_comp, QLIB2D_DESTINATION_FORMAT, |
| (void *)&dst_format); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto COMP_DEINIT; |
| } |
| |
| // Try setting the required mode. |
| rc = IMG_COMP_SET_PARAM(p_comp, QIMG_PARAM_MODE, (void *)&comp_mode); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto COMP_DEINIT; |
| } |
| |
| // Get the mode to make sure whether the component is really running |
| // in the mode what we set. |
| rc = IMG_COMP_GET_PARAM(p_comp, QIMG_PARAM_MODE, |
| (void *)&lib2d_obj->comp_mode); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto COMP_DEINIT; |
| } |
| |
| if (comp_mode != lib2d_obj->comp_mode) { |
| LOGD("Component is running in %d mode", |
| lib2d_obj->comp_mode); |
| } |
| |
| *my_obj = (void *)lib2d_obj; |
| |
| return MM_LIB2D_SUCCESS; |
| |
| COMP_DEINIT : |
| rc = IMG_COMP_DEINIT(p_comp); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| return MM_LIB2D_ERR_GENERAL; |
| } |
| |
| COMP_UNLOAD : |
| rc = IMG_COMP_UNLOAD(p_core_ops); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| return MM_LIB2D_ERR_GENERAL; |
| } |
| |
| FREE_LIB2D_OBJ : |
| free(lib2d_obj); |
| return MM_LIB2D_ERR_GENERAL; |
| } |
| |
| /** |
| * Function: mm_lib2d_deinit |
| * |
| * Description: De-Initialization function for Lib2D |
| * |
| * Input parameters: |
| * lib2d_obj_handle - handle tto the lib2d object |
| * |
| * Return values: |
| * MM_LIB2D_SUCCESS |
| * MM_LIB2D_ERR_GENERAL |
| * |
| * Notes: none |
| **/ |
| lib2d_error mm_lib2d_deinit(void *lib2d_obj_handle) |
| { |
| mm_lib2d_obj *lib2d_obj = (mm_lib2d_obj *)lib2d_obj_handle; |
| int rc = IMG_SUCCESS; |
| img_core_ops_t *p_core_ops = &lib2d_obj->core_ops; |
| img_component_ops_t *p_comp = &lib2d_obj->comp; |
| |
| rc = IMG_COMP_DEINIT(p_comp); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| return MM_LIB2D_ERR_GENERAL; |
| } |
| |
| rc = IMG_COMP_UNLOAD(p_core_ops); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| return MM_LIB2D_ERR_GENERAL; |
| } |
| |
| dlclose(lib2d_obj->img_lib.ptr); |
| free(lib2d_obj); |
| |
| return MM_LIB2D_SUCCESS; |
| } |
| |
| /** |
| * Function: mm_lib2d_start_job |
| * |
| * Description: Start executing the job |
| * |
| * Input parameters: |
| * lib2d_obj_handle - handle tto the lib2d object |
| * src_buffer - pointer to the source buffer |
| * dst_buffer - pointer to the destination buffer |
| * jobid - job id of this request |
| * userdata - userdata that will be pass through callback function |
| * cb - callback function that will be called on completion of this job |
| * |
| * Return values: |
| * MM_LIB2D_SUCCESS |
| * MM_LIB2D_ERR_MEMORY |
| * MM_LIB2D_ERR_GENERAL |
| * |
| * Notes: none |
| **/ |
| lib2d_error mm_lib2d_start_job(void *lib2d_obj_handle, |
| mm_lib2d_buffer* src_buffer, mm_lib2d_buffer* dst_buffer, |
| int jobid, void *userdata, lib2d_client_cb cb) |
| { |
| mm_lib2d_obj *lib2d_obj = (mm_lib2d_obj *)lib2d_obj_handle; |
| int rc = IMG_SUCCESS; |
| img_core_ops_t *p_core_ops = &lib2d_obj->core_ops; |
| img_component_ops_t *p_comp = &lib2d_obj->comp; |
| |
| img_frame_t *p_in_frame = malloc(sizeof(img_frame_t)); |
| if (p_in_frame == NULL) { |
| return MM_LIB2D_ERR_MEMORY; |
| } |
| |
| img_frame_t *p_out_frame = malloc(sizeof(img_frame_t)); |
| if (p_out_frame == NULL) { |
| free(p_in_frame); |
| return MM_LIB2D_ERR_MEMORY; |
| } |
| |
| lib2d_job_private_info *p_job_info = malloc(sizeof(lib2d_job_private_info)); |
| if (p_out_frame == NULL) { |
| free(p_in_frame); |
| free(p_out_frame); |
| return MM_LIB2D_ERR_MEMORY; |
| } |
| |
| memset(p_in_frame, 0x0, sizeof(img_frame_t)); |
| memset(p_out_frame, 0x0, sizeof(img_frame_t)); |
| memset(p_job_info, 0x0, sizeof(lib2d_job_private_info)); |
| |
| // Fill up job info private data structure that can be used in callback to |
| // inform back to the client. |
| p_job_info->jobid = jobid; |
| p_job_info->userdata = userdata; |
| p_job_info->lib2d_client_cb = cb; |
| |
| p_in_frame->private_data = (void *)p_job_info; |
| p_out_frame->private_data = (void *)p_job_info; |
| |
| // convert the input info into component understandble data structures |
| |
| // Prepare Input, output frames |
| lib2d_fill_img_frame(p_in_frame, src_buffer, jobid); |
| lib2d_fill_img_frame(p_out_frame, dst_buffer, jobid); |
| |
| // call set_param to set the source, destination formats |
| |
| rc = IMG_COMP_Q_BUF(p_comp, p_in_frame, IMG_IN); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto ERROR; |
| } |
| |
| rc = IMG_COMP_Q_BUF(p_comp, p_out_frame, IMG_OUT); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto ERROR; |
| } |
| |
| rc = IMG_COMP_START(p_comp, NULL); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto ERROR; |
| } |
| |
| if (lib2d_obj->lib2d_mode == MM_LIB2D_SYNC_MODE) { |
| if (lib2d_obj->comp_mode == IMG_ASYNC_MODE) { |
| LOGD("before wait rc %d", rc); |
| rc = lib2d_obj->img_lib.img_wait_for_completion(&lib2d_obj->cond, |
| &lib2d_obj->mutex, 10000); |
| if (rc != IMG_SUCCESS) { |
| LOGE("rc %d", rc); |
| goto ERROR; |
| } |
| } |
| } |
| |
| rc = IMG_COMP_ABORT(p_comp, NULL); |
| if (IMG_ERROR(rc)) { |
| LOGE("comp abort failed %d", rc); |
| return rc; |
| } |
| |
| return MM_LIB2D_SUCCESS; |
| ERROR: |
| free(p_in_frame); |
| free(p_out_frame); |
| free(p_job_info); |
| |
| return MM_LIB2D_ERR_GENERAL; |
| } |
| |