| /* |
| * 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> |
| |
| #define LOG_TAG "exynos_camera" |
| #include <utils/Log.h> |
| #include <utils/Timers.h> |
| |
| #include "exynos_camera.h" |
| |
| #define BIG2LITTLE_ENDIAN(big) ((big & 0xff) << 24 | (big & 0xff00) << 8 | (big & 0xff0000) >> 8 | (big & 0xff000000) >> 24) |
| |
| /* |
| * Devices configurations |
| */ |
| |
| struct exynos_camera_mbus_resolution exynos_camera_mbus_resolutions_s5k6a3_smdk4x12[] = { |
| // 16:9 ratio |
| { 1280, 720, 1344, 756 }, |
| // 4:3 ratio |
| { 1280, 960, 1392, 1044 }, |
| { 960, 720, 1392, 1044 }, |
| { 640, 480, 1392, 1044 }, |
| { 320, 240, 1392, 1044 }, |
| // 1:1 ratio |
| { 1392, 1392, 1392, 1392 }, |
| { 704, 704, 1392, 1392 }, |
| { 320, 320, 1392, 1392 }, |
| }; |
| |
| struct exynos_camera_preset exynos_camera_presets_smdk4x12[] = { |
| { |
| .name = "S5C73M3", |
| .facing = CAMERA_FACING_BACK, |
| .orientation = 90, |
| .rotation = 0, |
| .hflip = 0, |
| .vflip = 0, |
| .capture_format = V4L2_PIX_FMT_INTERLEAVED, |
| .picture_format = 0, |
| .fimc_is = 0, |
| .focal_length = 3.7f, |
| .horizontal_view_angle = 63.0f, |
| .vertical_view_angle = 49.3f, |
| .metering = METERING_CENTER, |
| .params = { |
| .preview_size_values = "960x720,1280x720,1184x666,960x640,704x576,640x480,352x288,320x240", |
| .preview_size = "960x720", |
| .preview_format_values = "yuv420sp,yuv420p,rgb565", |
| .preview_format = "yuv420sp", |
| .preview_frame_rate_values = "30,20,15", |
| .preview_frame_rate = 30, |
| .preview_fps_range_values = "(15000,15000),(15000,30000),(30000,30000)", |
| .preview_fps_range = "15000,30000", |
| |
| .picture_size_values = "640x480,1024x768,1280x720,1600x1200,2560x1920,3264x2448,2048x1536,3264x1836,2048x1152,3264x2176", |
| .picture_size = "3264x2448", |
| .picture_format_values = "jpeg", |
| .picture_format = "jpeg", |
| .jpeg_thumbnail_size_values = "160x120,160x90,144x96", |
| .jpeg_thumbnail_width = 160, |
| .jpeg_thumbnail_height = 120, |
| .jpeg_thumbnail_quality = 100, |
| .jpeg_quality = 90, |
| |
| .video_snapshot_supported = 0, |
| .full_video_snap_supported = 0, |
| |
| .recording_size = "1280x720", |
| .recording_size_values = "1280x720,1920x1080,720x480,640x480,352x288,320x240,176x144", |
| .recording_format = "yuv420sp", |
| |
| .focus_mode = "auto", |
| .focus_mode_values = "auto,infinity,macro,fixed,continuous-picture,continuous-video", |
| .focus_distances = "0.15,1.20,Infinity", |
| .focus_areas = "(0,0,0,0,0)", |
| .max_num_focus_areas = 1, |
| |
| .max_detected_faces = 15, |
| |
| .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, |
| |
| .auto_exposure_lock_supported = 1, |
| .auto_exposure_lock = 0, |
| |
| .auto_white_balance_lock_supported = 1, |
| .auto_white_balance_lock = 0, |
| |
| .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", |
| |
| .antibanding = "auto", |
| .antibanding_values = "off,auto,50hz,60hz", |
| |
| .scene_mode = "auto", |
| .scene_mode_values = "auto,portrait,landscape,night,beach,snow,sunset,fireworks,action,party,candlelight,dusk-dawn,fall-color,text,back-light,high-sensitivity", |
| |
| .effect = "none", |
| .effect_values = "none,mono,negative,sepia,solarize,posterize,washed,vintage-warm,vintage-cold,point-blue,point-red-yellow,point-green", |
| |
| .iso = "auto", |
| .iso_values = "auto,ISO100,ISO200,ISO400,ISO800", |
| |
| .image_stabilization = "off", |
| .image_stabilization_values = "on,off", |
| }, |
| .mbus_resolutions = NULL, |
| .mbus_resolutions_count = 0, |
| }, |
| { |
| .name = "S5K6A3", |
| .facing = CAMERA_FACING_FRONT, |
| .orientation = 270, |
| .rotation = 0, |
| .hflip = 0, |
| .vflip = 0, |
| .capture_format = 0, |
| .picture_format = V4L2_PIX_FMT_YUYV, |
| .fimc_is = 1, |
| .focal_length = 2.73f, |
| .horizontal_view_angle = 52.58f, |
| .vertical_view_angle = 52.58f, |
| .metering = METERING_CENTER, |
| .params = { |
| .preview_size_values = "1280x720,960x720,640x480,320x240,704x704,320x320", |
| .preview_size = "960x720", |
| .preview_format_values = "yuv420sp,yuv420p,rgb565", |
| .preview_format = "yuv420sp", |
| .preview_frame_rate_values = "30,20,15,8", |
| .preview_frame_rate = 30, |
| .preview_fps_range_values = "(8000,8000),(15000,15000),(15000,30000),(30000,30000)", |
| .preview_fps_range = "15000,30000", |
| |
| .picture_size_values = "1280x960,1392x1392,640x480,1280x720,720x480,320x240", |
| .picture_size = "1280x960", |
| .picture_format_values = "jpeg", |
| .picture_format = "jpeg", |
| .jpeg_thumbnail_size_values = "160x120,160x160,160x90,144x96", |
| .jpeg_thumbnail_width = 160, |
| .jpeg_thumbnail_height = 120, |
| .jpeg_thumbnail_quality = 100, |
| .jpeg_quality = 90, |
| |
| .video_snapshot_supported = 0, |
| .full_video_snap_supported = 0, |
| |
| .recording_size = "1280x720", |
| .recording_size_values = "1280x720,720x480,640x480,352x288,320x320,320x240,176x144", |
| .recording_format = "yuv420sp", |
| |
| .focus_mode = "fixed", |
| .focus_mode_values = "infinity,fixed", |
| .focus_distances = "0.20,0.25,Infinity", |
| .focus_areas = NULL, |
| .max_num_focus_areas = 0, |
| |
| .zoom_supported = 0, |
| |
| .auto_exposure_lock_supported = 0, |
| .auto_exposure_lock = 0, |
| |
| .auto_white_balance_lock_supported = 0, |
| .auto_white_balance_lock = 0, |
| |
| .flash_mode = NULL, |
| .flash_mode_values = NULL, |
| |
| .max_detected_faces = 5, |
| |
| .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", |
| |
| .antibanding = NULL, |
| .antibanding_values = NULL, |
| |
| .scene_mode = NULL, |
| .scene_mode_values = NULL, |
| |
| .effect = "none", |
| .effect_values = "none,mono,negative,sepia,solarize,posterize,washed,vintage-warm,vintage-cold,point-blue,point-red-yellow,point-green", |
| |
| .iso = "auto", |
| .iso_values = "auto", |
| |
| .image_stabilization = "off", |
| .image_stabilization_values = "off", |
| }, |
| .mbus_resolutions = (struct exynos_camera_mbus_resolution *) &exynos_camera_mbus_resolutions_s5k6a3_smdk4x12, |
| .mbus_resolutions_count = 8, |
| }, |
| }; |
| |
| struct exynos_v4l2_node exynos_v4l2_nodes_smdk4x12[] = { |
| { // FIMC0 is used for capture |
| .id = 0, |
| .node = "/dev/video0", |
| }, |
| { // FIMC1 is used for preview output |
| .id = 1, |
| .node = "/dev/video1", |
| }, |
| { // FIMC2 is used for picture output |
| .id = 2, |
| .node = "/dev/video2", |
| }, |
| { // FIMC3 is used for recording output |
| .id = 3, |
| .node = "/dev/video3", |
| }, |
| }; |
| |
| struct exynox_camera_config exynos_camera_config_smdk4x12 = { |
| .presets = (struct exynos_camera_preset *) &exynos_camera_presets_smdk4x12, |
| .presets_count = 2, |
| .v4l2_nodes = (struct exynos_v4l2_node *) &exynos_v4l2_nodes_smdk4x12, |
| .v4l2_nodes_count = 4, |
| }; |
| |
| /* |
| * Exynos Camera |
| */ |
| |
| struct exynox_camera_config *exynos_camera_config = |
| &exynos_camera_config_smdk4x12; |
| |
| int exynos_camera_start(struct exynos_camera *exynos_camera, int id) |
| { |
| int rc; |
| |
| if (exynos_camera == NULL || id >= exynos_camera->config->presets_count) |
| return -EINVAL; |
| |
| // ION |
| |
| #ifdef EXYNOS_ION |
| rc = exynos_ion_init(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to init ION", __func__); |
| goto error; |
| } |
| |
| rc = exynos_ion_open(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to open ION", __func__); |
| goto error; |
| } |
| #endif |
| |
| // V4L2 |
| |
| rc = exynos_v4l2_init(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to init v4l2", __func__); |
| goto error; |
| } |
| |
| // FIMC0 |
| |
| rc = exynos_v4l2_open(exynos_camera, 0); |
| if (rc < 0) { |
| ALOGE("%s: Unable to open v4l2 device", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_querycap_cap(exynos_camera, 0); |
| if (rc < 0) { |
| ALOGE("%s: Unable to query capabilities", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_enum_input(exynos_camera, 0, id); |
| if (rc < 0) { |
| ALOGE("%s: Unable to enumerate input", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_s_input(exynos_camera, 0, id); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set inputs", __func__); |
| goto error; |
| } |
| |
| // Recording |
| |
| exynos_camera->recording_metadata = 1; |
| |
| // Params |
| |
| rc = exynos_camera_params_init(exynos_camera, id); |
| if (rc < 0) { |
| ALOGE("%s: Unable to init params", __func__); |
| goto error; |
| } |
| |
| // Gralloc |
| |
| rc = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, (const struct hw_module_t **) &exynos_camera->gralloc); |
| if (rc) |
| ALOGE("%s: Unable to get gralloc module", __func__); |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| exynos_v4l2_close(exynos_camera, 0); |
| |
| #ifdef EXYNOS_ION |
| exynos_ion_close(exynos_camera); |
| #endif |
| |
| rc = -1; |
| |
| complete: |
| return rc; |
| } |
| |
| void exynos_camera_stop(struct exynos_camera *exynos_camera) |
| { |
| int i; |
| int id; |
| |
| if (exynos_camera == NULL || exynos_camera->config == NULL) |
| return; |
| |
| exynos_v4l2_close(exynos_camera, 0); |
| |
| #ifdef EXYNOS_ION |
| exynos_ion_close(exynos_camera); |
| #endif |
| } |
| |
| // 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_capture_format = exynos_camera->config->presets[id].capture_format; |
| exynos_camera->camera_picture_format = exynos_camera->config->presets[id].picture_format; |
| exynos_camera->camera_fimc_is = exynos_camera->config->presets[id].fimc_is; |
| exynos_camera->camera_focal_length = (int) (exynos_camera->config->presets[id].focal_length * 100); |
| exynos_camera->camera_metering = exynos_camera->config->presets[id].metering; |
| |
| exynos_camera->camera_mbus_resolutions = exynos_camera->config->presets[id].mbus_resolutions; |
| exynos_camera->camera_mbus_resolutions_count = exynos_camera->config->presets[id].mbus_resolutions_count; |
| |
| // 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); |
| |
| if (exynos_camera->config->presets[id].params.video_snapshot_supported == 1) |
| exynos_param_string_set(exynos_camera, "video-snapshot-supported", "true"); |
| if (exynos_camera->config->presets[id].params.full_video_snap_supported == 1) |
| exynos_param_string_set(exynos_camera, "full-video-snap-supported", "true"); |
| |
| // 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); |
| sprintf(exynos_camera->raw_focus_areas,"%s", |
| 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); |
| } |
| |
| // Face Detection |
| exynos_camera->max_detected_faces = exynos_camera->config->presets[id].params.max_detected_faces; |
| exynos_param_int_set(exynos_camera, "max-num-detected-faces-hw", |
| exynos_camera->max_detected_faces); |
| |
| // 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"); |
| } |
| |
| // AE lock |
| |
| if (exynos_camera->config->presets[id].params.auto_exposure_lock_supported == 1) { |
| exynos_param_string_set(exynos_camera, "auto-exposure-lock-supported", "true"); |
| |
| if (exynos_camera->config->presets[id].params.auto_exposure_lock) |
| exynos_param_string_set(exynos_camera, "auto-exposure-lock", "true"); |
| else |
| exynos_param_string_set(exynos_camera, "auto-exposure-lock", "false"); |
| } |
| |
| // AWB lock |
| |
| if (exynos_camera->config->presets[id].params.auto_white_balance_lock_supported == 1) { |
| exynos_param_string_set(exynos_camera, "auto-whitebalance-lock-supported", "true"); |
| |
| if (exynos_camera->config->presets[id].params.auto_white_balance_lock) |
| exynos_param_string_set(exynos_camera, "auto-whitebalance-lock", "true"); |
| else |
| exynos_param_string_set(exynos_camera, "auto-whitebalance-lock", "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); |
| |
| // Antibanding |
| |
| exynos_param_string_set(exynos_camera, "antibanding", |
| exynos_camera->config->presets[id].params.antibanding); |
| exynos_param_string_set(exynos_camera, "antibanding-values", |
| exynos_camera->config->presets[id].params.antibanding_values); |
| |
| // 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); |
| |
| // Image stabilization (Anti-shake) |
| |
| exynos_param_string_set(exynos_camera, "image-stabilization", |
| exynos_camera->config->presets[id].params.image_stabilization); |
| exynos_param_string_set(exynos_camera, "image-stabilization-values", |
| exynos_camera->config->presets[id].params.image_stabilization_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, 1); |
| if (rc < 0) { |
| ALOGE("%s: Unable to apply params", __func__); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int validate_focus_areas(int l, int t, int r, int b, int w) { |
| if (!(l || r || t || b || w)) { |
| // All zeros is a valid area |
| return 0; |
| } |
| |
| // If existing, a focus area must be contained between -1000 and 1000, |
| // on both dimensions |
| if (l < -1000 || t < -1000 || r > 1000 || b > 1000) { |
| return -EINVAL; |
| } |
| // No superimposed or reverted edges |
| if (l >= r || t >= b) { |
| return -EINVAL; |
| } |
| // If there's an area defined, weight must be positive and up to 1000 |
| if ((l !=0 || r !=0) && (w < 1 || w > 1000)) { |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| int exynos_camera_params_apply(struct exynos_camera *exynos_camera, int force) |
| { |
| 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; |
| 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 fimc_is_mode = 0; |
| |
| char *focus_mode_string; |
| int focus_mode = FOCUS_MODE_DEFAULT; |
| char *focus_areas_string; |
| int focus_left, focus_top, focus_right, focus_bottom, focus_weight; |
| int focus_x; |
| int focus_y; |
| |
| char *zoom_supported_string; |
| int zoom, max_zoom; |
| |
| char *ae_lock_supported_string; |
| char *ae_lock_string; |
| int ae_lock = 0; |
| |
| char *awb_lock_supported_string; |
| char *awb_lock_string; |
| int awb_lock = 0; |
| int aeawb = 0; |
| |
| char *flash_mode_string; |
| int flash_mode = 0; |
| |
| int exposure_compensation; |
| int min_exposure_compensation; |
| int max_exposure_compensation; |
| |
| char *antibanding_string; |
| int antibanding; |
| |
| char *whitebalance_string; |
| int whitebalance; |
| |
| char *scene_mode_string; |
| int scene_mode; |
| |
| char *effect_string; |
| int effect; |
| |
| char *iso_string; |
| int iso; |
| |
| char *image_stabilization_string; |
| int image_stabilization; |
| |
| int w, h; |
| char *k; |
| int rc; |
| |
| if (exynos_camera == NULL) |
| return -EINVAL; |
| |
| // 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_height < 0) { |
| char reset_preview[128]; |
| sprintf(reset_preview, "%dx%d", exynos_camera->preview_width, exynos_camera->preview_height); |
| exynos_param_string_set(exynos_camera, "preview-size", |
| reset_preview); |
| return -EINVAL; |
| } |
| 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; |
| } else if (strcmp(preview_format_string, "yuv420p") == 0) { |
| preview_format = V4L2_PIX_FMT_YUV420; |
| } else if (strcmp(preview_format_string, "rgb565") == 0) { |
| preview_format = V4L2_PIX_FMT_RGB565; |
| } else if (strcmp(preview_format_string, "rgb8888") == 0) { |
| preview_format = V4L2_PIX_FMT_RGB32; |
| } else { |
| ALOGE("%s: Unsupported preview format: %s", __func__, preview_format_string); |
| preview_format = V4L2_PIX_FMT_NV21; |
| } |
| |
| if (preview_format != exynos_camera->preview_format) |
| exynos_camera->preview_format = preview_format; |
| } |
| |
| 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_height != 0 && (picture_width != exynos_camera->picture_width || picture_height != exynos_camera->picture_height)) { |
| exynos_camera->picture_width = picture_width; |
| exynos_camera->picture_height = picture_height; |
| |
| if (exynos_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED) { |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_JPEG_RESOLUTION, (picture_width & 0xffff) << 16 | (picture_height & 0xffff)); |
| if (rc < 0) |
| ALOGE("%s: Unablet to set jpeg resolution", __func__); |
| } |
| } |
| } |
| |
| 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 { |
| ALOGE("%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) |
| ALOGE("%s: Unable to set jpeg quality", __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 { |
| ALOGE("%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; |
| |
| if (exynos_camera->camera_fimc_is) |
| fimc_is_mode = IS_MODE_PREVIEW_VIDEO; |
| } else { |
| camera_sensor_mode = SENSOR_CAMERA; |
| |
| if (exynos_camera->camera_fimc_is) |
| fimc_is_mode = IS_MODE_PREVIEW_STILL; |
| } |
| |
| // 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) |
| ALOGE("%s: Unable to set sensor mode", __func__); |
| } |
| |
| if (exynos_camera->camera_fimc_is && fimc_is_mode != exynos_camera->fimc_is_mode) { |
| exynos_camera->fimc_is_mode = fimc_is_mode; |
| |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_IS_S_FORMAT_SCENARIO, exynos_camera->fimc_is_mode); |
| if (rc < 0) |
| ALOGE("%s: Unable to set FIMC-IS scenario", __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_weight = 0; |
| |
| rc = sscanf(focus_areas_string, "(%d,%d,%d,%d,%d)", |
| &focus_left, &focus_top, &focus_right, &focus_bottom, &focus_weight); |
| if (rc != 5) { |
| ALOGE("%s: Unable to scan focus areas", __func__); |
| } else if (validate_focus_areas(focus_left, focus_top, focus_right, focus_bottom, focus_weight) != 0 || strstr(focus_areas_string, "),(")) { |
| exynos_param_string_set(exynos_camera, "focus-areas", |
| exynos_camera->raw_focus_areas); |
| return -EINVAL; |
| } else if ((focus_left != 0 || focus_right != 0) && (focus_top != 0 || focus_bottom != 0)) { |
| sprintf(exynos_camera->raw_focus_areas,"%s",focus_areas_string); |
| 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) |
| ALOGE("%s: Unable to set object x position", __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) |
| ALOGE("%s: Unable to set object y position", __func__); |
| } |
| |
| /* After taking a picture, focus-areas is reseted by stock camera app to the center of the screen */ |
| if (! ( (focus_x == (preview_width / 2)) && (focus_y == (preview_height / 2)) )) { |
| //ALOGV("%s focus_mode changed to %d due to focus-areas='%s'", __func__, focus_mode, focus_areas_string); |
| focus_mode = FOCUS_MODE_TOUCH; |
| } |
| } |
| |
| } |
| |
| // 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) |
| ALOGE("%s: Unable to set camera zoom", __func__); |
| } else if (zoom > max_zoom) { |
| exynos_param_int_set(exynos_camera, "zoom", max_zoom); |
| return -EINVAL; |
| } |
| |
| } |
| |
| // AE lock |
| |
| ae_lock_supported_string = exynos_param_string_get(exynos_camera, "auto-exposure-lock-supported"); |
| ae_lock_string = exynos_param_string_get(exynos_camera, "auto-exposure-lock"); |
| if (ae_lock_supported_string != NULL && ae_lock_string != NULL && strcmp(ae_lock_supported_string, "true") == 0 && strcmp(ae_lock_string, "true") == 0) |
| ae_lock = 1; |
| else |
| ae_lock = 0; |
| |
| // AWB lock |
| |
| awb_lock_supported_string = exynos_param_string_get(exynos_camera, "auto-whitebalance-lock-supported"); |
| awb_lock_string = exynos_param_string_get(exynos_camera, "auto-whitebalance-lock"); |
| if (awb_lock_supported_string != NULL && awb_lock_string != NULL && strcmp(awb_lock_supported_string, "true") == 0 && strcmp(awb_lock_string, "true") == 0) |
| awb_lock = 1; |
| else |
| awb_lock = 0; |
| |
| // 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; |
| flash_mode = FLASH_MODE_AUTO; |
| } 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, "action") == 0) |
| scene_mode = SCENE_MODE_SPORTS; |
| else if (strcmp(scene_mode_string, "party") == 0) { |
| scene_mode = SCENE_MODE_PARTY_INDOOR; |
| flash_mode = FLASH_MODE_AUTO; |
| } 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 if (strcmp(scene_mode_string, "high-sensitivity") == 0) |
| scene_mode = SCENE_MODE_LOW_LIGHT; |
| 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) |
| ALOGE("%s: Unable to set scene mode", __func__); |
| } |
| |
| if (scene_mode != SCENE_MODE_NONE && !flash_mode && focus_mode == FOCUS_MODE_DEFAULT) { |
| flash_mode = FLASH_MODE_OFF; |
| focus_mode = FOCUS_MODE_AUTO; |
| } |
| } |
| |
| // Flash |
| |
| flash_mode_string = exynos_param_string_get(exynos_camera, "flash-mode"); |
| if (flash_mode_string != NULL) { |
| if (flash_mode == 0) { |
| 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 { |
| exynos_param_string_set(exynos_camera, "flash-mode", |
| exynos_camera->raw_flash_mode); |
| return -EINVAL; |
| } |
| } |
| |
| if (flash_mode != exynos_camera->flash_mode || force) { |
| exynos_camera->flash_mode = flash_mode; |
| sprintf(exynos_camera->raw_flash_mode, "%s", flash_mode_string); |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_FLASH_MODE, flash_mode); |
| if (rc < 0) |
| ALOGE("%s:Unable to set flash mode", __func__); |
| } |
| } |
| |
| // Lock Auto Exposure and White Balance only when Flash is OFF |
| if ((ae_lock != exynos_camera->ae_lock || awb_lock != exynos_camera->awb_lock || force) && |
| exynos_camera->flash_mode == FLASH_MODE_OFF) { |
| exynos_camera->ae_lock = ae_lock; |
| exynos_camera->awb_lock = awb_lock; |
| aeawb = (ae_lock ? 0x1 : 0x0) | (awb_lock ? 0x2 : 0x0); |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_AEAWB_LOCK_UNLOCK, aeawb); |
| if (rc < 0) |
| ALOGE("%s: Unable to set AEAWB lock", __func__); |
| } |
| |
| focus_mode_string = exynos_param_string_get(exynos_camera, "focus-mode"); |
| if (focus_mode_string != NULL) { |
| if (focus_mode == FOCUS_MODE_DEFAULT) { |
| 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_VIDEO; |
| else if (strcmp(focus_mode_string, "continuous-picture") == 0) |
| focus_mode = FOCUS_MODE_CONTINOUS_PICTURE; |
| else { |
| exynos_param_string_set(exynos_camera, "focus-mode", |
| exynos_camera->raw_focus_mode); |
| return -EINVAL; |
| } |
| } |
| |
| 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) |
| ALOGE("%s: Unable to set focus mode", __func__); |
| } |
| |
| exynos_camera->focus_mode = focus_mode; |
| sprintf(exynos_camera->raw_focus_mode, "%s", focus_mode_string); |
| } |
| |
| // 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) |
| ALOGE("%s: Unable to set exposure", __func__); |
| } |
| |
| // Antibanding |
| |
| antibanding_string = exynos_param_string_get(exynos_camera, "antibanding"); |
| if (antibanding_string != NULL) { |
| if (strcmp(antibanding_string, "auto") == 0) |
| antibanding = ANTI_BANDING_AUTO; |
| else if (strcmp(antibanding_string, "50hz") == 0) |
| antibanding = ANTI_BANDING_50HZ; |
| else if (strcmp(antibanding_string, "60hz") == 0) |
| antibanding = ANTI_BANDING_60HZ; |
| else if (strcmp(antibanding_string, "off") == 0) |
| antibanding = ANTI_BANDING_OFF; |
| else |
| antibanding = ANTI_BANDING_AUTO; |
| |
| if (antibanding != exynos_camera->antibanding || force) { |
| exynos_camera->antibanding = antibanding; |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_ANTI_BANDING, antibanding); |
| if (rc < 0) |
| ALOGE("%s: Unable to set antibanding", __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) |
| ALOGE("%s: Unable to set whitebalance", __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; |
| if (strcmp(effect_string, "none") == 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 if (strcmp(effect_string, "solarize") == 0) |
| effect = IMAGE_EFFECT_SOLARIZE; |
| else if (strcmp(effect_string, "posterize") == 0) |
| effect = IMAGE_EFFECT_POSTERIZE; |
| else if (strcmp(effect_string, "washed") == 0) |
| effect = IMAGE_EFFECT_WASHED; |
| else if (strcmp(effect_string, "sketch") == 0) |
| effect = IMAGE_EFFECT_SKETCH; |
| else if (strcmp(effect_string, "vintage-warm") == 0) |
| effect = IMAGE_EFFECT_VINTAGE_WARM; |
| else if (strcmp(effect_string, "vintage-cold") == 0) |
| effect = IMAGE_EFFECT_VINTAGE_COLD; |
| else if (strcmp(effect_string, "point-blue") == 0) |
| effect = IMAGE_EFFECT_POINT_BLUE; |
| else if (strcmp(effect_string, "point-red-yellow") == 0) |
| effect = IMAGE_EFFECT_POINT_RED_YELLOW; |
| else if (strcmp(effect_string, "point-green") == 0) |
| effect = IMAGE_EFFECT_POINT_GREEN; |
| 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) |
| ALOGE("%s: Unable to set effect", __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) |
| ALOGE("%s: Unable to set iso", __func__); |
| } |
| } |
| |
| // Image stabilization (Anti-shake) |
| |
| image_stabilization_string = exynos_param_string_get(exynos_camera, "image-stabilization"); |
| if (image_stabilization_string != NULL) { |
| if (strcmp(image_stabilization_string, "on") == 0) |
| image_stabilization = ANTI_SHAKE_STILL_ON; |
| else |
| image_stabilization = ANTI_SHAKE_OFF; |
| |
| if (image_stabilization != exynos_camera->image_stabilization || force) { |
| exynos_camera->image_stabilization = image_stabilization; |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_ANTI_SHAKE, image_stabilization); |
| if (rc < 0) |
| ALOGE("%s: Unable to set image-stabilization", __func__); |
| } |
| } |
| ALOGD("%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; |
| } |
| |
| // Capture |
| |
| int s5c73m3_interleaved_decode(struct exynos_camera *exynos_camera, void *data, int size, |
| void *yuv_data, int *yuv_size, int yuv_width, int yuv_height, |
| void *jpeg_data, int *jpeg_size, int *decoded, int *auto_focus_result, |
| struct exynos_exif *exif) |
| { |
| exif_attribute_t *attributes; |
| camera_face_t caface[exynos_camera->max_detected_faces]; |
| int yuv_length; |
| int jpeg_length; |
| int num_detected_faces; |
| int face; |
| unsigned char *yuv_p; |
| unsigned char *jpeg_p; |
| unsigned char *data_p; |
| unsigned int *offset_p; |
| unsigned int pointers_array_offset; |
| unsigned int pointers_array_size; |
| unsigned int interleaved_size; |
| unsigned char s5c73m3_auto_focus_result; |
| unsigned int yuv_offset_last; |
| unsigned int yuv_offset; |
| unsigned int yuv_line_size; |
| unsigned short *jpeg_start_p; |
| int gap; |
| unsigned int i; |
| |
| if (data == NULL || size <= 0 || yuv_data == NULL || yuv_size == NULL || yuv_width <= 0 || yuv_height <= 0 || jpeg_data == NULL || jpeg_size == NULL || decoded == NULL || auto_focus_result == NULL) |
| return -EINVAL; |
| |
| yuv_length = 0; |
| jpeg_length = 0; |
| |
| data_p = (unsigned char *) data; |
| data_p += size - 0x1000; // End of the first plane (interleaved buffer) |
| data_p += 4046; // Experimental offset for decoded |
| |
| *decoded = (int) *data_p; |
| |
| data_p = (unsigned char *) data; |
| data_p += size - 0x1000; // End of the first plane (interleaved buffer) |
| data_p += 50; // Experimental offset for auto-focus result |
| |
| *auto_focus_result = (int) *data_p; |
| |
| data_p = (unsigned char *) data; |
| data_p += size - 0x1000; // End of the first plane (interleaved buffer) |
| data_p += 4084; // Experimental offset for interleaved size |
| |
| // Read the pointers array offset |
| offset_p = (unsigned int *) data_p; |
| pointers_array_offset = BIG2LITTLE_ENDIAN(*offset_p); |
| interleaved_size = pointers_array_offset; |
| |
| // Read the pointers array size, it should be 4 * yuv_height |
| data_p += sizeof(pointers_array_offset); |
| offset_p = (unsigned int *) data_p; |
| pointers_array_size = BIG2LITTLE_ENDIAN(*offset_p); |
| |
| // FaceDetection Information |
| data_p = (unsigned char *) data; |
| data_p += size - 0x1000; // End of the first plane (interleaved buffer) |
| data_p += 108; //Number of Faces Detected |
| |
| num_detected_faces = (int) *data_p; |
| data_p += 2; //Start of Face Detection Info |
| |
| exynos_camera->mFaceData.faces = caface; |
| if (num_detected_faces > 0 && num_detected_faces < exynos_camera->max_detected_faces) |
| { |
| for (face = 0; face < num_detected_faces; face++) { |
| exynos_camera->mFaceData.faces[face].rect[0] = (short)(data_p[1] << 8) + data_p[0]; |
| data_p += 2; |
| exynos_camera->mFaceData.faces[face].rect[1] = (short)(data_p[1] << 8) + data_p[0]; |
| data_p += 2; |
| exynos_camera->mFaceData.faces[face].rect[2] = (short)(data_p[1] << 8) + data_p[0]; |
| data_p += 2; |
| exynos_camera->mFaceData.faces[face].rect[3] = (short)(data_p[1] << 8) + data_p[0]; |
| data_p += 2; |
| exynos_camera->mFaceData.faces[face].score = (short)(data_p[1] << 8) + data_p[0]; |
| data_p += 2; |
| exynos_camera->mFaceData.faces[face].id = (short)(data_p[1] << 8) + data_p[0]; |
| data_p += 2; |
| } |
| } |
| exynos_camera->mFaceData.number_of_faces = num_detected_faces; |
| |
| if (!*decoded) |
| return 0; |
| |
| attributes = &exif->attributes; |
| |
| //Extract the EXIF from the Metadata |
| //Flash |
| data_p = (unsigned char *) data; |
| data_p += size - 0x1000; // End of the first plane (interleaved buffer) |
| data_p += 4; // EXIF Flash Offset |
| attributes->flash = (int) data_p[0]; |
| |
| //ISO |
| data_p += 4; // EXIF ISO Offset |
| attributes->iso_speed_rating = (data_p[1] << 8) + data_p[0]; |
| |
| //Exposure |
| data_p += 4; // EXIF Exposure Offset |
| attributes->brightness.num = (int) data_p[0]; |
| |
| //Exposure Bias |
| data_p += 4; // EXIF Exposure Bias Offset |
| attributes->exposure_bias.num = (data_p[1] << 8) + data_p[0]; |
| |
| //Exposure Time |
| data_p += 8; // EXIF Exposure Time Offset |
| attributes->exposure_time.den = (data_p[1] << 8) + data_p[0]; |
| |
| |
| ALOGD("%s: Interleaved pointers array is at offset 0x%x, 0x%x bytes long\n", __func__, pointers_array_offset, pointers_array_size); |
| |
| if ((int) pointers_array_offset > size || (int) pointers_array_size > size || (int) pointers_array_size < yuv_height * (int) sizeof(unsigned int)) { |
| ALOGE("%s: Invalid informations", __func__); |
| return -1; |
| } |
| |
| data_p = (unsigned char *) data; |
| data_p += pointers_array_offset; |
| yuv_p = (unsigned char *) yuv_data; |
| jpeg_p = (unsigned char *) jpeg_data; |
| jpeg_start_p = NULL; |
| |
| yuv_line_size = yuv_width * 2; |
| yuv_offset_last = 0; |
| yuv_offset = 0; |
| i = 0; |
| |
| while (i < pointers_array_size) { |
| offset_p = (unsigned int *) data_p; |
| yuv_offset = BIG2LITTLE_ENDIAN(*offset_p); |
| |
| if (yuv_offset > size - yuv_line_size) |
| return -1; |
| |
| gap = yuv_offset - yuv_offset_last - yuv_line_size; |
| |
| if (gap > 0) { |
| data_p = (unsigned char *) data + yuv_offset_last + yuv_line_size; |
| |
| if (jpeg_start_p == NULL) { |
| jpeg_start_p = (unsigned short *) data_p; |
| if (*jpeg_start_p != 0xd8ff) { |
| ALOGE("%s: Invalid jpeg start", __func__); |
| return -1; |
| } |
| } |
| |
| memcpy(jpeg_p, data_p, gap); |
| jpeg_p += gap; |
| jpeg_length += gap; |
| } |
| |
| yuv_offset_last = yuv_offset; |
| |
| data_p = (unsigned char *) data + yuv_offset; |
| memcpy(yuv_p, data_p, yuv_line_size); |
| yuv_p += yuv_line_size; |
| yuv_length += yuv_line_size; |
| |
| data_p = (unsigned char *) offset_p; |
| data_p += sizeof(yuv_offset); |
| i += sizeof(yuv_offset); |
| } |
| |
| gap = interleaved_size - yuv_offset_last - yuv_line_size; |
| |
| if (gap > 0) { |
| data_p = (unsigned char *) data + yuv_offset_last + yuv_line_size; |
| |
| memcpy(jpeg_p, data_p, gap); |
| jpeg_p += gap; |
| jpeg_length += gap; |
| } |
| |
| *yuv_size = yuv_length; |
| *jpeg_size = jpeg_length; |
| |
| return 0; |
| } |
| |
| int exynos_camera_capture(struct exynos_camera *exynos_camera) |
| { |
| struct exynos_camera_capture_listener *listener; |
| struct exynos_camera_buffer *buffers = NULL; |
| struct exynos_camera_buffer *buffer; |
| struct list_head *list; |
| int width, height, format; |
| int yuv_length, jpeg_length; |
| int jpeg_offset, jpeg_size; |
| int jpeg_thumbnail_offset, jpeg_thumbnail_size; |
| int buffers_count; |
| int buffer_length; |
| int auto_focus_result; |
| int current_af; |
| int decoded; |
| int busy; |
| void *pointer; |
| int address; |
| int offset; |
| int index; |
| int rc; |
| |
| if (exynos_camera == NULL) |
| return -EINVAL; |
| |
| width = exynos_camera->capture_width; |
| height = exynos_camera->capture_height; |
| format = exynos_camera->capture_format; |
| |
| buffers_count = exynos_camera->capture_buffers_count; |
| buffer_length = exynos_camera->capture_buffer_length; |
| |
| // V4L2 |
| |
| index = exynos_v4l2_dqbuf_cap(exynos_camera, 0); |
| if (index < 0 || index >= buffers_count) { |
| rc = exynos_v4l2_poll(exynos_camera, 0); |
| if (rc < 0) { |
| ALOGE("%s Unable to poll", __func__); |
| goto error; |
| } else if (rc == 0) { |
| // Timeout |
| rc = 0; |
| goto complete; |
| } |
| |
| index = exynos_v4l2_dqbuf_cap(exynos_camera, 0); |
| if (index < 0 || index >= buffers_count) { |
| ALOGE("%s: Unable to dequeue buffer", __func__); |
| goto error; |
| } |
| } |
| |
| exynos_camera->capture_memory_index = index; |
| |
| address = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_PADDR_Y, index); |
| if (address == 0 || address == (int) 0xffffffff) { |
| ALOGE("%s: Unable to get address", __func__); |
| goto error; |
| } |
| |
| offset = address - exynos_camera->capture_memory_address; |
| if (offset != index * buffer_length) |
| ALOGE("%s: Inconsistent memory offset (0x%x/0x%x)", __func__, offset, index * buffer_length); |
| |
| pointer = (void *) ((unsigned char *) exynos_camera->capture_memory->data + offset); |
| |
| // Buffers |
| |
| switch (format) { |
| case V4L2_PIX_FMT_INTERLEAVED: |
| yuv_length = jpeg_length = 0; |
| auto_focus_result = decoded = 0; |
| |
| rc = s5c73m3_interleaved_decode(exynos_camera, pointer, buffer_length, exynos_camera->capture_yuv_buffer, &yuv_length, width, height, exynos_camera->capture_jpeg_buffer, &jpeg_length, &decoded, &auto_focus_result, &exynos_camera->exif); |
| if (rc < 0) { |
| ALOGE("%s: Unable to decode S5C73M3 interleaved", __func__); |
| goto error; |
| } |
| |
| // AutoFocus |
| switch (auto_focus_result) { |
| case S5C73M3_CAF_STATUS_FOCUSING: |
| case S5C73M3_CAF_STATUS_FIND_SEARCHING_DIR: |
| case S5C73M3_AF_STATUS_FOCUSING: |
| current_af = CAMERA_AF_STATUS_IN_PROGRESS; |
| break; |
| case S5C73M3_CAF_STATUS_FOCUSED: |
| case S5C73M3_AF_STATUS_FOCUSED: |
| current_af = CAMERA_AF_STATUS_SUCCESS; |
| break; |
| case S5C73M3_CAF_STATUS_UNFOCUSED: |
| case S5C73M3_AF_STATUS_UNFOCUSED: |
| current_af = CAMERA_AF_STATUS_FAIL; |
| break; |
| case S5C73M3_AF_STATUS_INVALID: |
| default: |
| current_af = CAMERA_AF_STATUS_RESTART; |
| } |
| |
| if (exynos_camera->auto_focus_enabled) { |
| rc = exynos_camera_auto_focus(exynos_camera, current_af); |
| if (rc < 0) { |
| ALOGE("%s: Unable to auto focus", __func__); |
| goto error; |
| } |
| } |
| |
| // CAF |
| switch (auto_focus_result) { |
| case S5C73M3_CAF_STATUS_FOCUSING: |
| case S5C73M3_CAF_STATUS_FIND_SEARCHING_DIR: |
| current_af = CAMERA_AF_STATUS_IN_PROGRESS; |
| break; |
| case S5C73M3_CAF_STATUS_FOCUSED: |
| current_af = CAMERA_AF_STATUS_SUCCESS; |
| break; |
| case S5C73M3_CAF_STATUS_UNFOCUSED: |
| default: |
| current_af = CAMERA_AF_STATUS_RESTART; |
| } |
| |
| rc = exynos_camera_continuous_auto_focus(exynos_camera, current_af); |
| if (rc < 0) { |
| ALOGE("%s: Unable to continuous auto focus", __func__); |
| goto error; |
| } |
| |
| if (!decoded) { |
| buffers_count = 1; |
| buffers = (struct exynos_camera_buffer *) calloc(buffers_count, sizeof(struct exynos_camera_buffer)); |
| |
| buffer = buffers; |
| |
| buffer->pointer = pointer; |
| buffer->address = address; |
| buffer->length = exynos_camera_buffer_length(width, height, V4L2_PIX_FMT_UYVY); |
| buffer->width = width; |
| buffer->height = height; |
| buffer->format = V4L2_PIX_FMT_UYVY; |
| } else { |
| buffers_count = 2; |
| buffers = (struct exynos_camera_buffer *) calloc(buffers_count, sizeof(struct exynos_camera_buffer)); |
| |
| buffer = buffers; |
| |
| memcpy(pointer, exynos_camera->capture_yuv_buffer, yuv_length); |
| |
| buffer->pointer = pointer; |
| buffer->address = address; |
| buffer->length = yuv_length; |
| buffer->width = width; |
| buffer->height = height; |
| buffer->format = V4L2_PIX_FMT_UYVY; |
| |
| pointer = (void *) ((unsigned char *) pointer + yuv_length); |
| address += yuv_length; |
| buffer = (struct exynos_camera_buffer *) ((unsigned char *) buffer + sizeof(struct exynos_camera_buffer)); |
| |
| memcpy(pointer, exynos_camera->capture_jpeg_buffer, jpeg_length); |
| |
| buffer->pointer = pointer; |
| buffer->address = address; |
| buffer->length = jpeg_length; |
| buffer->width = exynos_camera->picture_width; |
| buffer->height = exynos_camera->picture_height; |
| buffer->format = V4L2_PIX_FMT_JPEG; |
| |
| exynos_camera->capture_hybrid = 0; |
| |
| exynos_exif_create(exynos_camera, &exynos_camera->exif); |
| } |
| break; |
| case V4L2_PIX_FMT_JPEG: |
| jpeg_size = jpeg_offset = 0; |
| jpeg_thumbnail_size = jpeg_thumbnail_offset = 0; |
| |
| rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_MAIN_SIZE, &jpeg_size); |
| if (rc < 0 || jpeg_size <= 0) { |
| ALOGE("%s: Unable to get jpeg size", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_MAIN_OFFSET, &jpeg_offset); |
| if (rc < 0) { |
| ALOGE("%s: Unable to get jpeg offset", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_THUMB_SIZE, &jpeg_thumbnail_size); |
| if (rc < 0 || jpeg_thumbnail_size <= 0) { |
| ALOGE("%s: Unable to get jpeg thumbnail size", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_g_ctrl(exynos_camera, 0, V4L2_CID_CAM_JPEG_THUMB_OFFSET, &jpeg_thumbnail_offset); |
| if (rc < 0) { |
| ALOGE("%s: Unable to get jpeg thumbnail offset", __func__); |
| goto error; |
| } |
| |
| buffers_count = 2; |
| buffers = (struct exynos_camera_buffer *) calloc(buffers_count, sizeof(struct exynos_camera_buffer)); |
| |
| buffer = buffers; |
| |
| buffer->pointer = (void *) ((unsigned char *) pointer + jpeg_offset); |
| buffer->address = address + jpeg_offset; |
| buffer->length = jpeg_size; |
| buffer->width = exynos_camera->picture_width; |
| buffer->height = exynos_camera->picture_height; |
| buffer->format = V4L2_PIX_FMT_JPEG; |
| |
| buffer = (struct exynos_camera_buffer *) ((unsigned char *) buffer + sizeof(struct exynos_camera_buffer)); |
| |
| buffer->pointer = (void *) ((unsigned char *) pointer + jpeg_thumbnail_offset); |
| buffer->address = address + jpeg_thumbnail_offset; |
| buffer->length = jpeg_thumbnail_size; |
| buffer->width = exynos_camera->jpeg_thumbnail_width; |
| buffer->height = exynos_camera->jpeg_thumbnail_height; |
| buffer->format = V4L2_PIX_FMT_JPEG; |
| |
| break; |
| default: |
| buffers_count = 1; |
| buffers = (struct exynos_camera_buffer *) calloc(buffers_count, sizeof(struct exynos_camera_buffer)); |
| |
| buffer = buffers; |
| |
| buffer->pointer = pointer; |
| buffer->address = address; |
| buffer->length = buffer_length; |
| buffer->width = width; |
| buffer->height = height; |
| buffer->format = format; |
| break; |
| } |
| |
| // Listeners |
| |
| list = (struct list_head *) exynos_camera->capture_listeners; |
| while (list != NULL) { |
| listener = (struct exynos_camera_capture_listener *) list; |
| |
| if (listener->callback == NULL) |
| goto list_continue_callback; |
| |
| /* |
| * Callback must never call a capture-locked function or it will |
| * block. Hence, do not unregister the listener in callback. |
| */ |
| |
| listener->callback(exynos_camera, buffers, buffers_count); |
| |
| list_continue_callback: |
| list = list->next; |
| } |
| |
| do { |
| busy = 0; |
| |
| list = (struct list_head *) exynos_camera->capture_listeners; |
| while (list != NULL) { |
| listener = (struct exynos_camera_capture_listener *) list; |
| |
| if (listener->callback == NULL) |
| goto list_continue_busy; |
| |
| busy |= listener->busy; |
| |
| list_continue_busy: |
| list = list->next; |
| } |
| |
| if (busy) |
| usleep(1000); |
| } while (busy); |
| |
| rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, index); |
| if (rc < 0) { |
| ALOGE("%s: Unable to queue buffer", __func__); |
| goto error; |
| } |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| rc = -1; |
| |
| complete: |
| if (buffers != NULL) |
| free(buffers); |
| |
| return rc; |
| } |
| |
| void *exynos_camera_capture_thread(void *data) |
| { |
| struct exynos_camera *exynos_camera; |
| int rc; |
| |
| if (data == NULL) |
| return NULL; |
| |
| exynos_camera = (struct exynos_camera *) data; |
| |
| ALOGE("%s: Starting thread", __func__); |
| exynos_camera->capture_thread_running = 1; |
| |
| while (exynos_camera->capture_thread_enabled) { |
| pthread_mutex_lock(&exynos_camera->capture_lock_mutex); |
| |
| while (exynos_camera->capture_enabled) { |
| pthread_mutex_lock(&exynos_camera->capture_mutex); |
| |
| if (!exynos_camera->capture_enabled) { |
| pthread_mutex_unlock(&exynos_camera->capture_mutex); |
| break; |
| } |
| |
| rc = exynos_camera_capture(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to capture", __func__); |
| pthread_mutex_unlock(&exynos_camera->capture_mutex); |
| break; |
| } |
| |
| pthread_mutex_unlock(&exynos_camera->capture_mutex); |
| |
| // Wait a bit to let others lock the mutex if they need to |
| usleep(10); |
| } |
| } |
| |
| exynos_camera->capture_thread_running = 0; |
| ALOGE("%s: Exiting thread", __func__); |
| |
| return NULL; |
| } |
| |
| int exynos_camera_capture_thread_start(struct exynos_camera *exynos_camera) |
| { |
| pthread_attr_t thread_attr; |
| int rc; |
| |
| if (exynos_camera == NULL) |
| return -EINVAL; |
| |
| ALOGD("%s()", __func__); |
| |
| if (exynos_camera->capture_thread_enabled) { |
| ALOGE("Capture thread was already started!"); |
| return -1; |
| } |
| |
| pthread_mutex_init(&exynos_camera->capture_mutex, NULL); |
| pthread_mutex_init(&exynos_camera->capture_lock_mutex, NULL); |
| |
| // Initial lock |
| pthread_mutex_lock(&exynos_camera->capture_lock_mutex); |
| |
| pthread_attr_init(&thread_attr); |
| pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); |
| |
| exynos_camera->capture_thread_enabled = 1; |
| |
| rc = pthread_create(&exynos_camera->capture_thread, &thread_attr, exynos_camera_capture_thread, (void *) exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to create thread", __func__); |
| goto error; |
| } |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| pthread_mutex_destroy(&exynos_camera->capture_mutex); |
| pthread_mutex_destroy(&exynos_camera->capture_lock_mutex); |
| |
| rc = -1; |
| |
| complete: |
| return rc; |
| } |
| |
| void exynos_camera_capture_thread_stop(struct exynos_camera *exynos_camera) |
| { |
| int i; |
| |
| if (exynos_camera == NULL) |
| return; |
| |
| ALOGD("%s()", __func__); |
| |
| if (!exynos_camera->capture_thread_enabled) { |
| ALOGE("Capture thread was already stopped!"); |
| return; |
| } |
| |
| exynos_camera->capture_enabled = 0; |
| exynos_camera->capture_thread_enabled = 0; |
| |
| pthread_mutex_unlock(&exynos_camera->capture_lock_mutex); |
| |
| // Wait for the thread to end |
| i = 0; |
| while (exynos_camera->capture_thread_running) { |
| if (i++ > 10000) { |
| ALOGE("Capture thread is taking too long to end, something is going wrong"); |
| break; |
| } |
| usleep(100); |
| } |
| |
| if (exynos_camera->capture_enabled) { |
| pthread_mutex_lock(&exynos_camera->capture_mutex); |
| exynos_camera_capture_stop(exynos_camera); |
| pthread_mutex_unlock(&exynos_camera->capture_mutex); |
| } |
| |
| pthread_mutex_destroy(&exynos_camera->capture_mutex); |
| pthread_mutex_destroy(&exynos_camera->capture_lock_mutex); |
| } |
| |
| int exynos_camera_capture_start(struct exynos_camera *exynos_camera) |
| { |
| struct v4l2_streamparm fps_param; |
| int width, height, format; |
| int mbus_width, mbus_height; |
| int buffers_count, buffer_length; |
| camera_memory_t *memory = NULL; |
| int value; |
| int fd; |
| int rc; |
| int i; |
| |
| if (exynos_camera == NULL) |
| return -EINVAL; |
| |
| ALOGD("%s()", __func__); |
| |
| if (exynos_camera->capture_enabled) { |
| ALOGE("Capture was already started!"); |
| return -1; |
| } |
| |
| width = exynos_camera->capture_width; |
| height = exynos_camera->capture_height; |
| format = exynos_camera->capture_format; |
| |
| // V4L2 |
| |
| if (format == V4L2_PIX_FMT_INTERLEAVED) { |
| ALOGD("Enabling hybrid capture"); |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_HYBRID, 1); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set hybrid", __func__); |
| goto error; |
| } |
| } |
| |
| if (exynos_camera->camera_fimc_is) { |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_IS_S_FORMAT_SCENARIO, exynos_camera->fimc_is_mode); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set FIMC-IS scenario", __func__); |
| goto error; |
| } |
| } |
| |
| rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 0, format); |
| if (rc < 0) { |
| ALOGE("%s: Unable to enumerate formats", __func__); |
| goto error; |
| } |
| |
| mbus_width = width; |
| mbus_height = height; |
| |
| if (exynos_camera->camera_mbus_resolutions != NULL) { |
| for (i = 0; i < exynos_camera->camera_mbus_resolutions_count; i++) { |
| if (exynos_camera->camera_mbus_resolutions[i].width == width && exynos_camera->camera_mbus_resolutions[i].height == height) { |
| mbus_width = exynos_camera->camera_mbus_resolutions[i].mbus_width; |
| mbus_height = exynos_camera->camera_mbus_resolutions[i].mbus_height; |
| break; |
| } |
| } |
| } |
| |
| if (exynos_camera->camera_fimc_is) { |
| // Set MBUS width/height/format |
| rc = exynos_v4l2_s_fmt_pix(exynos_camera, 0, V4L2_BUF_TYPE_PRIVATE, mbus_width, mbus_height, format, exynos_camera->fimc_is_mode, V4L2_PIX_FMT_MODE_PREVIEW); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set MBUS capture pixel format", __func__); |
| goto error; |
| } |
| } |
| |
| rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 0, width, height, format, V4L2_PIX_FMT_MODE_PREVIEW); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set capture pixel format", __func__); |
| goto error; |
| } |
| |
| if (!exynos_camera->camera_fimc_is) { |
| // Set MBUS width/height/format |
| rc = exynos_v4l2_s_fmt_pix(exynos_camera, 0, V4L2_BUF_TYPE_PRIVATE, mbus_width, mbus_height, format, V4L2_FIELD_NONE, V4L2_PIX_FMT_MODE_PREVIEW); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set MBUS capture pixel format", __func__); |
| goto error; |
| } |
| } |
| |
| if (format == V4L2_PIX_FMT_INTERLEAVED) |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CACHEABLE, 0); |
| else |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CACHEABLE, 1); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set cacheable", __func__); |
| goto error; |
| } |
| |
| if (exynos_camera->camera_fimc_is) { |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_IS_S_SCENARIO_MODE, exynos_camera->fimc_is_mode); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set FIMC-IS scenario mode", __func__); |
| goto error; |
| } |
| } |
| |
| if (format == V4L2_PIX_FMT_INTERLEAVED) { |
| // This must be set to 1 for interleaved data decoding |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_EMBEDDEDDATA_ENABLE, 1); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set embdedded data enable", __func__); |
| goto error; |
| } |
| } |
| |
| // Let's assume FIMC0 has memory available through mmap |
| |
| for (i = EXYNOS_CAMERA_CAPTURE_BUFFERS_COUNT; i > 0; i--) { |
| rc = exynos_v4l2_reqbufs_cap(exynos_camera, 0, i); |
| if (rc >= 0) |
| break; |
| } |
| |
| if (rc < 0) { |
| ALOGE("%s: Unable to request buffers", __func__); |
| goto error; |
| } |
| |
| buffers_count = rc; |
| ALOGD("Found %d buffers available for capture!", buffers_count); |
| |
| memset(&fps_param, 0, sizeof(fps_param)); |
| fps_param.parm.capture.timeperframe.numerator = 1; |
| fps_param.parm.capture.timeperframe.denominator = exynos_camera->preview_fps; |
| |
| rc = exynos_v4l2_s_parm_cap(exynos_camera, 0, &fps_param); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set fps", __func__); |
| goto error; |
| } |
| |
| for (i = 0; i < buffers_count; i++) { |
| rc = exynos_v4l2_querybuf_cap(exynos_camera, 0, i); |
| if (rc < 0) { |
| ALOGE("%s: Unable to query buffers", __func__); |
| goto error; |
| } |
| } |
| |
| buffer_length = rc; |
| |
| value = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_PADDR_Y, 0); |
| if (value == 0 || value == (int) 0xffffffff) { |
| ALOGE("%s: Unable to get address", __func__); |
| goto error; |
| } |
| |
| exynos_camera->capture_memory_address = value; |
| |
| if (EXYNOS_CAMERA_CALLBACK_DEFINED(request_memory)) { |
| fd = exynos_v4l2_fd(exynos_camera, 0); |
| if (fd < 0) { |
| ALOGE("%s: Unable to get v4l2 fd for id %d", __func__, 0); |
| goto error; |
| } |
| |
| exynos_camera->capture_memory = NULL; |
| |
| memory = exynos_camera->callbacks.request_memory(fd, buffer_length, buffers_count, exynos_camera->callbacks.user); |
| if (memory == NULL || memory->data == NULL || memory->data == MAP_FAILED) { |
| ALOGE("%s: Unable to request memory", __func__); |
| goto error; |
| } |
| |
| exynos_camera->capture_memory = memory; |
| |
| memory = exynos_camera->callbacks.request_memory(-1, 1, 1, exynos_camera->callbacks.user); |
| |
| exynos_camera->face_data = memory; |
| } else { |
| ALOGE("%s: No memory request function!", __func__); |
| goto error; |
| } |
| |
| if (format == V4L2_PIX_FMT_INTERLEAVED) { |
| exynos_camera->capture_yuv_buffer = malloc(buffer_length); |
| exynos_camera->capture_jpeg_buffer = malloc(buffer_length); |
| } |
| |
| // Start EXIF |
| memset(&exynos_camera->exif, 0, sizeof(exynos_camera->exif)); |
| exynos_exif_start(exynos_camera, &exynos_camera->exif); |
| |
| for (i = 0; i < buffers_count; i++) { |
| rc = exynos_v4l2_qbuf_cap(exynos_camera, 0, i); |
| if (rc < 0) { |
| ALOGE("%s: Unable to queue buffer", __func__); |
| goto error; |
| } |
| } |
| |
| exynos_camera->capture_buffers_count = buffers_count; |
| exynos_camera->capture_buffer_length = buffer_length; |
| |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_ROTATION, |
| exynos_camera->camera_rotation); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set rotation", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_HFLIP, |
| exynos_camera->camera_hflip); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set hflip", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_VFLIP, |
| exynos_camera->camera_vflip); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set vflip", __func__); |
| goto error; |
| } |
| |
| rc = exynos_v4l2_streamon_cap(exynos_camera, 0); |
| if (rc < 0) { |
| ALOGE("%s: Unable to start stream", __func__); |
| goto error; |
| } |
| |
| // Few Scene Modes require to be set after stream on |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SCENE_MODE, exynos_camera->scene_mode); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set scene mode", __func__); |
| goto error; |
| } |
| |
| if (exynos_camera->camera_fimc_is) { |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_IS_CMD_FD, IS_FD_COMMAND_START); |
| if (rc < 0) { |
| ALOGE("%s: Unable to start face detection", __func__); |
| goto error; |
| } |
| } |
| |
| exynos_camera->capture_enabled = 1; |
| pthread_mutex_unlock(&exynos_camera->capture_lock_mutex); |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| if (exynos_camera->face_data != NULL && exynos_camera->face_data->release != NULL) { |
| exynos_camera->face_data->release(exynos_camera->face_data); |
| exynos_camera->face_data = NULL; |
| } |
| |
| if (exynos_camera->capture_memory != NULL && exynos_camera->capture_memory->release != NULL) { |
| exynos_camera->capture_memory->release(exynos_camera->capture_memory); |
| exynos_camera->capture_memory = NULL; |
| } |
| |
| rc = -1; |
| |
| complete: |
| return rc; |
| } |
| |
| void exynos_camera_capture_stop(struct exynos_camera *exynos_camera) |
| { |
| int rc; |
| int i; |
| |
| if (exynos_camera == NULL) |
| return; |
| |
| ALOGD("%s()", __func__); |
| |
| if (!exynos_camera->capture_enabled) { |
| ALOGE("Capture was already stopped!"); |
| return; |
| } |
| |
| if (exynos_camera->capture_format == V4L2_PIX_FMT_INTERLEAVED) { |
| ALOGD("Disabling hybrid capture"); |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_HYBRID, 0); |
| if (rc < 0) |
| ALOGE("%s: Unable to set hybrid", __func__); |
| } |
| |
| if (exynos_camera->camera_fimc_is) { |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_IS_CMD_FD, IS_FD_COMMAND_STOP); |
| if (rc < 0) |
| ALOGE("%s: Unable to stop face detection", __func__); |
| } |
| |
| rc = exynos_v4l2_streamoff_cap(exynos_camera, 0); |
| if (rc < 0) { |
| ALOGE("%s: Unable to stop stream", __func__); |
| } |
| |
| if (exynos_camera->face_data != NULL && exynos_camera->face_data->release != NULL) { |
| exynos_camera->face_data->release(exynos_camera->face_data); |
| exynos_camera->face_data = NULL; |
| } |
| |
| if (exynos_camera->capture_memory != NULL && exynos_camera->capture_memory->release != NULL) { |
| exynos_camera->capture_memory->release(exynos_camera->capture_memory); |
| exynos_camera->capture_memory = NULL; |
| } |
| |
| if (exynos_camera->capture_yuv_buffer != NULL) { |
| free(exynos_camera->capture_yuv_buffer); |
| exynos_camera->capture_yuv_buffer = NULL; |
| } |
| |
| if (exynos_camera->capture_jpeg_buffer != NULL) { |
| free(exynos_camera->capture_jpeg_buffer); |
| exynos_camera->capture_jpeg_buffer = NULL; |
| } |
| |
| if (&exynos_camera->exif != NULL) { |
| exynos_exif_stop(exynos_camera, &exynos_camera->exif); |
| free(&exynos_camera->exif); |
| } |
| |
| exynos_camera->capture_enabled = 0; |
| } |
| |
| int exynos_camera_capture_setup(struct exynos_camera *exynos_camera) |
| { |
| struct exynos_camera_capture_listener *listener; |
| struct list_head *list; |
| int width, height, format; |
| int rc; |
| |
| if (exynos_camera == NULL) |
| return -EINVAL; |
| |
| ALOGD("%s()", __func__); |
| |
| // No listener left |
| if (exynos_camera->capture_listeners == NULL && exynos_camera->capture_enabled) { |
| exynos_camera_capture_stop(exynos_camera); |
| return 0; |
| } |
| |
| width = height = format = 0; |
| |
| list = (struct list_head *) exynos_camera->capture_listeners; |
| while (list != NULL) { |
| listener = (struct exynos_camera_capture_listener *) list; |
| |
| // Interleaved format already has the correct width and height for picture set through ioctl |
| if (exynos_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED) |
| if (listener->format == V4L2_PIX_FMT_JPEG || listener->format == V4L2_PIX_FMT_INTERLEAVED) |
| goto list_continue; |
| |
| if (listener->width >= width && listener->height >= height) { |
| width = listener->width; |
| height = listener->height; |
| format = listener->format; |
| } |
| |
| list_continue: |
| list = list->next; |
| } |
| |
| // Override the capture format |
| if (exynos_camera->camera_capture_format) |
| format = exynos_camera->camera_capture_format; |
| |
| // Only picture is listening, but we need some preview size anyway |
| if (format == V4L2_PIX_FMT_INTERLEAVED && (width == 0 || height == 0)) { |
| width = exynos_camera->preview_width; |
| height = exynos_camera->preview_height; |
| } |
| |
| ALOGD("%s: Selected width: %d, height: %d, format: 0x%x", __func__, width, height, format); |
| |
| if (!exynos_camera->capture_enabled) { |
| exynos_camera->capture_width = width; |
| exynos_camera->capture_height = height; |
| exynos_camera->capture_format = format; |
| |
| rc = exynos_camera_capture_start(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to start capture", __func__); |
| return -1; |
| } |
| } else if (exynos_camera->capture_width != width || exynos_camera->capture_height != height || exynos_camera->capture_format != format) { |
| exynos_camera_capture_stop(exynos_camera); |
| |
| exynos_camera->capture_width = width; |
| exynos_camera->capture_height = height; |
| exynos_camera->capture_format = format; |
| |
| rc = exynos_camera_capture_start(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to start capture", __func__); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| struct exynos_camera_capture_listener *exynos_camera_capture_listener_register( |
| struct exynos_camera *exynos_camera, int width, int height, int format, |
| int (*callback)(struct exynos_camera *exynos_camera, struct exynos_camera_buffer *buffers, int buffers_count)) |
| { |
| struct exynos_camera_capture_listener *listener = NULL; |
| struct list_head *list_end; |
| struct list_head *list; |
| int rc; |
| |
| if (exynos_camera == NULL || callback == NULL) |
| return NULL; |
| |
| pthread_mutex_lock(&exynos_camera->capture_mutex); |
| |
| listener = calloc(1, sizeof(struct exynos_camera_capture_listener)); |
| if (listener == NULL) |
| goto error; |
| |
| listener->width = width; |
| listener->height = height; |
| listener->format = format; |
| listener->callback = callback; |
| listener->busy = 0; |
| |
| list_end = (struct list_head *) exynos_camera->capture_listeners; |
| while (list_end != NULL && list_end->next != NULL) |
| list_end = list_end->next; |
| |
| list = (struct list_head *) listener; |
| list_head_insert(list, list_end, NULL); |
| |
| if (exynos_camera->capture_listeners == NULL) |
| exynos_camera->capture_listeners = listener; |
| |
| if (!(exynos_camera->camera_fimc_is && exynos_camera->picture_thread_enabled)) { |
| rc = exynos_camera_capture_setup(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to setup capture", __func__); |
| goto error; |
| } |
| } |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| listener = NULL; |
| |
| complete: |
| pthread_mutex_unlock(&exynos_camera->capture_mutex); |
| |
| return listener; |
| } |
| |
| void exynos_camera_capture_listener_unregister( |
| struct exynos_camera *exynos_camera, |
| struct exynos_camera_capture_listener *listener) |
| { |
| struct list_head *list; |
| int rc; |
| |
| if (exynos_camera == NULL || listener == NULL) |
| return; |
| |
| pthread_mutex_lock(&exynos_camera->capture_mutex); |
| |
| list = (struct list_head *) exynos_camera->capture_listeners; |
| while (list != NULL) { |
| if ((void *) list == (void *) listener) { |
| list_head_remove(list); |
| |
| if ((void *) list == (void *) exynos_camera->capture_listeners) |
| exynos_camera->capture_listeners = (struct exynos_camera_capture_listener *) list->next; |
| |
| memset(listener, 0, sizeof(struct exynos_camera_capture_listener)); |
| free(listener); |
| |
| break; |
| } |
| list_continue: |
| list = list->next; |
| } |
| |
| rc = exynos_camera_capture_setup(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to setup capture", __func__); |
| goto complete; |
| } |
| |
| complete: |
| pthread_mutex_unlock(&exynos_camera->capture_mutex); |
| } |
| |
| // Preview |
| |
| int exynos_camera_preview_output_start(struct exynos_camera *exynos_camera) |
| { |
| struct exynos_v4l2_output *output; |
| int rc; |
| |
| if (exynos_camera == NULL) |
| return -EINVAL; |
| |
| ALOGD("%s()", __func__); |
| |
| if (exynos_camera->preview_output_enabled) { |
| ALOGE("Preview was already started!"); |
| return -1; |
| } |
| |
| output = &exynos_camera->preview_output; |
| |
| memset(output, 0, sizeof(struct exynos_v4l2_output)); |
| output->v4l2_id = 1; |
| output->width = exynos_camera->preview_width; |
| output->height = exynos_camera->preview_height; |
| output->format = exynos_camera->preview_format; |
| output->buffer_width = exynos_camera->preview_buffer.width; |
| output->buffer_height = exynos_camera->preview_buffer.height; |
| output->buffer_format = exynos_camera->preview_buffer.format; |
| output->buffers_count = EXYNOS_CAMERA_PREVIEW_BUFFERS_COUNT; |
| |
| rc = exynos_v4l2_output_start(exynos_camera, output); |
| if (rc < 0) { |
| ALOGE("%s: Unable to start preview output", __func__); |
| goto error; |
| } |
| |
| exynos_camera->preview_output_enabled = 1; |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| rc = -1; |
| |
| complete: |
| return rc; |
| } |
| |
| void exynos_camera_preview_output_stop(struct exynos_camera *exynos_camera) |
| { |
| struct exynos_v4l2_output *output; |
| |
| if (exynos_camera == NULL) |
| return; |
| |
| ALOGD("%s()", __func__); |
| |
| if (!exynos_camera->preview_output_enabled) { |
| ALOGE("Preview was already stopped!"); |
| return; |
| } |
| |
| output = &exynos_camera->preview_output; |
| |
| exynos_v4l2_output_stop(exynos_camera, output); |
| |
| exynos_camera->preview_output_enabled = 0; |
| } |
| |
| int exynos_camera_preview_callback(struct exynos_camera *exynos_camera, |
| struct exynos_camera_buffer *buffers, int buffers_count) |
| { |
| struct exynos_camera_buffer *buffer = NULL; |
| int width, height, format; |
| int buffer_width, buffer_height, buffer_format; |
| int rc; |
| int i; |
| |
| if (exynos_camera == NULL || buffers == NULL || buffers_count <= 0) |
| return -EINVAL; |
| |
| // ALOGD("%s()", __func__); |
| |
| if (exynos_camera->preview_listener == NULL) |
| return -1; |
| |
| if (exynos_camera->preview_listener->busy) { |
| ALOGE("%s: Dropping buffer", __func__); |
| return 0; |
| } |
| |
| exynos_camera->preview_listener->busy = 1; |
| |
| width = exynos_camera->preview_width; |
| height = exynos_camera->preview_height; |
| format = exynos_camera->preview_format; |
| |
| for (i = 0; i < buffers_count; i++) { |
| if (buffers->format == V4L2_PIX_FMT_JPEG) |
| goto buffers_continue; |
| |
| if (buffers->format == V4L2_PIX_FMT_INTERLEAVED) |
| goto buffers_continue; |
| |
| // Optimal buffer |
| if (buffers->width == width && buffers->height == height) { |
| buffer = buffers; |
| break; |
| } |
| |
| // Might-work buffer, but not optimal |
| buffer = buffers; |
| |
| buffers_continue: |
| buffers = (struct exynos_camera_buffer *) ((unsigned char *) buffers + sizeof(struct exynos_camera_buffer)); |
| } |
| |
| if (buffer == NULL) { |
| ALOGE("%s: Unable to find an appropriate buffer for preview", __func__); |
| exynos_camera->preview_listener->busy = 0; |
| return 0; |
| } |
| |
| buffer_width = buffer->width; |
| buffer_height = buffer->height; |
| buffer_format = buffer->format; |
| |
| pthread_mutex_lock(&exynos_camera->preview_mutex); |
| |
| if (buffer_width != width || buffer_height != height || buffer_format != format) { |
| if (!exynos_camera->preview_output_enabled) { |
| memcpy(&exynos_camera->preview_buffer, buffer, sizeof(struct exynos_camera_buffer)); |
| |
| rc = exynos_camera_preview_output_start(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to start preview", __func__); |
| goto error; |
| } |
| } else if (exynos_camera->preview_buffer.width != buffer_width || exynos_camera->preview_buffer.height != buffer_height || exynos_camera->preview_buffer.format != buffer_format) { |
| exynos_camera_preview_output_stop(exynos_camera); |
| |
| memcpy(&exynos_camera->preview_buffer, buffer, sizeof(struct exynos_camera_buffer)); |
| |
| rc = exynos_camera_preview_output_start(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to start preview", __func__); |
| goto error; |
| } |
| } else { |
| memcpy(&exynos_camera->preview_buffer, buffer, sizeof(struct exynos_camera_buffer)); |
| } |
| } else { |
| // The buffer format exactly matches our expectations |
| |
| if (exynos_camera->preview_output_enabled) |
| exynos_camera_preview_output_stop(exynos_camera); |
| |
| memcpy(&exynos_camera->preview_buffer, buffer, sizeof(struct exynos_camera_buffer)); |
| } |
| |
| pthread_mutex_unlock(&exynos_camera->preview_lock_mutex); |
| |
| pthread_mutex_unlock(&exynos_camera->preview_mutex); |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| pthread_mutex_unlock(&exynos_camera->preview_mutex); |
| |
| exynos_camera->preview_listener->busy = 0; |
| |
| rc = -1; |
| |
| complete: |
| return rc; |
| } |
| |
| int exynos_camera_preview(struct exynos_camera *exynos_camera) |
| { |
| struct exynos_v4l2_output *output; |
| int width, height, format; |
| buffer_handle_t *window_buffer; |
| void *window_data; |
| int window_stride; |
| camera_memory_t *memory; |
| camera_face_t caface[exynos_camera->max_detected_faces]; |
| void *memory_pointer; |
| int memory_index; |
| int memory_size; |
| int rc; |
| |
| if (exynos_camera == NULL) |
| goto error; |
| |
| // ALOGD("%s()", __func__); |
| |
| width = exynos_camera->preview_width; |
| height = exynos_camera->preview_height; |
| format = exynos_camera->preview_format; |
| |
| output = &exynos_camera->preview_output; |
| |
| if (exynos_camera->preview_output_enabled) { |
| rc = exynos_v4l2_output(exynos_camera, output, exynos_camera->preview_buffer.address); |
| if (rc < 0) { |
| ALOGE("%s: Unable to output preview", __func__); |
| goto error; |
| } |
| |
| memory = output->memory; |
| memory_index = output->memory_index; |
| memory_pointer = (void *) ((unsigned char *) memory->data + output->buffer_length * memory_index); |
| memory_size = output->buffer_length; |
| } else { |
| // In that case, we can directly use the capture memory |
| memory = exynos_camera->capture_memory; |
| memory_index = exynos_camera->capture_memory_index; |
| memory_pointer = exynos_camera->preview_buffer.pointer; |
| memory_size = exynos_camera->preview_buffer.length; |
| } |
| |
| if (exynos_camera->preview_window != NULL && exynos_camera->gralloc != NULL) { |
| exynos_camera->preview_window->dequeue_buffer(exynos_camera->preview_window, &window_buffer, &window_stride); |
| exynos_camera->gralloc->lock(exynos_camera->gralloc, *window_buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, 0, 0, width, height, &window_data); |
| |
| if (window_data == NULL) { |
| ALOGE("%s: Unable to lock gralloc", __func__); |
| goto error; |
| } |
| |
| memcpy(window_data, memory_pointer, memory_size); |
| |
| exynos_camera->gralloc->unlock(exynos_camera->gralloc, *window_buffer); |
| exynos_camera->preview_window->enqueue_buffer(exynos_camera->preview_window, window_buffer); |
| } |
| |
| if (exynos_camera->camera_fimc_is) { |
| exynos_camera->mFaceData.faces = caface; |
| exynos_v4l2_s_ext_ctrl_face_detection(exynos_camera, 0, &exynos_camera->mFaceData); |
| } |
| |
| if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_PREVIEW_FRAME) && EXYNOS_CAMERA_CALLBACK_DEFINED(data) && !exynos_camera->callback_lock) { |
| exynos_camera->callbacks.data(CAMERA_MSG_PREVIEW_FRAME, memory, memory_index, NULL, exynos_camera->callbacks.user); |
| } |
| |
| if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_PREVIEW_METADATA) && EXYNOS_CAMERA_CALLBACK_DEFINED(data) && !exynos_camera->callback_lock) { |
| exynos_camera->callbacks.data(CAMERA_MSG_PREVIEW_METADATA, exynos_camera->face_data, 0, &exynos_camera->mFaceData, exynos_camera->callbacks.user); |
| } |
| |
| if (exynos_camera->preview_output_enabled) { |
| rc = exynos_v4l2_output_release(exynos_camera, output); |
| if (rc < 0) { |
| ALOGE("%s: Unable to release preview output", __func__); |
| goto error; |
| } |
| } |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| rc = -1; |
| |
| complete: |
| exynos_camera->preview_listener->busy = 0; |
| |
| return rc; |
| } |
| |
| 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; |
| |
| ALOGE("%s: Starting thread", __func__); |
| exynos_camera->preview_thread_running = 1; |
| |
| while (exynos_camera->preview_thread_enabled) { |
| pthread_mutex_lock(&exynos_camera->preview_lock_mutex); |
| |
| pthread_mutex_lock(&exynos_camera->preview_mutex); |
| |
| if (exynos_camera->preview_listener == NULL) { |
| pthread_mutex_unlock(&exynos_camera->preview_mutex); |
| break; |
| } |
| |
| if (exynos_camera->preview_listener->busy) { |
| // Prevent preview restart race conditions |
| usleep((useconds_t)25 * 1000); |
| rc = exynos_camera_preview(exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to preview", __func__); |
| pthread_mutex_unlock(&exynos_camera->preview_mutex); |
| break; |
| } |
| } |
| |
| pthread_mutex_unlock(&exynos_camera->preview_mutex); |
| } |
| |
| exynos_camera->preview_thread_running = 0; |
| ALOGE("%s: Exiting thread", __func__); |
| |
| return NULL; |
| } |
| |
| int exynos_camera_preview_thread_start(struct exynos_camera *exynos_camera) |
| { |
| struct exynos_camera_capture_listener *listener; |
| pthread_attr_t thread_attr; |
| int rc; |
| |
| if (exynos_camera == NULL) |
| return -EINVAL; |
| |
| ALOGD("%s()", __func__); |
| |
| if (exynos_camera->preview_thread_enabled) { |
| ALOGE("Preview thread was already started!"); |
| return -1; |
| } |
| |
| pthread_mutex_init(&exynos_camera->preview_mutex, NULL); |
| pthread_mutex_init(&exynos_camera->preview_lock_mutex, NULL); |
| |
| // Initial 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_thread_enabled = 1; |
| |
| rc = pthread_create(&exynos_camera->preview_thread, &thread_attr, exynos_camera_preview_thread, (void *) exynos_camera); |
| if (rc < 0) { |
| ALOGE("%s: Unable to create thread", __func__); |
| goto error; |
| } |
| |
| listener = exynos_camera_capture_listener_register(exynos_camera, exynos_camera->preview_width, exynos_camera->preview_height, exynos_camera->preview_format, exynos_camera_preview_callback); |
| if (listener == NULL) { |
| ALOGE("%s: Unable to register preview capture listener", __func__); |
| goto error; |
| } |
| |
| exynos_camera->preview_listener = listener; |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| pthread_mutex_destroy(&exynos_camera->preview_mutex); |
| pthread_mutex_destroy(&exynos_camera->preview_lock_mutex); |
| |
| rc = -1; |
| |
| complete: |
| return rc; |
| } |
| |
| void exynos_camera_preview_thread_stop(struct exynos_camera *exynos_camera) |
| { |
| int i; |
| |
| if (exynos_camera == NULL) |
| return; |
| |
| ALOGD("%s()", __func__); |
| |
| if (!exynos_camera->preview_thread_enabled) { |
| ALOGE("Preview thread was already stopped!"); |
| return; |
| } |
| |
| if (exynos_camera->preview_listener != NULL) { |
| exynos_camera_capture_listener_unregister(exynos_camera, exynos_camera->preview_listener); |
| exynos_camera->preview_listener = NULL; |
| } |
| |
| exynos_camera->preview_thread_enabled = 0; |
| |
| pthread_mutex_unlock(&exynos_camera->preview_lock_mutex); |
| |
| // Wait for the thread to end |
| i = 0; |
| while (exynos_camera->preview_thread_running) { |
| if (i++ > 10000) { |
| ALOGE("Preview thread is taking too long to end, something is going wrong"); |
| break; |
| } |
| usleep(100); |
| } |
| |
| if (exynos_camera->preview_output_enabled) { |
| pthread_mutex_lock(&exynos_camera->preview_mutex); |
| exynos_camera_preview_output_stop(exynos_camera); |
| pthread_mutex_unlock(&exynos_camera->preview_mutex); |
| } |
| |
| pthread_mutex_destroy(&exynos_camera->preview_mutex); |
| pthread_mutex_destroy(&exynos_camera->preview_lock_mutex); |
| |
| // Invalidate the preview window |
| exynos_camera->preview_window = NULL; |
| } |
| |
| // Picture |
| |
| int exynos_camera_picture_callback(struct exynos_camera *exynos_camera, |
| struct exynos_camera_buffer *buffers, int buffers_count) |
| { |
| struct exynos_camera_buffer *jpeg_buffer = NULL; |
| struct exynos_camera_buffer *jpeg_thumbnail_buffer = NULL; |
| struct exynos_camera_buffer *yuv_buffer = NULL; |
| struct exynos_camera_buffer *yuv_thumbnail_buffer = NULL; |
| int width, height; |
| int thumbnail_width, thumbnail_height; |
| int rc; |
| int i; |
| |
| if (exynos_camera == NULL || buffers == NULL || buffers_count <= 0) |
| return -EINVAL; |
| |
| // ALOGD("%s()", __func__); |
| |
| width = exynos_camera->picture_width; |
| height = exynos_camera->picture_height; |
| thumbnail_width = exynos_camera->jpeg_thumbnail_width; |
| thumbnail_height = exynos_camera->jpeg_thumbnail_height; |
| |
| if (exynos_camera->picture_completed) |
| return -1; |
| |
| if (exynos_camera->picture_listener == NULL) |
| return -1; |
| |
| if (exynos_camera->picture_listener->busy) { |
| ALOGE("%s: Dropping buffer", __func__); |
| return 0; |
| } |
| |
| pthread_mutex_lock(&exynos_camera->picture_mutex); |
| |
| if (!exynos_camera->picture_enabled && !exynos_camera->camera_fimc_is) { |
| |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_CAPTURE, 0); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set capture", __func__); |
| goto error; |
| } |
| |
| if (exynos_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED && !exynos_camera->capture_hybrid) { |
| rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_HYBRID_CAPTURE, 1); |
| if (rc < 0) { |
| ALOGE("%s: Unable to set hybrid capture", __func__); |
| goto error; |
| } |
| |
| exynos_camera->capture_hybrid = 1; |
| } |
| |
| exynos_camera->picture_enabled = 1; |
| |
| pthread_mutex_unlock(&exynos_camera->picture_mutex); |
| return 0; |
| } |
| |
| pthread_mutex_unlock(&exynos_camera->picture_mutex); |
| |
| exynos_camera->picture_listener->busy = 1; |
| |
| // Let's assume the picture format is JPEG |
| |
| for (i = 0; i < buffers_count; i++) { |
| if (buffers->format == V4L2_PIX_FMT_JPEG) { |
| if (buffers->width == width && buffers->height == height) |
| jpeg_buffer = buffers; |
| else if (buffers->width == thumbnail_width && buffers->height == thumbnail_height) |
| jpeg_thumbnail_buffer = buffers; |
| } else { |
| i
|