diff options
Diffstat (limited to 'camera/exynos_camera.c')
-rw-r--r-- | camera/exynos_camera.c | 2865 |
1 files changed, 2865 insertions, 0 deletions
diff --git a/camera/exynos_camera.c b/camera/exynos_camera.c new file mode 100644 index 0000000..c236c2e --- /dev/null +++ b/camera/exynos_camera.c @@ -0,0 +1,2865 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * Based on crespo libcamera and exynos4 hal libcamera: + * Copyright 2008, The Android Open Source Project + * Copyright 2010, Samsung Electronics Co. LTD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <time.h> +#include <errno.h> +#include <malloc.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <sys/ioctl.h> + +#include <asm/types.h> +#include <jpeg_api.h> + +#define LOG_TAG "exynos_camera" +#include <utils/Log.h> +#include <utils/Timers.h> + +#include "exynos_camera.h" + +/* + * Devices configurations + */ + +struct exynos_camera_preset exynos_camera_presets_galaxynote[] = { + { + .name = "M5MO", + .facing = CAMERA_FACING_BACK, + .orientation = 90, + .rotation = 0, + .hflip = 0, + .vflip = 0, + .picture_format = V4L2_PIX_FMT_JPEG, + .focal_length = 4.03f, + .horizontal_view_angle = 60.5f, + .vertical_view_angle = 47.1f, + .metering = METERING_CENTER, + .params = { + .preview_size_values = "1280x720,640x480,720x480,800x480,800x450,352x288,320x240,176x144", + .preview_size = "640x480", + .preview_format_values = "yuv420sp,yuv420p,rgb565", + .preview_format = "yuv420sp", + .preview_frame_rate_values = "30,25,20,15,10,7", + .preview_frame_rate = 30, + .preview_fps_range_values = "(7000,30000)", + .preview_fps_range = "7000,30000", + + .picture_size_values = "3264x2448,3264x1968,2048x1536,2048x1232,1280x960,800x480,640x480", + .picture_size = "3264x2448", + .picture_format_values = "jpeg", + .picture_format = "jpeg", + .jpeg_thumbnail_size_values = "320x240,400x240,0x0", + .jpeg_thumbnail_width = 320, + .jpeg_thumbnail_height = 240, + .jpeg_thumbnail_quality = 100, + .jpeg_quality = 90, + + .recording_size = "720x480", + .recording_size_values = "1920x1080,1280x720,720x480,640x480", + .recording_format = "yuv420sp", + + .focus_mode = "auto", + .focus_mode_values = "auto,infinity,macro,fixed,facedetect,continuous-video", + .focus_distances = "0.15,1.20,Infinity", + .focus_areas = "(0,0,0,0,0)", + .max_num_focus_areas = 1, + + .zoom_supported = 1, + .smooth_zoom_supported = 0, + .zoom_ratios = "100,102,104,109,111,113,119,121,124,131,134,138,146,150,155,159,165,170,182,189,200,213,222,232,243,255,283,300,319,364,400", + .zoom = 0, + .max_zoom = 30, + + .flash_mode = "off", + .flash_mode_values = "off,auto,on,torch", + + .exposure_compensation = 0, + .exposure_compensation_step = 0.5, + .min_exposure_compensation = -4, + .max_exposure_compensation = 4, + + .whitebalance = "auto", + .whitebalance_values = "auto,incandescent,fluorescent,daylight,cloudy-daylight", + + .scene_mode = "auto", + .scene_mode_values = "auto,portrait,landscape,night,beach,snow,sunset,fireworks,sports,party,candlelight,dusk-dawn,fall-color,back-light,text", + + .effect = "none", + .effect_values = "none,mono,negative,sepia,aqua", + + .iso = "auto", + .iso_values = "auto,ISO50,ISO100,ISO200,ISO400,ISO800", + }, + }, + { + .name = "S5K5BAFX", + .facing = CAMERA_FACING_FRONT, + .orientation = 270, + .rotation = 0, + .hflip = 0, + .vflip = 0, + .picture_format = V4L2_PIX_FMT_YUYV, + .focal_length = 2.73f, + .horizontal_view_angle = 51.2f, + .vertical_view_angle = 39.4f, + .metering = METERING_CENTER, + .params = { + .preview_size_values = "640x480,352x288,320x240,176x144", + .preview_size = "640x480", + .preview_format_values = "yuv420sp,yuv420p,rgb565", + .preview_format = "yuv420sp", + .preview_frame_rate_values = "30,25,20,15,10,7", + .preview_frame_rate = 30, + .preview_fps_range_values = "(7000,30000)", + .preview_fps_range = "7000,30000", + + .picture_size_values = "1600x1200,640x480", + .picture_size = "1600x1200", + .picture_format_values = "jpeg", + .picture_format = "jpeg", + .jpeg_thumbnail_size_values = "160x120,0x0", + .jpeg_thumbnail_width = 160, + .jpeg_thumbnail_height = 120, + .jpeg_thumbnail_quality = 100, + .jpeg_quality = 90, + + .recording_size = "640x480", + .recording_size_values = "640x480", + .recording_format = "yuv420sp", + + .focus_mode = "fixed", + .focus_mode_values = "fixed", + .focus_distances = "0.20,0.25,Infinity", + .focus_areas = NULL, + .max_num_focus_areas = 0, + + .zoom_supported = 0, + + .flash_mode = NULL, + .flash_mode_values = NULL, + + .exposure_compensation = 0, + .exposure_compensation_step = 0.5, + .min_exposure_compensation = -4, + .max_exposure_compensation = 4, + + .whitebalance = NULL, + .whitebalance_values = NULL, + + .scene_mode = NULL, + .scene_mode_values = NULL, + + .effect = NULL, + .effect_values = NULL, + + .iso = "auto", + .iso_values = "auto", + }, + }, +}; + +struct exynos_v4l2_node exynos_v4l2_nodes_galaxynote[] = { + { + .id = 0, + .node = "/dev/video0", + }, + { + .id = 1, + .node = "/dev/video1", + }, + { + .id = 2, + .node = "/dev/video2", + }, +}; + +struct exynox_camera_config exynos_camera_config_galaxynote = { + .presets = (struct exynos_camera_preset *) &exynos_camera_presets_galaxynote, + .presets_count = 2, + .v4l2_nodes = (struct exynos_v4l2_node *) &exynos_v4l2_nodes_galaxynote, + .v4l2_nodes_count = 3, +}; + +/* + * Exynos Camera + */ + +struct exynox_camera_config *exynos_camera_config = + &exynos_camera_config_galaxynote; + +int exynos_camera_init(struct exynos_camera *exynos_camera, int id) +{ + char firmware_version[7] = { 0 }; + struct exynos_v4l2_ext_control control; + int rc; + + if (exynos_camera == NULL || id >= exynos_camera->config->presets_count) + return -EINVAL; + + // Init FIMC1 + rc = exynos_v4l2_open(exynos_camera, 0); + if (rc < 0) { + LOGE("Unable to open v4l2 device"); + return -1; + } + + rc = exynos_v4l2_querycap_cap(exynos_camera, 0); + if (rc < 0) { + LOGE("%s: querycap failed", __func__); + return -1; + } + + rc = exynos_v4l2_enum_input(exynos_camera, 0, id); + if (rc < 0) { + LOGE("%s: enum input failed", __func__); + return -1; + } + + rc = exynos_v4l2_s_input(exynos_camera, 0, id); + if (rc < 0) { + LOGE("%s: s input failed", __func__); + return -1; + } + + // Init FIMC2 + rc = exynos_v4l2_open(exynos_camera, 2); + if (rc < 0) { + LOGE("Unable to open v4l2 device"); + return -1; + } + + rc = exynos_v4l2_querycap_cap(exynos_camera, 2); + if (rc < 0) { + LOGE("%s: querycap failed", __func__); + return -1; + } + + rc = exynos_v4l2_enum_input(exynos_camera, 2, id); + if (rc < 0) { + LOGE("%s: enum input failed", __func__); + return -1; + } + + rc = exynos_v4l2_s_input(exynos_camera, 2, id); + if (rc < 0) { + LOGE("%s: s input failed", __func__); + return -1; + } + + // Get firmware information + memset(&control, 0, sizeof(control)); + control.id = V4L2_CID_CAM_SENSOR_FW_VER; + control.data.string = firmware_version; + + rc = exynos_v4l2_g_ext_ctrls(exynos_camera, 0, (struct v4l2_ext_control *) &control, 1); + if (rc < 0) { + LOGE("%s: g ext ctrls failed", __func__); + } else { + LOGD("Firmware version: %s", firmware_version); + } + + // Params + rc = exynos_camera_params_init(exynos_camera, id); + if (rc < 0) + LOGE("%s: Unable to init params", __func__); + + // Gralloc + rc = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, (const struct hw_module_t **) &exynos_camera->gralloc); + if (rc) + LOGE("%s: Unable to get gralloc module", __func__); + + return 0; +} + +void exynos_camera_deinit(struct exynos_camera *exynos_camera) +{ + int i; + int id; + + if (exynos_camera == NULL || exynos_camera->config == NULL) + return; + + exynos_v4l2_close(exynos_camera, 0); + exynos_v4l2_close(exynos_camera, 2); +} + +// Params + +int exynos_camera_params_init(struct exynos_camera *exynos_camera, int id) +{ + int rc; + + if (exynos_camera == NULL || id >= exynos_camera->config->presets_count) + return -EINVAL; + + // Camera params + exynos_camera->camera_rotation = exynos_camera->config->presets[id].rotation; + exynos_camera->camera_hflip = exynos_camera->config->presets[id].hflip; + exynos_camera->camera_vflip = exynos_camera->config->presets[id].vflip; + exynos_camera->camera_picture_format = exynos_camera->config->presets[id].picture_format; + exynos_camera->camera_focal_length = (int) (exynos_camera->config->presets[id].focal_length * 100); + exynos_camera->camera_metering = exynos_camera->config->presets[id].metering; + + // Recording preview + exynos_param_string_set(exynos_camera, "preferred-preview-size-for-video", + exynos_camera->config->presets[id].params.preview_size); + + // Preview + exynos_param_string_set(exynos_camera, "preview-size-values", + exynos_camera->config->presets[id].params.preview_size_values); + exynos_param_string_set(exynos_camera, "preview-size", + exynos_camera->config->presets[id].params.preview_size); + exynos_param_string_set(exynos_camera, "preview-format-values", + exynos_camera->config->presets[id].params.preview_format_values); + exynos_param_string_set(exynos_camera, "preview-format", + exynos_camera->config->presets[id].params.preview_format); + exynos_param_string_set(exynos_camera, "preview-frame-rate-values", + exynos_camera->config->presets[id].params.preview_frame_rate_values); + exynos_param_int_set(exynos_camera, "preview-frame-rate", + exynos_camera->config->presets[id].params.preview_frame_rate); + exynos_param_string_set(exynos_camera, "preview-fps-range-values", + exynos_camera->config->presets[id].params.preview_fps_range_values); + exynos_param_string_set(exynos_camera, "preview-fps-range", + exynos_camera->config->presets[id].params.preview_fps_range); + + // Picture + exynos_param_string_set(exynos_camera, "picture-size-values", + exynos_camera->config->presets[id].params.picture_size_values); + exynos_param_string_set(exynos_camera, "picture-size", + exynos_camera->config->presets[id].params.picture_size); + exynos_param_string_set(exynos_camera, "picture-format-values", + exynos_camera->config->presets[id].params.picture_format_values); + exynos_param_string_set(exynos_camera, "picture-format", + exynos_camera->config->presets[id].params.picture_format); + exynos_param_string_set(exynos_camera, "jpeg-thumbnail-size-values", + exynos_camera->config->presets[id].params.jpeg_thumbnail_size_values); + exynos_param_int_set(exynos_camera, "jpeg-thumbnail-width", + exynos_camera->config->presets[id].params.jpeg_thumbnail_width); + exynos_param_int_set(exynos_camera, "jpeg-thumbnail-height", + exynos_camera->config->presets[id].params.jpeg_thumbnail_height); + exynos_param_int_set(exynos_camera, "jpeg-thumbnail-quality", + exynos_camera->config->presets[id].params.jpeg_thumbnail_quality); + exynos_param_int_set(exynos_camera, "jpeg-quality", + exynos_camera->config->presets[id].params.jpeg_quality); + + // Recording + exynos_param_string_set(exynos_camera, "video-size", + exynos_camera->config->presets[id].params.recording_size); + exynos_param_string_set(exynos_camera, "video-size-values", + exynos_camera->config->presets[id].params.recording_size_values); + exynos_param_string_set(exynos_camera, "video-frame-format", + exynos_camera->config->presets[id].params.recording_format); + + // Focus + exynos_param_string_set(exynos_camera, "focus-mode", + exynos_camera->config->presets[id].params.focus_mode); + exynos_param_string_set(exynos_camera, "focus-mode-values", + exynos_camera->config->presets[id].params.focus_mode_values); + exynos_param_string_set(exynos_camera, "focus-distances", + exynos_camera->config->presets[id].params.focus_distances); + if (exynos_camera->config->presets[id].params.max_num_focus_areas > 0) { + exynos_param_string_set(exynos_camera, "focus-areas", + exynos_camera->config->presets[id].params.focus_areas); + exynos_param_int_set(exynos_camera, "max-num-focus-areas", + exynos_camera->config->presets[id].params.max_num_focus_areas); + } + + // Zoom + if (exynos_camera->config->presets[id].params.zoom_supported == 1) { + exynos_param_string_set(exynos_camera, "zoom-supported", "true"); + + if (exynos_camera->config->presets[id].params.smooth_zoom_supported == 1) + exynos_param_string_set(exynos_camera, "smooth-zoom-supported", "true"); + + if (exynos_camera->config->presets[id].params.zoom_ratios != NULL) + exynos_param_string_set(exynos_camera, "zoom-ratios", exynos_camera->config->presets[id].params.zoom_ratios); + + exynos_param_int_set(exynos_camera, "zoom", exynos_camera->config->presets[id].params.zoom); + exynos_param_int_set(exynos_camera, "max-zoom", exynos_camera->config->presets[id].params.max_zoom); + + } else { + exynos_param_string_set(exynos_camera, "zoom-supported", "false"); + } + + // Flash + exynos_param_string_set(exynos_camera, "flash-mode", + exynos_camera->config->presets[id].params.flash_mode); + exynos_param_string_set(exynos_camera, "flash-mode-values", + exynos_camera->config->presets[id].params.flash_mode_values); + + // Exposure + exynos_param_int_set(exynos_camera, "exposure-compensation", + exynos_camera->config->presets[id].params.exposure_compensation); + exynos_param_float_set(exynos_camera, "exposure-compensation-step", + exynos_camera->config->presets[id].params.exposure_compensation_step); + exynos_param_int_set(exynos_camera, "min-exposure-compensation", + exynos_camera->config->presets[id].params.min_exposure_compensation); + exynos_param_int_set(exynos_camera, "max-exposure-compensation", + exynos_camera->config->presets[id].params.max_exposure_compensation); + + // WB + exynos_param_string_set(exynos_camera, "whitebalance", + exynos_camera->config->presets[id].params.whitebalance); + exynos_param_string_set(exynos_camera, "whitebalance-values", + exynos_camera->config->presets[id].params.whitebalance_values); + + // Scene mode + exynos_param_string_set(exynos_camera, "scene-mode", + exynos_camera->config->presets[id].params.scene_mode); + exynos_param_string_set(exynos_camera, "scene-mode-values", + exynos_camera->config->presets[id].params.scene_mode_values); + + // Effect + exynos_param_string_set(exynos_camera, "effect", + exynos_camera->config->presets[id].params.effect); + exynos_param_string_set(exynos_camera, "effect-values", + exynos_camera->config->presets[id].params.effect_values); + + // ISO + exynos_param_string_set(exynos_camera, "iso", + exynos_camera->config->presets[id].params.iso); + exynos_param_string_set(exynos_camera, "iso-values", + exynos_camera->config->presets[id].params.iso_values); + + // Camera + exynos_param_float_set(exynos_camera, "focal-length", + exynos_camera->config->presets[id].focal_length); + exynos_param_float_set(exynos_camera, "horizontal-view-angle", + exynos_camera->config->presets[id].horizontal_view_angle); + exynos_param_float_set(exynos_camera, "vertical-view-angle", + exynos_camera->config->presets[id].vertical_view_angle); + + rc = exynos_camera_params_apply(exynos_camera); + if (rc < 0) { + LOGE("%s: Unable to apply params", __func__); + return -1; + } + + return 0; +} + +int exynos_camera_params_apply(struct exynos_camera *exynos_camera) +{ + char *recording_hint_string; + char *recording_preview_size_string; + + char *preview_size_string; + int preview_width = 0; + int preview_height = 0; + char *preview_format_string; + int preview_format; + float preview_format_bpp; + int preview_fps; + + char *picture_size_string; + int picture_width = 0; + int picture_height = 0; + char *picture_format_string; + int picture_format; + + int jpeg_thumbnail_width; + int jpeg_thumbnail_height; + int jpeg_thumbnail_quality; + int jpeg_quality; + + char *video_size_string; + int recording_width = 0; + int recording_height = 0; + char *video_frame_format_string; + int recording_format; + int camera_sensor_mode; + int camera_sensor_output_size; + + char *focus_mode_string; + int focus_mode = 0; + char *focus_areas_string; + int focus_left, focus_top, focus_right, focus_bottom, focus_weigth; + int focus_x; + int focus_y; + + char *zoom_supported_string; + int zoom, max_zoom; + + char *flash_mode_string; + int flash_mode; + + int exposure_compensation; + int min_exposure_compensation; + int max_exposure_compensation; + + char *whitebalance_string; + int whitebalance; + + char *scene_mode_string; + int scene_mode; + + char *effect_string; + int effect; + + char *iso_string; + int iso; + + int force = 0; + + int w, h; + char *k; + int rc; + + if (exynos_camera == NULL) + return -EINVAL; + + if (!exynos_camera->preview_params_set) { + LOGE("%s: Setting preview params", __func__); + exynos_camera->preview_params_set = 1; + force = 1; + } + + // Preview + preview_size_string = exynos_param_string_get(exynos_camera, "preview-size"); + if (preview_size_string != NULL) { + sscanf(preview_size_string, "%dx%d", &preview_width, &preview_height); + + if (preview_width != 0 && preview_width != exynos_camera->preview_width) + exynos_camera->preview_width = preview_width; + if (preview_height != 0 && preview_height != exynos_camera->preview_height) + exynos_camera->preview_height = preview_height; + } + + preview_format_string = exynos_param_string_get(exynos_camera, "preview-format"); + if (preview_format_string != NULL) { + if (strcmp(preview_format_string, "yuv420sp") == 0) { + preview_format = V4L2_PIX_FMT_NV21; + preview_format_bpp = 1.5f; + } else if (strcmp(preview_format_string, "yuv420p") == 0) { + preview_format = V4L2_PIX_FMT_YUV420; + preview_format_bpp = 1.5f; + } else if (strcmp(preview_format_string, "rgb565") == 0) { + preview_format = V4L2_PIX_FMT_RGB565; + preview_format_bpp = 2.0f; + } else if (strcmp(preview_format_string, "rgb8888") == 0) { + preview_format = V4L2_PIX_FMT_RGB32; + preview_format_bpp = 4.0f; + } else { + LOGE("%s: Unsupported preview format: %s", __func__, preview_format_string); + preview_format = V4L2_PIX_FMT_NV21; + preview_format_bpp = 1.5f; + } + + if (preview_format != exynos_camera->preview_format) { + exynos_camera->preview_format = preview_format; + exynos_camera->preview_format_bpp = preview_format_bpp; + } + } + + preview_fps = exynos_param_int_get(exynos_camera, "preview-frame-rate"); + if (preview_fps > 0) + exynos_camera->preview_fps = preview_fps; + else + exynos_camera->preview_fps = 0; + + // Picture + picture_size_string = exynos_param_string_get(exynos_camera, "picture-size"); + if (picture_size_string != NULL) { + sscanf(picture_size_string, "%dx%d", &picture_width, &picture_height); + + if (picture_width != 0 && picture_width != exynos_camera->picture_width) + exynos_camera->picture_width = picture_width; + if (picture_height != 0 && picture_height != exynos_camera->picture_height) + exynos_camera->picture_height = picture_height; + } + + picture_format_string = exynos_param_string_get(exynos_camera, "picture-format"); + if (picture_format_string != NULL) { + if (strcmp(picture_format_string, "jpeg") == 0) { + picture_format = V4L2_PIX_FMT_JPEG; + } else { + LOGE("%s: Unsupported picture format: %s", __func__, picture_format_string); + picture_format = V4L2_PIX_FMT_JPEG; + } + + if (picture_format != exynos_camera->picture_format) + exynos_camera->picture_format = picture_format; + } + + jpeg_thumbnail_width = exynos_param_int_get(exynos_camera, "jpeg-thumbnail-width"); + if (jpeg_thumbnail_width > 0) + exynos_camera->jpeg_thumbnail_width = jpeg_thumbnail_width; + + jpeg_thumbnail_height = exynos_param_int_get(exynos_camera, "jpeg-thumbnail-height"); + if (jpeg_thumbnail_height > 0) + exynos_camera->jpeg_thumbnail_height = jpeg_thumbnail_height; + + jpeg_thumbnail_quality = exynos_param_int_get(exynos_camera, "jpeg-thumbnail-quality"); + if (jpeg_thumbnail_quality > 0) + exynos_camera->jpeg_thumbnail_quality = jpeg_thumbnail_quality; + + jpeg_quality = exynos_param_int_get(exynos_camera, "jpeg-quality"); + if (jpeg_quality <= 100 && jpeg_quality >= 0 && (jpeg_quality != exynos_camera->jpeg_quality || force)) { + exynos_camera->jpeg_quality = jpeg_quality; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_QUALITY, jpeg_quality); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + // Recording + video_size_string = exynos_param_string_get(exynos_camera, "video-size"); + if (video_size_string == NULL) + video_size_string = exynos_param_string_get(exynos_camera, "preview-size"); + + if (video_size_string != NULL) { + sscanf(video_size_string, "%dx%d", &recording_width, &recording_height); + + if (recording_width != 0 && recording_width != exynos_camera->recording_width) + exynos_camera->recording_width = recording_width; + if (recording_height != 0 && recording_height != exynos_camera->recording_height) + exynos_camera->recording_height = recording_height; + } + + video_frame_format_string = exynos_param_string_get(exynos_camera, "video-frame-format"); + if (video_frame_format_string != NULL) { + if (strcmp(video_frame_format_string, "yuv420sp") == 0) { + recording_format = V4L2_PIX_FMT_NV12; + } else if (strcmp(video_frame_format_string, "yuv420p") == 0) { + recording_format = V4L2_PIX_FMT_YUV420; + } else if (strcmp(video_frame_format_string, "rgb565") == 0) { + recording_format = V4L2_PIX_FMT_RGB565; + } else if (strcmp(video_frame_format_string, "rgb8888") == 0) { + recording_format = V4L2_PIX_FMT_RGB32; + } else { + LOGE("%s: Unsupported recording format: %s", __func__, video_frame_format_string); + recording_format = V4L2_PIX_FMT_NV12; + } + + if (recording_format != exynos_camera->recording_format) + exynos_camera->recording_format = recording_format; + } + + recording_hint_string = exynos_param_string_get(exynos_camera, "recording-hint"); + if (recording_hint_string != NULL && strcmp(recording_hint_string, "true") == 0) { + camera_sensor_mode = SENSOR_MOVIE; + + k = exynos_param_string_get(exynos_camera, "preview-size-values"); + while (recording_width != 0 && recording_height != 0) { + if (k == NULL) + break; + + sscanf(k, "%dx%d", &w, &h); + + // Look for same aspect ratio + if ((recording_width * h) / recording_height == w) { + preview_width = w; + preview_height = h; + break; + } + + k = strchr(k, ','); + if (k == NULL) + break; + + k++; + } + + if (preview_width != 0 && preview_width != exynos_camera->preview_width) + exynos_camera->preview_width = preview_width; + if (preview_height != 0 && preview_height != exynos_camera->preview_height) + exynos_camera->preview_height = preview_height; + + camera_sensor_output_size = ((recording_width & 0xffff) << 16) | (recording_height & 0xffff); + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SENSOR_OUTPUT_SIZE, + camera_sensor_output_size); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } else { + camera_sensor_mode = SENSOR_CAMERA; + } + + // Switching modes + if (camera_sensor_mode != exynos_camera->camera_sensor_mode) { + exynos_camera->camera_sensor_mode = camera_sensor_mode; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SENSOR_MODE, camera_sensor_mode); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + // Focus + focus_areas_string = exynos_param_string_get(exynos_camera, "focus-areas"); + if (focus_areas_string != NULL) { + focus_left = focus_top = focus_right = focus_bottom = focus_weigth = 0; + + rc = sscanf(focus_areas_string, "(%d,%d,%d,%d,%d)", + &focus_left, &focus_top, &focus_right, &focus_bottom, &focus_weigth); + if (rc != 5) { + LOGE("%s: sscanf failed!", __func__); + } else if (focus_left != 0 && focus_top != 0 && focus_right != 0 && focus_bottom != 0) { + focus_x = (((focus_left + focus_right) / 2) + 1000) * preview_width / 2000; + focus_y = (((focus_top + focus_bottom) / 2) + 1000) * preview_height / 2000; + + if (focus_x != exynos_camera->focus_x || force) { + exynos_camera->focus_x = focus_x; + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_OBJECT_POSITION_X, focus_x); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + if (focus_y != exynos_camera->focus_y || force) { + exynos_camera->focus_y = focus_y; + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_OBJECT_POSITION_Y, focus_y); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + focus_mode = FOCUS_MODE_TOUCH; + } + } + + focus_mode_string = exynos_param_string_get(exynos_camera, "focus-mode"); + if (focus_mode_string != NULL) { + if (focus_mode == 0) { + if (strcmp(focus_mode_string, "auto") == 0) + focus_mode = FOCUS_MODE_AUTO; + else if (strcmp(focus_mode_string, "infinity") == 0) + focus_mode = FOCUS_MODE_INFINITY; + else if (strcmp(focus_mode_string, "macro") == 0) + focus_mode = FOCUS_MODE_MACRO; + else if (strcmp(focus_mode_string, "fixed") == 0) + focus_mode = FOCUS_MODE_FIXED; + else if (strcmp(focus_mode_string, "facedetect") == 0) + focus_mode = FOCUS_MODE_FACEDETECT; + else if (strcmp(focus_mode_string, "continuous-video") == 0) + focus_mode = FOCUS_MODE_CONTINOUS; + else if (strcmp(focus_mode_string, "continuous-picture") == 0) + focus_mode = FOCUS_MODE_CONTINOUS; + else + focus_mode = FOCUS_MODE_AUTO; + } + + if (focus_mode != exynos_camera->focus_mode || force) { + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_FOCUS_MODE, focus_mode); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + if (focus_mode == FOCUS_MODE_TOUCH) { + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_TOUCH_AF_START_STOP, 1); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } else if (exynos_camera->focus_mode == FOCUS_MODE_TOUCH) { + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_TOUCH_AF_START_STOP, 0); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + exynos_camera->focus_mode = focus_mode; + } + + // Zoom + zoom_supported_string = exynos_param_string_get(exynos_camera, "zoom-supported"); + if (zoom_supported_string != NULL && strcmp(zoom_supported_string, "true") == 0) { + zoom = exynos_param_int_get(exynos_camera, "zoom"); + max_zoom = exynos_param_int_get(exynos_camera, "max-zoom"); + if (zoom <= max_zoom && zoom >= 0 && (zoom != exynos_camera->zoom || force)) { + exynos_camera->zoom = zoom; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_ZOOM, zoom); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + } + + // Flash + flash_mode_string = exynos_param_string_get(exynos_camera, "flash-mode"); + if (flash_mode_string != NULL) { + if (strcmp(flash_mode_string, "off") == 0) + flash_mode = FLASH_MODE_OFF; + else if (strcmp(flash_mode_string, "auto") == 0) + flash_mode = FLASH_MODE_AUTO; + else if (strcmp(flash_mode_string, "on") == 0) + flash_mode = FLASH_MODE_ON; + else if (strcmp(flash_mode_string, "torch") == 0) + flash_mode = FLASH_MODE_TORCH; + else + flash_mode = FLASH_MODE_AUTO; + + if (flash_mode != exynos_camera->flash_mode || force) { + exynos_camera->flash_mode = flash_mode; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_FLASH_MODE, flash_mode); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + } + + // Exposure + exposure_compensation = exynos_param_int_get(exynos_camera, "exposure-compensation"); + min_exposure_compensation = exynos_param_int_get(exynos_camera, "min-exposure-compensation"); + max_exposure_compensation = exynos_param_int_get(exynos_camera, "max-exposure-compensation"); + + if (exposure_compensation <= max_exposure_compensation && exposure_compensation >= min_exposure_compensation && + (exposure_compensation != exynos_camera->exposure_compensation || force)) { + exynos_camera->exposure_compensation = exposure_compensation; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_BRIGHTNESS, exposure_compensation); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + + // WB + whitebalance_string = exynos_param_string_get(exynos_camera, "whitebalance"); + if (whitebalance_string != NULL) { + if (strcmp(whitebalance_string, "auto") == 0) + whitebalance = WHITE_BALANCE_AUTO; + else if (strcmp(whitebalance_string, "incandescent") == 0) + whitebalance = WHITE_BALANCE_TUNGSTEN; + else if (strcmp(whitebalance_string, "fluorescent") == 0) + whitebalance = WHITE_BALANCE_FLUORESCENT; + else if (strcmp(whitebalance_string, "daylight") == 0) + whitebalance = WHITE_BALANCE_SUNNY; + else if (strcmp(whitebalance_string, "cloudy-daylight") == 0) + whitebalance = WHITE_BALANCE_CLOUDY; + else + whitebalance = WHITE_BALANCE_AUTO; + + if (whitebalance != exynos_camera->whitebalance || force) { + exynos_camera->whitebalance = whitebalance; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_WHITE_BALANCE, whitebalance); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + } + + // Scene mode + scene_mode_string = exynos_param_string_get(exynos_camera, "scene-mode"); + if (scene_mode_string != NULL) { + if (strcmp(scene_mode_string, "auto") == 0) + scene_mode = SCENE_MODE_NONE; + else if (strcmp(scene_mode_string, "portrait") == 0) + scene_mode = SCENE_MODE_PORTRAIT; + else if (strcmp(scene_mode_string, "landscape") == 0) + scene_mode = SCENE_MODE_LANDSCAPE; + else if (strcmp(scene_mode_string, "night") == 0) + scene_mode = SCENE_MODE_NIGHTSHOT; + else if (strcmp(scene_mode_string, "beach") == 0) + scene_mode = SCENE_MODE_BEACH_SNOW; + else if (strcmp(scene_mode_string, "snow") == 0) + scene_mode = SCENE_MODE_BEACH_SNOW; + else if (strcmp(scene_mode_string, "sunset") == 0) + scene_mode = SCENE_MODE_SUNSET; + else if (strcmp(scene_mode_string, "fireworks") == 0) + scene_mode = SCENE_MODE_FIREWORKS; + else if (strcmp(scene_mode_string, "sports") == 0) + scene_mode = SCENE_MODE_SPORTS; + else if (strcmp(scene_mode_string, "party") == 0) + scene_mode = SCENE_MODE_PARTY_INDOOR; + else if (strcmp(scene_mode_string, "candlelight") == 0) + scene_mode = SCENE_MODE_CANDLE_LIGHT; + else if (strcmp(scene_mode_string, "dusk-dawn") == 0) + scene_mode = SCENE_MODE_DUSK_DAWN; + else if (strcmp(scene_mode_string, "fall-color") == 0) + scene_mode = SCENE_MODE_FALL_COLOR; + else if (strcmp(scene_mode_string, "back-light") == 0) + scene_mode = SCENE_MODE_BACK_LIGHT; + else if (strcmp(scene_mode_string, "text") == 0) + scene_mode = SCENE_MODE_TEXT; + else + scene_mode = SCENE_MODE_NONE; + + if (scene_mode != exynos_camera->scene_mode || force) { + exynos_camera->scene_mode = scene_mode; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SCENE_MODE, scene_mode); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + } + + // Effect + effect_string = exynos_param_string_get(exynos_camera, "effect"); + if (effect_string != NULL) { + if (strcmp(effect_string, "auto") == 0) + effect = IMAGE_EFFECT_NONE; + else if (strcmp(effect_string, "mono") == 0) + effect = IMAGE_EFFECT_BNW; + else if (strcmp(effect_string, "negative") == 0) + effect = IMAGE_EFFECT_NEGATIVE; + else if (strcmp(effect_string, "sepia") == 0) + effect = IMAGE_EFFECT_SEPIA; + else if (strcmp(effect_string, "aqua") == 0) + effect = IMAGE_EFFECT_AQUA; + else + effect = IMAGE_EFFECT_NONE; + + if (effect != exynos_camera->effect || force) { + exynos_camera->effect = effect; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_EFFECT, effect); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + } + + // ISO + iso_string = exynos_param_string_get(exynos_camera, "iso"); + if (iso_string != NULL) { + if (strcmp(iso_string, "auto") == 0) + iso = ISO_AUTO; + else if (strcmp(iso_string, "ISO50") == 0) + iso = ISO_50; + else if (strcmp(iso_string, "ISO100") == 0) + iso = ISO_100; + else if (strcmp(iso_string, "ISO200") == 0) + iso = ISO_200; + else if (strcmp(iso_string, "ISO400") == 0) + iso = ISO_400; + else if (strcmp(iso_string, "ISO800") == 0) + iso = ISO_800; + else + iso = ISO_AUTO; + + if (iso != exynos_camera->iso || force) { + exynos_camera->iso = iso; + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_ISO, iso); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + } + } + + LOGD("%s: Preview size: %dx%d, picture size: %dx%d, recording size: %dx%d", + __func__, preview_width, preview_height, picture_width, picture_height, + recording_width, recording_height); + + return 0; +} + +// Picture + +int exynos_camera_picture(struct exynos_camera *exynos_camera) +{ + camera_memory_t *data_memory = NULL; + camera_memory_t *exif_data_memory = NULL; + camera_memory_t *picture_data_memory = NULL; + camera_memory_t *jpeg_thumbnail_data_memory = NULL; + + int camera_picture_format; + int picture_width; + int picture_height; + int picture_format; + + int jpeg_thumbnail_width; + int jpeg_thumbnail_height; + int jpeg_thumbnail_quality; + int jpeg_quality; + + int data_size; + + int offset = 0; + void *picture_addr = NULL; + int picture_size = 0; + void *jpeg_thumbnail_addr = NULL; + int jpeg_thumbnail_size = 0; + + int jpeg_fd; + struct jpeg_enc_param jpeg_enc_params; + enum jpeg_frame_format jpeg_in_format; + enum jpeg_stream_format jpeg_out_format; + enum jpeg_ret_type jpeg_result; + void *jpeg_in_buffer; + int jpeg_in_size; + void *jpeg_out_buffer; + int jpeg_out_size; + + exif_attribute_t exif_attributes; + int exif_size = 0; + + int index; + int rc; + + if (exynos_camera == NULL) + return -EINVAL; + + picture_width = exynos_camera->picture_width; + picture_height = exynos_camera->picture_height; + picture_format = exynos_camera->picture_format; + camera_picture_format = exynos_camera->camera_picture_format; + jpeg_thumbnail_width = exynos_camera->jpeg_thumbnail_width; + jpeg_thumbnail_height = exynos_camera->jpeg_thumbnail_height; + jpeg_thumbnail_quality = exynos_camera->jpeg_thumbnail_quality; + jpeg_quality = exynos_camera->jpeg_quality; + + if (camera_picture_format == 0) + camera_picture_format = picture_format; + + // V4L2 + + rc = exynos_v4l2_poll(exynos_camera, 0); + if (rc < 0) { + LOGE("%s: poll failed!", __func__); + return -1; + } else if (rc == 0) { + LOGE("%s: poll timeout!", __func__); + return -1; + } + + rc = exynos_v4l2_streamoff_cap(exynos_camera, 0); + if (rc < 0) { + LOGE("%s: streamoff failed!", __func__); + return -1; + } + + index = exynos_v4l2_dqbuf_cap(exynos_camera, 0); + if (index < 0) { + LOGE("%s: dqbuf failed!", __func__); + return -1; + } + + // This assumes that the output format is JPEG + + if (camera_picture_format == V4L2_PIX_FMT_JPEG) { + rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_MAIN_SIZE, + &picture_size); + if (rc < 0) { + LOGE("%s: g ctrl failed!", __func__); + return -1; + } + + rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_MAIN_OFFSET, + &offset); + if (rc < 0) { + LOGE("%s: g ctrl failed!", __func__); + return -1; + } + + picture_addr = (void *) ((int) exynos_camera->picture_memory->data + offset); + + rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_THUMB_SIZE, + &jpeg_thumbnail_size); + if (rc < 0) { + LOGE("%s: g ctrl failed!", __func__); + return -1; + } + + rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_THUMB_OFFSET, + &offset); + if (rc < 0) { + LOGE("%s: g ctrl failed!", __func__); + return -1; + } + + jpeg_thumbnail_addr = (void *) ((int) exynos_camera->picture_memory->data + offset); + } + + // Thumbnail + + if (camera_picture_format == V4L2_PIX_FMT_JPEG && jpeg_thumbnail_addr != NULL && jpeg_thumbnail_size >= 0) { + if (exynos_camera->callbacks.request_memory != NULL) { + jpeg_thumbnail_data_memory = + exynos_camera->callbacks.request_memory(-1, + jpeg_thumbnail_size, 1, 0); + if (jpeg_thumbnail_data_memory == NULL) { + LOGE("%s: thumb memory request failed!", __func__); + goto error; + } + } else { + LOGE("%s: No memory request function!", __func__); + goto error; + } + + memcpy(jpeg_thumbnail_data_memory->data, jpeg_thumbnail_addr, jpeg_thumbnail_size); + } else { + jpeg_fd = api_jpeg_encode_init(); + if (jpeg_fd < 0) { + LOGE("%s: Failed to init JPEG", __func__); + goto error; + } + + switch (camera_picture_format) { + case V4L2_PIX_FMT_RGB565: + jpeg_in_format = RGB_565; + jpeg_out_format = JPEG_420; + jpeg_in_size = (picture_width * picture_height * 2); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12T: + case V4L2_PIX_FMT_YUV420: + jpeg_in_format = YUV_420; + jpeg_out_format = JPEG_420; + jpeg_in_size = (picture_width * picture_height * 1.5); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUV422P: + default: + jpeg_in_format = YUV_422; + jpeg_out_format = JPEG_422; + jpeg_in_size = (picture_width * picture_height * 2); + break; + } + + memset(&jpeg_enc_params, 0, sizeof(jpeg_enc_params)); + + jpeg_enc_params.width = jpeg_thumbnail_width; + jpeg_enc_params.height = jpeg_thumbnail_height; + jpeg_enc_params.in_fmt = jpeg_in_format; + jpeg_enc_params.out_fmt = jpeg_out_format; + + if (jpeg_thumbnail_quality >= 90) + jpeg_enc_params.quality = QUALITY_LEVEL_1; + else if (jpeg_thumbnail_quality >= 80) + jpeg_enc_params.quality = QUALITY_LEVEL_2; + else if (jpeg_thumbnail_quality >= 70) + jpeg_enc_params.quality = QUALITY_LEVEL_3; + else + jpeg_enc_params.quality = QUALITY_LEVEL_4; + + api_jpeg_set_encode_param(&jpeg_enc_params); + + jpeg_in_buffer = api_jpeg_get_encode_in_buf(jpeg_fd, jpeg_in_size); + if (jpeg_in_buffer == NULL) { + LOGE("%s: Failed to get JPEG in buffer", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + jpeg_out_buffer = api_jpeg_get_encode_out_buf(jpeg_fd); + if (jpeg_out_buffer == NULL) { + LOGE("%s: Failed to get JPEG out buffer", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + memcpy(jpeg_in_buffer, exynos_camera->picture_memory->data, jpeg_in_size); + + jpeg_result = api_jpeg_encode_exe(jpeg_fd, &jpeg_enc_params); + if (jpeg_result != JPEG_ENCODE_OK) { + LOGE("%s: Failed to encode JPEG", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + jpeg_out_size = jpeg_enc_params.size; + if (jpeg_out_size <= 0) { + LOGE("%s: Failed to get JPEG out size", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + if (exynos_camera->callbacks.request_memory != NULL) { + jpeg_thumbnail_data_memory = + exynos_camera->callbacks.request_memory(-1, + jpeg_out_size, 1, 0); + if (jpeg_thumbnail_data_memory == NULL) { + LOGE("%s: thumbnail memory request failed!", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + } else { + LOGE("%s: No memory request function!", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + memcpy(jpeg_thumbnail_data_memory->data, jpeg_out_buffer, jpeg_out_size); + jpeg_thumbnail_size = jpeg_out_size; + + api_jpeg_encode_deinit(jpeg_fd); + } + + // Picture + + if (camera_picture_format == V4L2_PIX_FMT_JPEG && picture_addr != NULL && picture_size >= 0) { + if (exynos_camera->callbacks.request_memory != NULL) { + picture_data_memory = + exynos_camera->callbacks.request_memory(-1, + picture_size, 1, 0); + if (picture_data_memory == NULL) { + LOGE("%s: picture memory request failed!", __func__); + goto error; + } + } else { + LOGE("%s: No memory request function!", __func__); + goto error; + } + + memcpy(picture_data_memory->data, picture_addr, picture_size); + } else { + jpeg_fd = api_jpeg_encode_init(); + if (jpeg_fd < 0) { + LOGE("%s: Failed to init JPEG", __func__); + goto error; + } + + switch (camera_picture_format) { + case V4L2_PIX_FMT_RGB565: + jpeg_in_format = RGB_565; + jpeg_out_format = JPEG_420; + jpeg_in_size = (picture_width * picture_height * 2); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12T: + case V4L2_PIX_FMT_YUV420: + jpeg_in_format = YUV_420; + jpeg_out_format = JPEG_420; + jpeg_in_size = (picture_width * picture_height * 1.5); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUV422P: + default: + jpeg_in_format = YUV_422; + jpeg_out_format = JPEG_422; + jpeg_in_size = (picture_width * picture_height * 2); + break; + } + + memset(&jpeg_enc_params, 0, sizeof(jpeg_enc_params)); + + jpeg_enc_params.width = picture_width; + jpeg_enc_params.height = picture_height; + jpeg_enc_params.in_fmt = jpeg_in_format; + jpeg_enc_params.out_fmt = jpeg_out_format; + + if (jpeg_quality >= 90) + jpeg_enc_params.quality = QUALITY_LEVEL_1; + else if (jpeg_quality >= 80) + jpeg_enc_params.quality = QUALITY_LEVEL_2; + else if (jpeg_quality >= 70) + jpeg_enc_params.quality = QUALITY_LEVEL_3; + else + jpeg_enc_params.quality = QUALITY_LEVEL_4; + + api_jpeg_set_encode_param(&jpeg_enc_params); + + jpeg_in_buffer = api_jpeg_get_encode_in_buf(jpeg_fd, jpeg_in_size); + if (jpeg_in_buffer == NULL) { + LOGE("%s: Failed to get JPEG in buffer", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + jpeg_out_buffer = api_jpeg_get_encode_out_buf(jpeg_fd); + if (jpeg_out_buffer == NULL) { + LOGE("%s: Failed to get JPEG out buffer", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + memcpy(jpeg_in_buffer, exynos_camera->picture_memory->data, jpeg_in_size); + + jpeg_result = api_jpeg_encode_exe(jpeg_fd, &jpeg_enc_params); + if (jpeg_result != JPEG_ENCODE_OK) { + LOGE("%s: Failed to encode JPEG", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + jpeg_out_size = jpeg_enc_params.size; + if (jpeg_out_size <= 0) { + LOGE("%s: Failed to get JPEG out size", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + if (exynos_camera->callbacks.request_memory != NULL) { + picture_data_memory = + exynos_camera->callbacks.request_memory(-1, + jpeg_out_size, 1, 0); + if (picture_data_memory == NULL) { + LOGE("%s: picture memory request failed!", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + } else { + LOGE("%s: No memory request function!", __func__); + api_jpeg_encode_deinit(jpeg_fd); + goto error; + } + + memcpy(picture_data_memory->data, jpeg_out_buffer, jpeg_out_size); + picture_size = jpeg_out_size; + + api_jpeg_encode_deinit(jpeg_fd); + } + + // EXIF + + memset(&exif_attributes, 0, sizeof(exif_attributes)); + exynos_exif_attributes_create_static(exynos_camera, &exif_attributes); + exynos_exif_attributes_create_params(exynos_camera, &exif_attributes); + + rc = exynos_exif_create(exynos_camera, &exif_attributes, + jpeg_thumbnail_data_memory, jpeg_thumbnail_size, + &exif_data_memory, &exif_size); + if (rc < 0 || exif_data_memory == NULL || exif_size <= 0) { + LOGE("%s: EXIF create failed!", __func__); + goto error; + } + + data_size = exif_size + picture_size; + + if (exynos_camera->callbacks.request_memory != NULL) { + data_memory = + exynos_camera->callbacks.request_memory(-1, + data_size, 1, 0); + if (data_memory == NULL) { + LOGE("%s: data memory request failed!", __func__); + goto error; + } + } else { + LOGE("%s: No memory request function!", __func__); + goto error; + } + + // Copy the first two bytes of the JPEG picture + memcpy(data_memory->data, picture_data_memory->data, 2); + + // Copy the EXIF data + memcpy((void *) ((int) data_memory->data + 2), exif_data_memory->data, + exif_size); + + // Copy the JPEG picture + memcpy((void *) ((int) data_memory->data + 2 + exif_size), + (void *) ((int) picture_data_memory->data + 2), picture_size - 2); + + // Callbacks + + if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_SHUTTER) && EXYNOS_CAMERA_CALLBACK_DEFINED(notify)) + exynos_camera->callbacks.notify(CAMERA_MSG_SHUTTER, 0, 0, + exynos_camera->callbacks.user); + + if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_RAW_IMAGE) && EXYNOS_CAMERA_CALLBACK_DEFINED(data) && + jpeg_thumbnail_data_memory != NULL) + exynos_camera->callbacks.data(CAMERA_MSG_RAW_IMAGE, + jpeg_thumbnail_data_memory, 0, NULL, exynos_camera->callbacks.user); + + if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_COMPRESSED_IMAGE) && EXYNOS_CAMERA_CALLBACK_DEFINED(data) && + data_memory != NULL) + exynos_camera->callbacks.data(CAMERA_MSG_COMPRESSED_IMAGE, + data_memory, 0, NULL, exynos_camera->callbacks.user); + + // Release memory + + if (jpeg_thumbnail_data_memory != NULL && jpeg_thumbnail_data_memory->release != NULL) + jpeg_thumbnail_data_memory->release(jpeg_thumbnail_data_memory); + + if (picture_data_memory != NULL && picture_data_memory->release != NULL) + picture_data_memory->release(picture_data_memory); + + if (exif_data_memory != NULL && exif_data_memory->release != NULL) + exif_data_memory->release(exif_data_memory); + + if (data_memory != NULL && data_memory->release != NULL) + data_memory->release(data_memory); + + return 0; + +error: + if (jpeg_thumbnail_data_memory != NULL && jpeg_thumbnail_data_memory->release != NULL) + jpeg_thumbnail_data_memory->release(jpeg_thumbnail_data_memory); + + if (picture_data_memory != NULL && picture_data_memory->release != NULL) + picture_data_memory->release(picture_data_memory); + + if (exif_data_memory != NULL && exif_data_memory->release != NULL) + exif_data_memory->release(exif_data_memory); + + if (data_memory != NULL && data_memory->release != NULL) + data_memory->release(data_memory); + + return -1; +} + +void *exynos_camera_picture_thread(void *data) +{ + struct exynos_camera *exynos_camera; + int rc; + int i; + + if (data == NULL) + return NULL; + + exynos_camera = (struct exynos_camera *) data; + + LOGE("%s: Starting thread", __func__); + exynos_camera->picture_thread_running = 1; + + if (exynos_camera->picture_enabled == 1) { + pthread_mutex_lock(&exynos_camera->picture_mutex); + + rc = exynos_camera_picture(exynos_camera); + if (rc < 0) { + LOGE("%s: picture failed!", __func__); + exynos_camera->picture_enabled = 0; + } + + if (exynos_camera->picture_memory != NULL && exynos_camera->picture_memory->release != NULL) { + exynos_camera->picture_memory->release(exynos_camera->picture_memory); + exynos_camera->picture_memory = NULL; + } + + pthread_mutex_unlock(&exynos_camera->picture_mutex); + } + + exynos_camera->picture_thread_running = 0; + exynos_camera->picture_enabled = 0; + + LOGE("%s: Exiting thread", __func__); + + return NULL; +} + +int exynos_camera_picture_start(struct exynos_camera *exynos_camera) +{ + pthread_attr_t thread_attr; + + int width, height, format, camera_format; + + int fd; + int rc; + + if (exynos_camera == NULL) + return -EINVAL; + + // Stop preview thread + exynos_camera_preview_stop(exynos_camera); + + width = exynos_camera->picture_width; + height = exynos_camera->picture_height; + format = exynos_camera->picture_format; + camera_format = exynos_camera->camera_picture_format; + + // V4L2 + + if (camera_format == 0) + camera_format = format; + + rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 0, camera_format); + if (rc < 0) { + LOGE("%s: enum fmt failed!", __func__); + return -1; + } + + rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 0, width, height, camera_format, V4L2_PIX_FMT_MODE_CAPTURE); + if (rc < 0) { + LOGE("%s: s fmt failed!", __func__); + return -1; + } + + // Only use 1 buffer + rc = exynos_v4l2_reqbufs_cap(exynos_camera, 0, 1); + if (rc < 0) { + LOGE("%s: reqbufs failed!", __func__); + return -1; + } + + rc = exynos_v4l2_querybuf_cap(exynos_camera, 0, 0); + if (rc < 0) { + LOGE("%s: querybuf failed!", __func__); + return -1; + } + + exynos_camera->picture_buffer_length = rc; + + if (exynos_camera->callbacks.request_memory != NULL) { + fd = exynos_v4l2_find_fd(exynos_camera, 0); + if (fd < 0) { + LOGE("%s: Unable to find v4l2 fd", __func__); + return -1; + } + + if (exynos_camera->picture_memory != NULL && exynos_camera->picture_memory->release != NULL) + exynos_camera->picture_memory->release(exynos_camera->picture_memory); + + exynos_camera->picture_memory = + exynos_camera->callbacks.request_memory(fd, + exynos_camera->picture_buffer_length, 1, 0); + if (exynos_camera->picture_memory == NULL) { + LOGE("%s: memory request failed!", __func__); + return -1; + } + } else { + LOGE("%s: No memory request function!", __func__); + return -1; + } + + rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, 0); + if (rc < 0) { + LOGE("%s: qbuf failed!", __func__); + return -1; + } + + rc = exynos_v4l2_streamon_cap(exynos_camera, 0); + if (rc < 0) { + LOGE("%s: streamon failed!", __func__); + return -1; + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_CAPTURE, 0); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + return -1; + } + + // Thread + + if (exynos_camera->picture_thread_running) { + LOGE("Picture thread is already running!"); + return -1; + } + + pthread_mutex_init(&exynos_camera->picture_mutex, NULL); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + + exynos_camera->picture_enabled = 1; + + rc = pthread_create(&exynos_camera->picture_thread, &thread_attr, + exynos_camera_picture_thread, (void *) exynos_camera); + if (rc < 0) { + LOGE("%s: Unable to create thread", __func__); + return -1; + } + + return 0; +} + +void exynos_camera_picture_stop(struct exynos_camera *exynos_camera) +{ + int rc; + int i; + + if (exynos_camera == NULL) + return; + + if (!exynos_camera->picture_enabled) { + LOGE("Picture was already stopped!"); + return; + } + + pthread_mutex_lock(&exynos_camera->picture_mutex); + + // Disable picture to make the thread end + exynos_camera->picture_enabled = 0; + + pthread_mutex_unlock(&exynos_camera->picture_mutex); + + // Wait for the thread to end + for (i = 0; i < 10; i++) { + if (!exynos_camera->picture_thread_running) + break; + + usleep(500); + } + + pthread_mutex_destroy(&exynos_camera->picture_mutex); +} + +// Auto-focus + +void *exynos_camera_auto_focus_thread(void *data) +{ + struct exynos_camera *exynos_camera; + int auto_focus_status = -1; + int auto_focus_result = 0; + int rc; + int i; + + if (data == NULL) + return NULL; + + exynos_camera = (struct exynos_camera *) data; + + LOGE("%s: Starting thread", __func__); + exynos_camera->auto_focus_thread_running = 1; + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SET_AUTO_FOCUS, AUTO_FOCUS_ON); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + auto_focus_result = 0; + goto thread_exit; + } + + while (exynos_camera->auto_focus_enabled == 1) { + pthread_mutex_lock(&exynos_camera->auto_focus_mutex); + + rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_AUTO_FOCUS_RESULT, &auto_focus_status); + if (rc < 0) { + LOGE("%s: g ctrl failed!", __func__); + auto_focus_result = 0; + pthread_mutex_unlock(&exynos_camera->auto_focus_mutex); + goto thread_exit; + } + + if (auto_focus_status & M5MO_AF_STATUS_IN_PROGRESS) { + usleep(10000); + } else if (auto_focus_status == M5MO_AF_STATUS_SUCCESS || auto_focus_status == M5MO_AF_STATUS_1ST_SUCCESS) { + auto_focus_result = 1; + pthread_mutex_unlock(&exynos_camera->auto_focus_mutex); + goto thread_exit; + } else { + auto_focus_result = 0; + pthread_mutex_unlock(&exynos_camera->auto_focus_mutex); + goto thread_exit; + } + + pthread_mutex_unlock(&exynos_camera->auto_focus_mutex); + } + +thread_exit: + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SET_AUTO_FOCUS, AUTO_FOCUS_OFF); + if (rc < 0) + LOGE("%s: s ctrl failed!", __func__); + + if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_FOCUS) && EXYNOS_CAMERA_CALLBACK_DEFINED(notify)) + exynos_camera->callbacks.notify(CAMERA_MSG_FOCUS, + (int32_t) auto_focus_result, 0, exynos_camera->callbacks.user); + + exynos_camera->auto_focus_thread_running = 0; + exynos_camera->auto_focus_enabled = 0; + + LOGE("%s: Exiting thread", __func__); + + return NULL; +} + +int exynos_camera_auto_focus_start(struct exynos_camera *exynos_camera) +{ + pthread_attr_t thread_attr; + int rc; + + if (exynos_camera == NULL) + return -EINVAL; + + // Thread + + if (exynos_camera->auto_focus_thread_running) { + LOGE("Auto-focus thread is already running!"); + return -1; + } + + pthread_mutex_init(&exynos_camera->auto_focus_mutex, NULL); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + + exynos_camera->auto_focus_enabled = 1; + + rc = pthread_create(&exynos_camera->auto_focus_thread, &thread_attr, + exynos_camera_auto_focus_thread, (void *) exynos_camera); + if (rc < 0) { + LOGE("%s: Unable to create thread", __func__); + return -1; + } + + return 0; +} + +void exynos_camera_auto_focus_stop(struct exynos_camera *exynos_camera) +{ + int rc; + int i; + + if (exynos_camera == NULL) + return; + + if (!exynos_camera->auto_focus_enabled) { + LOGE("Auto-focus was already stopped!"); + return; + } + + pthread_mutex_lock(&exynos_camera->auto_focus_mutex); + + // Disable auto-focus to make the thread end + exynos_camera->auto_focus_enabled = 0; + + pthread_mutex_unlock(&exynos_camera->auto_focus_mutex); + + // Wait for the thread to end + for (i = 0; i < 10; i++) { + if (!exynos_camera->auto_focus_thread_running) + break; + + usleep(500); + } + + pthread_mutex_destroy(&exynos_camera->auto_focus_mutex); +} + +// Preview + +int exynos_camera_preview(struct exynos_camera *exynos_camera) +{ + buffer_handle_t *buffer; + int stride; + + int width, height; + float format_bpp; + + char *preview_format_string; + int frame_size, offset; + void *preview_data; + void *window_data; + + unsigned int recording_y_addr; + unsigned int recording_cbcr_addr; + nsecs_t timestamp; + struct exynos_camera_addrs *addrs; + struct timespec ts; + + int index; + int rc; + int i; + + if (exynos_camera == NULL || exynos_camera->preview_memory == NULL || + exynos_camera->preview_window == NULL) + return -EINVAL; + + timestamp = systemTime(1); + + // V4L2 + + rc = exynos_v4l2_poll(exynos_camera, 0); + if (rc < 0) { + LOGE("%s: poll failed!", __func__); + return -1; + } else if (rc == 0) { + LOGE("%s: poll timeout!", __func__); + return -1; + } + + index = exynos_v4l2_dqbuf_cap(exynos_camera, 0); + if (index < 0 || index >= exynos_camera->preview_buffers_count) { + LOGE("%s: dqbuf failed!", __func__); + return -1; + } + + rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, index); + if (rc < 0) { + LOGE("%s: qbuf failed!", __func__); + return -1; + } + + // Preview window + + width = exynos_camera->preview_width; + height = exynos_camera->preview_height; + format_bpp = exynos_camera->preview_format_bpp; + + exynos_camera->preview_window->dequeue_buffer(exynos_camera->preview_window, + &buffer, &stride); + exynos_camera->gralloc->lock(exynos_camera->gralloc, *buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, + 0, 0, width, height, &window_data); + + if (window_data == NULL) { + LOGE("%s: gralloc lock failed!", __func__); + return -1; + } + + frame_size = exynos_camera->preview_frame_size; + offset = index * frame_size; + + preview_data = (void *) ((int) exynos_camera->preview_memory->data + offset); + memcpy(window_data, preview_data, frame_size); + + exynos_camera->gralloc->unlock(exynos_camera->gralloc, *buffer); + exynos_camera->preview_window->enqueue_buffer(exynos_camera->preview_window, + buffer); + + if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_PREVIEW_FRAME) && EXYNOS_CAMERA_CALLBACK_DEFINED(data)) { + exynos_camera->callbacks.data(CAMERA_MSG_PREVIEW_FRAME, + exynos_camera->preview_memory, index, NULL, exynos_camera->callbacks.user); + } + + // Recording + + if (exynos_camera->recording_enabled && exynos_camera->recording_memory != NULL) { + pthread_mutex_lock(&exynos_camera->recording_mutex); + + // V4L2 + + rc = exynos_v4l2_poll(exynos_camera, 2); + if (rc < 0) { + LOGE("%s: poll failed!", __func__); + goto error_recording; + } else if (rc == 0) { + LOGE("%s: poll timeout!", __func__); + goto error_recording; + } + + index = exynos_v4l2_dqbuf_cap(exynos_camera, 2); + if (index < 0) { + LOGE("%s: dqbuf failed!", __func__); + goto error_recording; + } + + recording_y_addr = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_PADDR_Y, index); + if (recording_y_addr == 0xffffffff) { + LOGE("%s: s ctrl failed!", __func__); + goto error_recording; + } + + recording_cbcr_addr = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_PADDR_CBCR, index); + if (recording_cbcr_addr == 0xffffffff) { + LOGE("%s: s ctrl failed!", __func__); + goto error_recording; + } + + addrs = (struct exynos_camera_addrs *) exynos_camera->recording_memory->data; + + addrs[index].type = 0; // kMetadataBufferTypeCameraSource + addrs[index].y = recording_y_addr; + addrs[index].cbcr = recording_cbcr_addr; + addrs[index].index = index; + addrs[index].reserved = 0; + + pthread_mutex_unlock(&exynos_camera->recording_mutex); + + if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_VIDEO_FRAME) && EXYNOS_CAMERA_CALLBACK_DEFINED(data_timestamp)) { + exynos_camera->callbacks.data_timestamp(timestamp, CAMERA_MSG_VIDEO_FRAME, + exynos_camera->recording_memory, index, exynos_camera->callbacks.user); + } else { + rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, index); + if (rc < 0) { + LOGE("%s: qbuf failed!", __func__); + return -1; + } + } + } + + return 0; + +error_recording: + pthread_mutex_lock(&exynos_camera->recording_mutex); + + return -1; +} + +void *exynos_camera_preview_thread(void *data) +{ + struct exynos_camera *exynos_camera; + int rc; + + if (data == NULL) + return NULL; + + exynos_camera = (struct exynos_camera *) data; + + LOGE("%s: Starting thread", __func__); + exynos_camera->preview_thread_running = 1; + + if (exynos_camera->preview_window == NULL) { + // Lock preview lock mutex + pthread_mutex_lock(&exynos_camera->preview_lock_mutex); + } + + while (exynos_camera->preview_enabled == 1) { + pthread_mutex_lock(&exynos_camera->preview_mutex); + + rc = exynos_camera_preview(exynos_camera); + if (rc < 0) { + LOGE("%s: preview failed!", __func__); + exynos_camera->preview_enabled = 0; + } + + pthread_mutex_unlock(&exynos_camera->preview_mutex); + } + + exynos_camera->preview_thread_running = 0; + LOGE("%s: Exiting thread", __func__); + + return NULL; +} + +int exynos_camera_preview_start(struct exynos_camera *exynos_camera) +{ + struct v4l2_streamparm streamparm; + int width, height, format; + float format_bpp; + int fps, frame_size; + int fd; + + pthread_attr_t thread_attr; + + int rc; + int i; + + if (exynos_camera == NULL) + return -EINVAL; + + if (exynos_camera->preview_enabled) { + LOGE("Preview was already started!"); + return 0; + } + + // V4L2 + + format = exynos_camera->preview_format; + + rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 0, format); + if (rc < 0) { + LOGE("%s: enum fmt failed!", __func__); + return -1; + } + + width = exynos_camera->preview_width; + height = exynos_camera->preview_height; + format_bpp = exynos_camera->preview_format_bpp; + + rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 0, width, height, format, V4L2_PIX_FMT_MODE_PREVIEW); + if (rc < 0) { + LOGE("%s: s fmt failed!", __func__); + return -1; + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CACHEABLE, 1); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + return -1; + } + + for (i = EXYNOS_CAMERA_MAX_BUFFERS_COUNT; i >= EXYNOS_CAMERA_MIN_BUFFERS_COUNT; i--) { + rc = exynos_v4l2_reqbufs_cap(exynos_camera, 0, i); + if (rc >= 0) + break; + } + + if (rc < 0) { + LOGE("%s: reqbufs failed!", __func__); + return -1; + } + + exynos_camera->preview_buffers_count = rc; + LOGD("Found %d preview buffers available!", exynos_camera->preview_buffers_count); + + fps = exynos_camera->preview_fps; + memset(&streamparm, 0, sizeof(streamparm)); + streamparm.parm.capture.timeperframe.numerator = 1; + streamparm.parm.capture.timeperframe.denominator = fps; + + rc = exynos_v4l2_s_parm_cap(exynos_camera, 0, &streamparm); + if (rc < 0) { + LOGE("%s: s parm failed!", __func__); + return -1; + } + + frame_size = (int) ((float) width * (float) height * format_bpp); + for (i = 0; i < exynos_camera->preview_buffers_count; i++) { + rc = exynos_v4l2_querybuf_cap(exynos_camera, 0, i); + if (rc < 0) { + LOGE("%s: querybuf failed!", __func__); + return -1; + } + + if (rc < frame_size) { + LOGE("%s: problematic frame size: %d/%d", __func__, rc, frame_size); + return -1; + } + } + + frame_size = rc; + exynos_camera->preview_frame_size = frame_size; + + if (exynos_camera->callbacks.request_memory != NULL) { + fd = exynos_v4l2_find_fd(exynos_camera, 0); + if (fd < 0) { + LOGE("%s: Unable to find v4l2 fd", __func__); + return -1; + } + + if (exynos_camera->preview_memory != NULL && exynos_camera->preview_memory->release != NULL) + exynos_camera->preview_memory->release(exynos_camera->preview_memory); + + exynos_camera->preview_memory = + exynos_camera->callbacks.request_memory(fd, + frame_size, exynos_camera->preview_buffers_count, 0); + if (exynos_camera->preview_memory == NULL) { + LOGE("%s: memory request failed!", __func__); + return -1; + } + } else { + LOGE("%s: No memory request function!", __func__); + return -1; + } + + for (i = 0; i < exynos_camera->preview_buffers_count; i++) { + rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, i); + if (rc < 0) { + LOGE("%s: qbuf failed!", __func__); + return -1; + } + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_ROTATION, + exynos_camera->camera_rotation); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + return -1; + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_HFLIP, + exynos_camera->camera_hflip); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + return -1; + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_VFLIP, + exynos_camera->camera_vflip); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + return -1; + } + + rc = exynos_v4l2_streamon_cap(exynos_camera, 0); + if (rc < 0) { + LOGE("%s: streamon failed!", __func__); + return -1; + } + + // Thread + + pthread_mutex_init(&exynos_camera->preview_mutex, NULL); + pthread_mutex_init(&exynos_camera->preview_lock_mutex, NULL); + + // Lock preview lock + pthread_mutex_lock(&exynos_camera->preview_lock_mutex); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + + exynos_camera->preview_enabled = 1; + + rc = pthread_create(&exynos_camera->preview_thread, &thread_attr, + exynos_camera_preview_thread, (void *) exynos_camera); + if (rc < 0) { + LOGE("%s: Unable to create thread", __func__); + return -1; + } + + return 0; +} + +void exynos_camera_preview_stop(struct exynos_camera *exynos_camera) +{ + int rc; + int i; + + if (exynos_camera == NULL) + return; + + if (!exynos_camera->preview_enabled) { + LOGE("Preview was already stopped!"); + return; + } + + exynos_camera->preview_enabled = 0; + + // Unlock preview lock + pthread_mutex_unlock(&exynos_camera->preview_lock_mutex); + + pthread_mutex_lock(&exynos_camera->preview_mutex); + + // Wait for the thread to end + for (i = 0; i < 10; i++) { + if (!exynos_camera->preview_thread_running) + break; + + usleep(1000); + } + + rc = exynos_v4l2_streamoff_cap(exynos_camera, 0); + if (rc < 0) { + LOGE("%s: streamoff failed!", __func__); + } + + exynos_camera->preview_params_set = 0; + + if (exynos_camera->preview_memory != NULL && exynos_camera->preview_memory->release != NULL) { + exynos_camera->preview_memory->release(exynos_camera->preview_memory); + exynos_camera->preview_memory = NULL; + } + + exynos_camera->preview_window = NULL; + + pthread_mutex_unlock(&exynos_camera->preview_mutex); + + pthread_mutex_destroy(&exynos_camera->preview_lock_mutex); + pthread_mutex_destroy(&exynos_camera->preview_mutex); +} + +// Recording + +void exynos_camera_recording_frame_release(struct exynos_camera *exynos_camera, void *data) +{ + struct exynos_camera_addrs *addrs; + int rc; + + if (exynos_camera == NULL || data == NULL) + return; + + addrs = (struct exynos_camera_addrs *) data; + if (addrs->index >= (unsigned int) exynos_camera->recording_buffers_count) + return; + + pthread_mutex_lock(&exynos_camera->recording_mutex); + + rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, addrs->index); + if (rc < 0) { + LOGE("%s: qbuf failed!", __func__); + goto error; + } + +error: + pthread_mutex_unlock(&exynos_camera->recording_mutex); +} + +int exynos_camera_recording_start(struct exynos_camera *exynos_camera) +{ + int width, height, format; + int fd; + + int rc; + int i; + + if (exynos_camera == NULL) + return -EINVAL; + + if (exynos_camera->recording_enabled) { + LOGE("Recording was already started!"); + return 0; + } + + pthread_mutex_lock(&exynos_camera->preview_mutex); + + // V4L2 + + format = exynos_camera->recording_format; + + rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 2, format); + if (rc < 0) { + LOGE("%s: enum fmt failed!", __func__); + goto error; + } + + width = exynos_camera->recording_width; + height = exynos_camera->recording_height; + + rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 2, width, height, format, V4L2_PIX_FMT_MODE_CAPTURE); + if (rc < 0) { + LOGE("%s: s fmt failed!", __func__); + goto error; + } + + for (i = EXYNOS_CAMERA_MAX_BUFFERS_COUNT; i >= EXYNOS_CAMERA_MIN_BUFFERS_COUNT; i--) { + rc = exynos_v4l2_reqbufs_cap(exynos_camera, 2, i); + if (rc >= 0) + break; + } + + if (rc < 0) { + LOGE("%s: reqbufs failed!", __func__); + goto error; + } + + exynos_camera->recording_buffers_count = rc; + LOGD("Found %d recording buffers available!", exynos_camera->recording_buffers_count); + + for (i = 0; i < exynos_camera->recording_buffers_count; i++) { + rc = exynos_v4l2_querybuf_cap(exynos_camera, 2, i); + if (rc < 0) { + LOGE("%s: querybuf failed!", __func__); + goto error; + } + } + + if (exynos_camera->callbacks.request_memory != NULL) { + if (exynos_camera->recording_memory != NULL && exynos_camera->recording_memory->release != NULL) + exynos_camera->recording_memory->release(exynos_camera->recording_memory); + + exynos_camera->recording_memory = + exynos_camera->callbacks.request_memory(-1, sizeof(struct exynos_camera_addrs), + exynos_camera->recording_buffers_count, 0); + if (exynos_camera->recording_memory == NULL) { + LOGE("%s: memory request failed!", __func__); + goto error; + } + } else { + LOGE("%s: No memory request function!", __func__); + goto error; + } + + for (i = 0; i < exynos_camera->recording_buffers_count; i++) { + rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, i); + if (rc < 0) { + LOGE("%s: qbuf failed!", __func__); + goto error; + } + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_ROTATION, + exynos_camera->camera_rotation); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + goto error; + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_HFLIP, + exynos_camera->camera_hflip); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + goto error; + } + + rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_VFLIP, + exynos_camera->camera_vflip); + if (rc < 0) { + LOGE("%s: s ctrl failed!", __func__); + goto error; + } + + rc = exynos_v4l2_streamon_cap(exynos_camera, 2); + if (rc < 0) { + LOGE("%s: streamon failed!", __func__); + goto error; + } + + pthread_mutex_init(&exynos_camera->recording_mutex, NULL); + + exynos_camera->recording_enabled = 1; + + pthread_mutex_unlock(&exynos_camera->preview_mutex); + + return 0; +error: + pthread_mutex_unlock(&exynos_camera->preview_mutex); + + return -1; +} + +void exynos_camera_recording_stop(struct exynos_camera *exynos_camera) +{ + int rc; + + if (exynos_camera == NULL) + return; + + if (!exynos_camera->recording_enabled) { + LOGE("Recording was already stopped!"); + return; + } + + exynos_camera->recording_enabled = 0; + + pthread_mutex_lock(&exynos_camera->preview_mutex); + + rc = exynos_v4l2_streamoff_cap(exynos_camera, 2); + if (rc < 0) { + LOGE("%s: streamoff failed!", __func__); + } + + if (exynos_camera->recording_memory != NULL && exynos_camera->recording_memory->release != NULL) { + exynos_camera->recording_memory->release(exynos_camera->recording_memory); + exynos_camera->recording_memory = NULL; + } + + pthread_mutex_unlock(&exynos_camera->preview_mutex); + + pthread_mutex_destroy(&exynos_camera->recording_mutex); +} + +/* + * Exynos Camera OPS + */ + +int exynos_camera_set_preview_window(struct camera_device *dev, + struct preview_stream_ops *w) +{ + struct exynos_camera *exynos_camera; + + int width, height, format, hal_format; + + buffer_handle_t *buffer; + int stride; + void *addr = NULL; + + int rc; + + LOGD("%s(%p, %p)", __func__, dev, w); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + if (w == NULL) + return 0; + + exynos_camera->preview_window = w; + + if (w->set_buffer_count == NULL || w->set_usage == NULL || w->set_buffers_geometry == NULL) + return -EINVAL; + + if (exynos_camera->preview_buffers_count <= 0) { + LOGE("%s: Invalid preview buffers count", __func__); + exynos_camera->preview_buffers_count = EXYNOS_CAMERA_MAX_BUFFERS_COUNT; + } + + rc = w->set_buffer_count(w, exynos_camera->preview_buffers_count); + if (rc) { + LOGE("%s: Unable to set buffer count (%d)", __func__, + exynos_camera->preview_buffers_count); + return -1; + } + + rc = w->set_usage(w, GRALLOC_USAGE_SW_WRITE_OFTEN); + if (rc) { + LOGE("%s: Unable to set usage", __func__); + return -1; + } + + width = exynos_camera->preview_width; + height = exynos_camera->preview_height; + format = exynos_camera->preview_format; + + switch (format) { + case V4L2_PIX_FMT_NV21: + hal_format = HAL_PIXEL_FORMAT_YCrCb_420_SP; + break; + case V4L2_PIX_FMT_YUV420: + hal_format = HAL_PIXEL_FORMAT_YV12; + break; + case V4L2_PIX_FMT_RGB565: + hal_format = HAL_PIXEL_FORMAT_RGB_565; + break; + case V4L2_PIX_FMT_RGB32: + hal_format = HAL_PIXEL_FORMAT_RGBX_8888; + break; + default: + hal_format = HAL_PIXEL_FORMAT_YCrCb_420_SP; + break; + } + + rc = w->set_buffers_geometry(w, width, height, hal_format); + if (rc) { + LOGE("%s: Unable to set buffers geometry", __func__); + return -1; + } + + // Unlock preview lock + pthread_mutex_unlock(&exynos_camera->preview_lock_mutex); + + return 0; +} + +void exynos_camera_set_callbacks(struct camera_device *dev, + camera_notify_callback notify_cb, + camera_data_callback data_cb, + camera_data_timestamp_callback data_cb_timestamp, + camera_request_memory get_memory, + void *user) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p, %p)", __func__, dev, user); + + if (dev == NULL || dev->priv == NULL) + return; + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera->callbacks.notify = notify_cb; + exynos_camera->callbacks.data = data_cb; + exynos_camera->callbacks.data_timestamp = data_cb_timestamp; + exynos_camera->callbacks.request_memory = get_memory; + exynos_camera->callbacks.user = user; +} + +void exynos_camera_enable_msg_type(struct camera_device *dev, int32_t msg_type) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p, %d)", __func__, dev, msg_type); + + if (dev == NULL || dev->priv == NULL) + return; + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera->messages_enabled |= msg_type; +} + +void exynos_camera_disable_msg_type(struct camera_device *dev, int32_t msg_type) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p, %d)", __func__, dev, msg_type); + + if (dev == NULL || dev->priv == NULL) + return; + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera->messages_enabled &= ~msg_type; +} + +int exynos_camera_msg_type_enabled(struct camera_device *dev, int32_t msg_type) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p, %d)", __func__, dev, msg_type); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + return exynos_camera->messages_enabled & msg_type; +} + +int exynos_camera_start_preview(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + return exynos_camera_preview_start(exynos_camera); +} + +void exynos_camera_stop_preview(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return; + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera_preview_stop(exynos_camera); +} + +int exynos_camera_preview_enabled(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + return exynos_camera->preview_enabled; +} + +int exynos_camera_store_meta_data_in_buffers(struct camera_device *dev, + int enable) +{ + LOGD("%s(%p, %d)", __func__, dev, enable); + + if (!enable) { + LOGE("%s: Cannot disable meta-data in buffers!", __func__); + return -1; + } + + return 0; +} + +int exynos_camera_start_recording(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + exynos_camera = (struct exynos_camera *) dev->priv; + + return exynos_camera_recording_start(exynos_camera); +} + +void exynos_camera_stop_recording(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera_recording_stop(exynos_camera); +} + +int exynos_camera_recording_enabled(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + return exynos_camera->recording_enabled; +} + +void exynos_camera_release_recording_frame(struct camera_device *dev, + const void *opaque) +{ + struct exynos_camera *exynos_camera; + + LOGV("%s(%p, %p)", __func__, dev, opaque); + + if (dev == NULL || dev->priv == NULL) + return; + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera_recording_frame_release(exynos_camera, (void *) opaque); +} + +int exynos_camera_auto_focus(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + return exynos_camera_auto_focus_start(exynos_camera); +} + +int exynos_camera_cancel_auto_focus(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera_auto_focus_stop(exynos_camera); + + return 0; +} + +int exynos_camera_take_picture(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + return exynos_camera_picture_start(exynos_camera); +} + +int exynos_camera_cancel_picture(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + exynos_camera_picture_stop(exynos_camera); + + return 0; +} + +int exynos_camera_set_parameters(struct camera_device *dev, + const char *params) +{ + struct exynos_camera *exynos_camera; + int rc; + + LOGD("%s(%p, %s)", __func__, dev, params); + + if (dev == NULL || dev->priv == NULL || params == NULL) + return -EINVAL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + rc = exynos_params_string_set(exynos_camera, (char *) params); + if (rc < 0) { + LOGE("%s: Unable to set params string", __func__); + return -1; + } + + rc = exynos_camera_params_apply(exynos_camera); + if (rc < 0) { + LOGE("%s: Unable to apply params", __func__); + return -1; + } + + return 0; +} + +char *exynos_camera_get_parameters(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + char *params; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return NULL; + + exynos_camera = (struct exynos_camera *) dev->priv; + + params = exynos_params_string_get(exynos_camera); + if (params == NULL) { + LOGE("%s: Couldn't find any param", __func__); + return strdup(""); + } + + return params; +} + +void exynos_camera_put_parameters(struct camera_device *dev, char *params) +{ + LOGD("%s(%p)", __func__, dev); + + if (params != NULL) + free(params); +} + +int exynos_camera_send_command(struct camera_device *dev, + int32_t cmd, int32_t arg1, int32_t arg2) +{ + LOGD("%s(%p, %d, %d, %d)", __func__, dev, cmd, arg1, arg2); + + return 0; +} + +void exynos_camera_release(struct camera_device *dev) +{ + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, dev); + + if (dev == NULL || dev->priv == NULL) + return; + + exynos_camera = (struct exynos_camera *) dev->priv; + + if (exynos_camera->preview_memory != NULL && exynos_camera->preview_memory->release != NULL) { + exynos_camera->preview_memory->release(exynos_camera->preview_memory); + exynos_camera->preview_memory = NULL; + } + + if (exynos_camera->picture_memory != NULL && exynos_camera->picture_memory->release != NULL) { + exynos_camera->picture_memory->release(exynos_camera->picture_memory); + exynos_camera->picture_memory = NULL; + } + + exynos_camera_deinit(exynos_camera); +} + +int exynos_camera_dump(struct camera_device *dev, int fd) +{ + LOGD("%s(%p, %d)", __func__, dev, fd); + + return 0; +} + +/* + * Interface + */ + +struct camera_device_ops exynos_camera_ops = { + .set_preview_window = exynos_camera_set_preview_window, + .set_callbacks = exynos_camera_set_callbacks, + .enable_msg_type = exynos_camera_enable_msg_type, + .disable_msg_type = exynos_camera_disable_msg_type, + .msg_type_enabled = exynos_camera_msg_type_enabled, + .start_preview = exynos_camera_start_preview, + .stop_preview = exynos_camera_stop_preview, + .preview_enabled = exynos_camera_preview_enabled, + .store_meta_data_in_buffers = exynos_camera_store_meta_data_in_buffers, + .start_recording = exynos_camera_start_recording, + .stop_recording = exynos_camera_stop_recording, + .recording_enabled = exynos_camera_recording_enabled, + .release_recording_frame = exynos_camera_release_recording_frame, + .auto_focus = exynos_camera_auto_focus, + .cancel_auto_focus = exynos_camera_cancel_auto_focus, + .take_picture = exynos_camera_take_picture, + .cancel_picture = exynos_camera_cancel_picture, + .set_parameters = exynos_camera_set_parameters, + .get_parameters = exynos_camera_get_parameters, + .put_parameters = exynos_camera_put_parameters, + .send_command = exynos_camera_send_command, + .release = exynos_camera_release, + .dump = exynos_camera_dump, +}; + +int exynos_camera_close(hw_device_t *device) +{ + struct camera_device *camera_device; + struct exynos_camera *exynos_camera; + + LOGD("%s(%p)", __func__, device); + + if (device == NULL) + return -EINVAL; + + camera_device = (struct camera_device *) device; + + if (camera_device->priv != NULL) { + free(camera_device->priv); + } + + free(camera_device); + + return 0; +} + +int exynos_camera_open(const struct hw_module_t* module, const char *camera_id, + struct hw_device_t** device) +{ + struct camera_device *camera_device = NULL; + struct exynos_camera *exynos_camera = NULL; + int id; + int rc; + + LOGD("%s(%p, %s, %p)", __func__, module, camera_id, device); + + if (module == NULL || camera_id == NULL || device == NULL) + return -EINVAL; + + id = atoi(camera_id); + if (id < 0) + return -EINVAL; + + exynos_camera = calloc(1, sizeof(struct exynos_camera)); + exynos_camera->config = exynos_camera_config; + + if (exynos_camera->config->presets_count > EXYNOS_CAMERA_MAX_PRESETS_COUNT || + exynos_camera->config->v4l2_nodes_count > EXYNOS_CAMERA_MAX_V4L2_NODES_COUNT) + goto error_preset; + + if (id >= exynos_camera->config->presets_count) + goto error_preset; + + rc = exynos_camera_init(exynos_camera, id); + if (rc < 0) { + LOGE("%s: Unable to init camera", __func__); + goto error; + } + + camera_device = calloc(1, sizeof(struct camera_device)); + camera_device->common.tag = HARDWARE_DEVICE_TAG; + camera_device->common.version = 0; + camera_device->common.module = (struct hw_module_t *) module; + camera_device->common.close = exynos_camera_close; + + camera_device->ops = &exynos_camera_ops; + camera_device->priv = exynos_camera; + + *device = (struct hw_device_t *) &(camera_device->common); + + return 0; + +error: + exynos_camera_deinit(exynos_camera); + +error_device: + if (camera_device != NULL) + free(camera_device); + +error_preset: + if (exynos_camera != NULL) + free(exynos_camera); + + return -1; +} + +int exynos_camera_get_number_of_cameras(void) +{ + LOGD("%s()", __func__); + + if (exynos_camera_config == NULL || exynos_camera_config->presets == NULL) { + LOGE("%s: Unable to find proper camera config", __func__); + return -1; + } + + return exynos_camera_config->presets_count; +} + +int exynos_camera_get_camera_info(int id, struct camera_info *info) +{ + LOGD("%s(%d, %p)", __func__, id, info); + + if (id < 0 || info == NULL) + return -EINVAL; + + if (exynos_camera_config == NULL || exynos_camera_config->presets == NULL) { + LOGE("%s: Unable to find proper camera config", __func__); + return -1; + } + + if (id >= exynos_camera_config->presets_count) + return -EINVAL; + + LOGD("Selected camera: %s", exynos_camera_config->presets[id].name); + + info->facing = exynos_camera_config->presets[id].facing; + info->orientation = exynos_camera_config->presets[id].orientation; + + return 0; +} + +struct hw_module_methods_t exynos_camera_module_methods = { + .open = exynos_camera_open, +}; + +struct camera_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = CAMERA_HARDWARE_MODULE_ID, + .name = "Exynos Camera", + .author = "Paul Kocialkowski", + .methods = &exynos_camera_module_methods, + }, + .get_number_of_cameras = exynos_camera_get_number_of_cameras, + .get_camera_info = exynos_camera_get_camera_info, +}; |