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