blob: 59cf23cbaecb60fe59a8462e7555c7f1f3c1c2d5 [file] [log] [blame]
/*
* 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 = 0;
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__);
}
}
}
// 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) {
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 (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;
}
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 (current_af != exynos_camera->auto_focus_result) {
exynos_camera->auto_focus_result = current_af;
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;
}
}
}
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->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->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) {
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) {
if (exynos_camera->auto_focus_result == CAMERA_AF_STATUS_IN_PROGRESS) {
pthread_mutex_unlock(&exynos_camera->picture_mutex);
return 0;
}
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 {
if ((buffers->width >= width && buffers->height >= height) || exynos_camera->camera_fimc_is)
yuv_buffer = buffers;
if (buffers->width >= thumbnail_width && buffers->height >= thumbnail_height)
yuv_thumbnail_buffer = buffers;
}
buffers_continue:
buffers = (struct exynos_camera_buffer *) ((unsigned char *) buffers + sizeof(struct exynos_camera_buffer));
}
if (jpeg_buffer == NULL && yuv_buffer == NULL) {
// ALOGE("%s: Unable to find an appropriate buffer for picture", __func__);
exynos_camera->picture_listener->busy = 0;
return 0;
}
// Interleaved must not use a preview frame as picture
if (exynos_camera->camera_capture_format == V4L2_PIX_FMT_INTERLEAVED && jpeg_buffer == NULL) {
exynos_camera->picture_listener->busy = 0;
return 0;
}
pthread_mutex_lock(&exynos_camera->picture_mutex);
if (jpeg_buffer == NULL)
memset(&exynos_camera->picture_jpeg_buffer, 0, sizeof(exynos_camera->picture_jpeg_buffer));
else
memcpy(&exynos_camera->picture_jpeg_buffer, jpeg_buffer, sizeof(struct exynos_camera_buffer));
if (jpeg_thumbnail_buffer == NULL)
memset(&exynos_camera->picture_jpeg_thumbnail_buffer, 0, sizeof(exynos_camera->picture_jpeg_thumbnail_buffer));
else
memcpy(&exynos_camera->picture_jpeg_thumbnail_buffer, jpeg_thumbnail_buffer, sizeof(