The problem is even after release() is done, the singleton variable is not cleared, so a new openCameraHardware() call could return an instance which is already released. The singleton variable is cleared in the destructor, so we wait until that happens in openCameraHardware().
2141 lines
67 KiB
C++
2141 lines
67 KiB
C++
/*
|
|
** Copyright 2008, Google Inc.
|
|
**
|
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
|
** you may not use this file except in compliance with the License.
|
|
** You may obtain a copy of the License at
|
|
**
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
**
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
*/
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "QualcommCameraHardware"
|
|
#include <utils/Log.h>
|
|
|
|
#include "QualcommCameraHardware.h"
|
|
|
|
#include <utils/threads.h>
|
|
#include <utils/MemoryHeapPmem.h>
|
|
#include <utils/String16.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#if HAVE_ANDROID_OS
|
|
#include <linux/android_pmem.h>
|
|
#endif
|
|
#include <linux/ioctl.h>
|
|
|
|
#define LIKELY(exp) __builtin_expect(!!(exp), 1)
|
|
#define UNLIKELY(exp) __builtin_expect(!!(exp), 0)
|
|
|
|
extern "C" {
|
|
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <media/msm_camera.h>
|
|
|
|
#include <camera.h>
|
|
#include <camframe.h>
|
|
#include <jpeg_encoder.h>
|
|
|
|
#define THUMBNAIL_WIDTH 512
|
|
#define THUMBNAIL_HEIGHT 384
|
|
#define THUMBNAIL_WIDTH_STR "512"
|
|
#define THUMBNAIL_HEIGHT_STR "384"
|
|
#define DEFAULT_PICTURE_WIDTH 2048 // 1280
|
|
#define DEFAULT_PICTURE_HEIGHT 1536 // 768
|
|
#define THUMBNAIL_BUFFER_SIZE (THUMBNAIL_WIDTH * THUMBNAIL_HEIGHT * 3/2)
|
|
|
|
#define DEFAULT_PREVIEW_SETTING 2 // HVGA
|
|
#define MAX_ZOOM_STEPS 6
|
|
#define PREVIEW_SIZE_COUNT (sizeof(preview_sizes)/sizeof(preview_size_type))
|
|
|
|
#define BRIGHTNESS_MAX 10 // FIXME: this should correlate with brightness-values
|
|
#define BRIGHTNESS_DEF 5 // FIXME: this should correlate with brightness-values
|
|
#define ZOOM_MAX 10 // FIXME: this should correlate with zoom-values
|
|
|
|
#if DLOPEN_LIBMMCAMERA
|
|
#include <dlfcn.h>
|
|
|
|
void* (*LINK_cam_conf)(void *data);
|
|
void* (*LINK_cam_frame)(void *data);
|
|
bool (*LINK_jpeg_encoder_init)();
|
|
void (*LINK_jpeg_encoder_join)();
|
|
bool (*LINK_jpeg_encoder_encode)(const cam_ctrl_dimension_t *dimen,
|
|
const uint8_t *thumbnailbuf, int thumbnailfd,
|
|
const uint8_t *snapshotbuf, int snapshotfd,
|
|
common_crop_t *scaling_parms);
|
|
int (*LINK_camframe_terminate)(void);
|
|
int8_t (*LINK_jpeg_encoder_setMainImageQuality)(uint32_t quality);
|
|
int8_t (*LINK_jpeg_encoder_setThumbnailQuality)(uint32_t quality);
|
|
int8_t (*LINK_jpeg_encoder_setRotation)(uint32_t rotation);
|
|
int8_t (*LINK_jpeg_encoder_setLocation)(const camera_position_type *location);
|
|
// callbacks
|
|
void (**LINK_mmcamera_camframe_callback)(struct msm_frame *frame);
|
|
void (**LINK_mmcamera_jpegfragment_callback)(uint8_t *buff_ptr,
|
|
uint32_t buff_size);
|
|
void (**LINK_mmcamera_jpeg_callback)(jpeg_event_t status);
|
|
void (**LINK_mmcamera_shutter_callback)();
|
|
#else
|
|
#define LINK_cam_conf cam_conf
|
|
#define LINK_cam_frame cam_frame
|
|
#define LINK_jpeg_encoder_init jpeg_encoder_init
|
|
#define LINK_jpeg_encoder_join jpeg_encoder_join
|
|
#define LINK_jpeg_encoder_encode jpeg_encoder_encode
|
|
#define LINK_camframe_terminate camframe_terminate
|
|
#define LINK_jpeg_encoder_setMainImageQuality jpeg_encoder_setMainImageQuality
|
|
#define LINK_jpeg_encoder_setThumbnailQuality jpeg_encoder_setThumbnailQuality
|
|
#define LINK_jpeg_encoder_setRotation jpeg_encoder_setRotation
|
|
#define LINK_jpeg_encoder_setLocation jpeg_encoder_setLocation
|
|
extern void (*mmcamera_camframe_callback)(struct msm_frame *frame);
|
|
extern void (*mmcamera_jpegfragment_callback)(uint8_t *buff_ptr,
|
|
uint32_t buff_size);
|
|
extern void (*mmcamera_jpeg_callback)(jpeg_event_t status);
|
|
extern void (*mmcamera_shutter_callback)();
|
|
#endif
|
|
|
|
} // extern "C"
|
|
|
|
struct preview_size_type {
|
|
int width;
|
|
int height;
|
|
};
|
|
|
|
static preview_size_type preview_sizes[] = {
|
|
{ 800, 480 }, // WVGA
|
|
{ 640, 480 }, // VGA
|
|
{ 480, 320 }, // HVGA
|
|
{ 384, 288 },
|
|
{ 352, 288 }, // CIF
|
|
{ 320, 240 }, // QVGA
|
|
{ 240, 160 }, // SQVGA
|
|
{ 176, 144 }, // QCIF
|
|
};
|
|
|
|
struct str_map {
|
|
const char *const desc;
|
|
int val;
|
|
};
|
|
|
|
static int attr_lookup(const struct str_map *const arr,
|
|
const char *name,
|
|
int def)
|
|
{
|
|
if (name) {
|
|
const struct str_map *trav = arr;
|
|
while (trav->desc) {
|
|
if (!strcmp(trav->desc, name))
|
|
return trav->val;
|
|
trav++;
|
|
}
|
|
}
|
|
return def;
|
|
}
|
|
|
|
#define INIT_VALUES_FOR(parm) do { \
|
|
if (!parm##_values) { \
|
|
parm##_values = (char *)malloc(sizeof(parm)/ \
|
|
sizeof(parm[0])*30); \
|
|
char *ptr = parm##_values; \
|
|
const str_map *trav; \
|
|
for (trav = parm; trav->desc; trav++) { \
|
|
int len = strlen(trav->desc); \
|
|
strcpy(ptr, trav->desc); \
|
|
ptr += len; \
|
|
*ptr++ = ','; \
|
|
} \
|
|
*--ptr = 0; \
|
|
} \
|
|
} while(0)
|
|
|
|
// from aeecamera.h
|
|
static const str_map whitebalance[] = {
|
|
{ "auto", CAMERA_WB_AUTO },
|
|
{ "custom", CAMERA_WB_CUSTOM },
|
|
{ "incandescent", CAMERA_WB_INCANDESCENT },
|
|
{ "florescent", CAMERA_WB_FLUORESCENT },
|
|
{ "daylight", CAMERA_WB_DAYLIGHT },
|
|
{ "cloudy", CAMERA_WB_CLOUDY_DAYLIGHT },
|
|
{ "twilight", CAMERA_WB_TWILIGHT },
|
|
{ "shade", CAMERA_WB_SHADE },
|
|
{ NULL, 0 }
|
|
};
|
|
static char *whitebalance_values;
|
|
|
|
// from camera_effect_t
|
|
static const str_map color_effects[] = {
|
|
{ "off", CAMERA_EFFECT_OFF }, /* This list must match aeecamera.h */
|
|
{ "mono", CAMERA_EFFECT_MONO },
|
|
{ "negative", CAMERA_EFFECT_NEGATIVE },
|
|
{ "solarize", CAMERA_EFFECT_SOLARIZE },
|
|
{ "pastel", CAMERA_EFFECT_PASTEL },
|
|
{ "mosaic", CAMERA_EFFECT_MOSAIC },
|
|
{ "resize", CAMERA_EFFECT_RESIZE },
|
|
{ "sepia", CAMERA_EFFECT_SEPIA },
|
|
{ "postersize", CAMERA_EFFECT_POSTERIZE },
|
|
{ "whiteboard", CAMERA_EFFECT_WHITEBOARD },
|
|
{ "blackboard", CAMERA_EFFECT_BLACKBOARD },
|
|
{ "aqua", CAMERA_EFFECT_AQUA },
|
|
{ NULL, 0 }
|
|
};
|
|
static char *color_effects_values;
|
|
|
|
// from qcamera/common/camera.h
|
|
static const str_map anti_banding[] = {
|
|
{ "off", CAMERA_ANTIBANDING_OFF },
|
|
{ "60hz", CAMERA_ANTIBANDING_60HZ },
|
|
{ "50hz", CAMERA_ANTIBANDING_50HZ },
|
|
{ "auto", CAMERA_ANTIBANDING_AUTO },
|
|
{ NULL, 0 }
|
|
};
|
|
static char *anti_banding_values;
|
|
|
|
// round to the next power of two
|
|
static inline unsigned clp2(unsigned x)
|
|
{
|
|
x = x - 1;
|
|
x = x | (x >> 1);
|
|
x = x | (x >> 2);
|
|
x = x | (x >> 4);
|
|
x = x | (x >> 8);
|
|
x = x | (x >>16);
|
|
return x + 1;
|
|
}
|
|
|
|
namespace android {
|
|
|
|
static Mutex singleton_lock;
|
|
static bool singleton_releasing;
|
|
static Condition singleton_wait;
|
|
|
|
static void receive_camframe_callback(struct msm_frame *frame);
|
|
static void receive_jpeg_fragment_callback(uint8_t *buff_ptr, uint32_t buff_size);
|
|
static void receive_jpeg_callback(jpeg_event_t status);
|
|
static void receive_shutter_callback();
|
|
|
|
QualcommCameraHardware::QualcommCameraHardware()
|
|
: mParameters(),
|
|
mPreviewHeight(-1),
|
|
mPreviewWidth(-1),
|
|
mRawHeight(-1),
|
|
mRawWidth(-1),
|
|
mBrightness(BRIGHTNESS_DEF),
|
|
mZoomValuePrev(0),
|
|
mZoomValueCurr(0),
|
|
mZoomInitialised(false),
|
|
mCameraRunning(false),
|
|
mPreviewInitialized(false),
|
|
mFrameThreadRunning(false),
|
|
mSnapshotThreadRunning(false),
|
|
mReleasedRecordingFrame(false),
|
|
mShutterCallback(0),
|
|
mRawPictureCallback(0),
|
|
mJpegPictureCallback(0),
|
|
mPictureCallbackCookie(0),
|
|
mAutoFocusCallback(0),
|
|
mAutoFocusCallbackCookie(0),
|
|
mPreviewCallback(0),
|
|
mPreviewCallbackCookie(0),
|
|
mRecordingCallback(0),
|
|
mRecordingCallbackCookie(0),
|
|
mPreviewFrameSize(0),
|
|
mRawSize(0),
|
|
mCameraControlFd(-1),
|
|
mAutoFocusThreadRunning(false),
|
|
mAutoFocusFd(-1),
|
|
mInPreviewCallback(false)
|
|
{
|
|
memset(&mZoom, 0, sizeof(mZoom));
|
|
memset(&mDimension, 0, sizeof(mDimension));
|
|
memset(&mCrop, 0, sizeof(mCrop));
|
|
LOGV("constructor EX");
|
|
}
|
|
|
|
void QualcommCameraHardware::initDefaultParameters()
|
|
{
|
|
CameraParameters p;
|
|
|
|
LOGV("initDefaultParameters E");
|
|
|
|
preview_size_type *ps = &preview_sizes[DEFAULT_PREVIEW_SETTING];
|
|
p.setPreviewSize(ps->width, ps->height);
|
|
p.setPreviewFrameRate(15);
|
|
p.setPreviewFormat("yuv420sp"); // informative
|
|
p.setPictureFormat("jpeg"); // informative
|
|
|
|
p.set("jpeg-thumbnail-width", THUMBNAIL_WIDTH_STR); // informative
|
|
p.set("jpeg-thumbnail-height", THUMBNAIL_HEIGHT_STR); // informative
|
|
p.set("jpeg-thumbnail-quality", "90");
|
|
|
|
p.setPictureSize(DEFAULT_PICTURE_WIDTH, DEFAULT_PICTURE_HEIGHT);
|
|
|
|
#if 0
|
|
p.set("gps-timestamp", "1199145600"); // Jan 1, 2008, 00:00:00
|
|
p.set("gps-latitude", "37.736071"); // A little house in San Francisco
|
|
p.set("gps-longitude", "-122.441983");
|
|
p.set("gps-altitude", "21"); // meters
|
|
#endif
|
|
|
|
// This will happen only one in the lifetime of the mediaserver process.
|
|
// We do not free the _values arrays when we destroy the camera object.
|
|
INIT_VALUES_FOR(anti_banding);
|
|
INIT_VALUES_FOR(color_effects);
|
|
INIT_VALUES_FOR(whitebalance);
|
|
|
|
p.set("anti-banding-values", anti_banding_values);
|
|
p.set("effect-values", color_effects_values);
|
|
p.set("whitebalance-values", whitebalance_values);
|
|
p.set("picture-size-values", "2048x1536,1600x1200,1024x768");
|
|
|
|
// FIXME: we can specify these numeric ranges better
|
|
p.set("exposure-offset-values", "0,1,2,3,4,5,6,7,8,9,10");
|
|
p.set("zoom-values", "0,1,2,3,4,5,6,7,8,9,10");
|
|
|
|
if (setParameters(p) != NO_ERROR) {
|
|
LOGE("Failed to set default parameters?!");
|
|
}
|
|
|
|
LOGV("initDefaultParameters X");
|
|
}
|
|
|
|
#define ROUND_TO_PAGE(x) (((x)+0xfff)&~0xfff)
|
|
|
|
void QualcommCameraHardware::startCamera()
|
|
{
|
|
LOGV("startCamera E");
|
|
#if DLOPEN_LIBMMCAMERA
|
|
libmmcamera = ::dlopen("libqcamera.so", RTLD_NOW);
|
|
LOGV("loading libqcamera at %p", libmmcamera);
|
|
if (!libmmcamera) {
|
|
LOGE("FATAL ERROR: could not dlopen libqcamera.so: %s", dlerror());
|
|
return;
|
|
}
|
|
|
|
*(void **)&LINK_cam_frame =
|
|
::dlsym(libmmcamera, "cam_frame");
|
|
*(void **)&LINK_camframe_terminate =
|
|
::dlsym(libmmcamera, "camframe_terminate");
|
|
|
|
*(void **)&LINK_jpeg_encoder_init =
|
|
::dlsym(libmmcamera, "jpeg_encoder_init");
|
|
|
|
*(void **)&LINK_jpeg_encoder_encode =
|
|
::dlsym(libmmcamera, "jpeg_encoder_encode");
|
|
|
|
*(void **)&LINK_jpeg_encoder_join =
|
|
::dlsym(libmmcamera, "jpeg_encoder_join");
|
|
|
|
*(void **)&LINK_mmcamera_camframe_callback =
|
|
::dlsym(libmmcamera, "mmcamera_camframe_callback");
|
|
|
|
*LINK_mmcamera_camframe_callback = receive_camframe_callback;
|
|
|
|
*(void **)&LINK_mmcamera_jpegfragment_callback =
|
|
::dlsym(libmmcamera, "mmcamera_jpegfragment_callback");
|
|
|
|
*LINK_mmcamera_jpegfragment_callback = receive_jpeg_fragment_callback;
|
|
|
|
*(void **)&LINK_mmcamera_jpeg_callback =
|
|
::dlsym(libmmcamera, "mmcamera_jpeg_callback");
|
|
|
|
*LINK_mmcamera_jpeg_callback = receive_jpeg_callback;
|
|
|
|
*(void **)&LINK_mmcamera_shutter_callback =
|
|
::dlsym(libmmcamera, "mmcamera_shutter_callback");
|
|
|
|
*LINK_mmcamera_shutter_callback = receive_shutter_callback;
|
|
|
|
*(void**)&LINK_jpeg_encoder_setMainImageQuality =
|
|
::dlsym(libmmcamera, "jpeg_encoder_setMainImageQuality");
|
|
|
|
*(void**)&LINK_jpeg_encoder_setThumbnailQuality =
|
|
::dlsym(libmmcamera, "jpeg_encoder_setThumbnailQuality");
|
|
|
|
*(void**)&LINK_jpeg_encoder_setRotation =
|
|
::dlsym(libmmcamera, "jpeg_encoder_setRotation");
|
|
|
|
*(void**)&LINK_jpeg_encoder_setLocation =
|
|
::dlsym(libmmcamera, "jpeg_encoder_setLocation");
|
|
|
|
*(void **)&LINK_cam_conf =
|
|
::dlsym(libmmcamera, "cam_conf");
|
|
#else
|
|
mmcamera_camframe_callback = receive_camframe_callback;
|
|
mmcamera_jpegfragment_callback = receive_jpeg_fragment_callback;
|
|
mmcamera_jpeg_callback = receive_jpeg_callback;
|
|
mmcamera_shutter_callback = receive_shutter_callback;
|
|
#endif // DLOPEN_LIBMMCAMERA
|
|
|
|
/* The control thread is in libcamera itself. */
|
|
mCameraControlFd = open(MSM_CAMERA_CONTROL, O_RDWR);
|
|
if (mCameraControlFd < 0) {
|
|
LOGE("startCamera X: %s open failed: %s!",
|
|
MSM_CAMERA_CONTROL,
|
|
strerror(errno));
|
|
return;
|
|
}
|
|
|
|
pthread_create(&mCamConfigThread, NULL,
|
|
LINK_cam_conf, NULL);
|
|
|
|
LOGV("startCamera X");
|
|
}
|
|
|
|
status_t QualcommCameraHardware::dump(int fd,
|
|
const Vector<String16>& args) const
|
|
{
|
|
const size_t SIZE = 256;
|
|
char buffer[SIZE];
|
|
String8 result;
|
|
|
|
// Dump internal primitives.
|
|
result.append("QualcommCameraHardware::dump");
|
|
snprintf(buffer, 255, "preview width(%d) x height (%d)\n",
|
|
mPreviewWidth, mPreviewHeight);
|
|
result.append(buffer);
|
|
snprintf(buffer, 255, "raw width(%d) x height (%d)\n",
|
|
mRawWidth, mRawHeight);
|
|
result.append(buffer);
|
|
snprintf(buffer, 255,
|
|
"preview frame size(%d), raw size (%d), jpeg size (%d) "
|
|
"and jpeg max size (%d)\n", mPreviewFrameSize, mRawSize,
|
|
mJpegSize, mJpegMaxSize);
|
|
result.append(buffer);
|
|
write(fd, result.string(), result.size());
|
|
|
|
// Dump internal objects.
|
|
if (mPreviewHeap != 0) {
|
|
mPreviewHeap->dump(fd, args);
|
|
}
|
|
if (mRawHeap != 0) {
|
|
mRawHeap->dump(fd, args);
|
|
}
|
|
if (mJpegHeap != 0) {
|
|
mJpegHeap->dump(fd, args);
|
|
}
|
|
mParameters.dump(fd, args);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
bool QualcommCameraHardware::native_set_dimension(int camfd)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.type = CAMERA_SET_PARM_DIMENSION;
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.length = sizeof(cam_ctrl_dimension_t);
|
|
ctrlCmd.value = &mDimension;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
|
|
LOGE("native_set_dimension: ioctl fd %d error %s",
|
|
camfd,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
LOGV("native_set_dimension status %d\n", ctrlCmd.status);
|
|
return ctrlCmd.status == CAM_CTRL_SUCCESS;
|
|
}
|
|
|
|
static bool native_set_afmode(int camfd, isp3a_af_mode_t af_type)
|
|
{
|
|
int rc;
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_SET_PARM_AUTO_FOCUS;
|
|
ctrlCmd.length = sizeof(af_type);
|
|
ctrlCmd.value = &af_type;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if ((rc = ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd)) < 0)
|
|
LOGE("native_set_afmode: ioctl fd %d error %s\n",
|
|
camfd,
|
|
strerror(errno));
|
|
|
|
LOGV("native_set_afmode: ctrlCmd.status == %d\n", ctrlCmd.status);
|
|
return rc >= 0 && ctrlCmd.status == CAMERA_EXIT_CB_DONE;
|
|
}
|
|
|
|
static bool native_cancel_afmode(int camfd, int af_fd)
|
|
{
|
|
int rc;
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_AUTO_FOCUS_CANCEL;
|
|
ctrlCmd.length = 0;
|
|
ctrlCmd.resp_fd = af_fd;
|
|
|
|
if ((rc = ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND_2, &ctrlCmd)) < 0)
|
|
LOGE("native_cancel_afmode: ioctl fd %d error %s\n",
|
|
camfd,
|
|
strerror(errno));
|
|
return rc >= 0;
|
|
}
|
|
|
|
static bool native_start_preview(int camfd)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_START_PREVIEW;
|
|
ctrlCmd.length = 0;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if (ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
|
|
LOGE("native_start_preview: MSM_CAM_IOCTL_CTRL_COMMAND fd %d error %s",
|
|
camfd,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool native_get_picture (int camfd, common_crop_t *crop)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.length = sizeof(common_crop_t);
|
|
ctrlCmd.value = crop;
|
|
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_GET_PICTURE, &ctrlCmd) < 0) {
|
|
LOGE("native_get_picture: MSM_CAM_IOCTL_GET_PICTURE fd %d error %s",
|
|
camfd,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
LOGV("crop: in1_w %d", crop->in1_w);
|
|
LOGV("crop: in1_h %d", crop->in1_h);
|
|
LOGV("crop: out1_w %d", crop->out1_w);
|
|
LOGV("crop: out1_h %d", crop->out1_h);
|
|
|
|
LOGV("crop: in2_w %d", crop->in2_w);
|
|
LOGV("crop: in2_h %d", crop->in2_h);
|
|
LOGV("crop: out2_w %d", crop->out2_w);
|
|
LOGV("crop: out2_h %d", crop->out2_h);
|
|
|
|
LOGV("crop: update %d", crop->update_flag);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool native_stop_preview(int camfd)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_STOP_PREVIEW;
|
|
ctrlCmd.length = 0;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
|
|
LOGE("native_stop_preview: ioctl fd %d error %s",
|
|
camfd,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool native_start_snapshot(int camfd)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_START_SNAPSHOT;
|
|
ctrlCmd.length = 0;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
|
|
LOGE("native_start_snapshot: ioctl fd %d error %s",
|
|
camfd,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool native_stop_snapshot (int camfd)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_STOP_SNAPSHOT;
|
|
ctrlCmd.length = 0;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if (ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
|
|
LOGE("native_stop_snapshot: ioctl fd %d error %s",
|
|
camfd,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool QualcommCameraHardware::native_jpeg_encode(void)
|
|
{
|
|
int jpeg_quality = mParameters.getInt("jpeg-quality");
|
|
if (jpeg_quality >= 0) {
|
|
LOGV("native_jpeg_encode, current jpeg main img quality =%d",
|
|
jpeg_quality);
|
|
if(!LINK_jpeg_encoder_setMainImageQuality(jpeg_quality)) {
|
|
LOGE("native_jpeg_encode set jpeg-quality failed");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int thumbnail_quality = mParameters.getInt("jpeg-thumbnail-quality");
|
|
if (thumbnail_quality >= 0) {
|
|
LOGV("native_jpeg_encode, current jpeg thumbnail quality =%d",
|
|
thumbnail_quality);
|
|
if(!LINK_jpeg_encoder_setThumbnailQuality(thumbnail_quality)) {
|
|
LOGE("native_jpeg_encode set thumbnail-quality failed");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int rotation = mParameters.getInt("rotation");
|
|
if (rotation >= 0) {
|
|
LOGV("native_jpeg_encode, rotation = %d", rotation);
|
|
if(!LINK_jpeg_encoder_setRotation(rotation)) {
|
|
LOGE("native_jpeg_encode set rotation failed");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
jpeg_set_location();
|
|
|
|
if (!LINK_jpeg_encoder_encode(&mDimension,
|
|
(uint8_t *)mThumbnailHeap->mHeap->base(),
|
|
mThumbnailHeap->mHeap->getHeapID(),
|
|
(uint8_t *)mRawHeap->mHeap->base(),
|
|
mRawHeap->mHeap->getHeapID(),
|
|
&mCrop)) {
|
|
LOGE("native_jpeg_encode: jpeg_encoder_encode failed.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void QualcommCameraHardware::jpeg_set_location()
|
|
{
|
|
bool encode_location = true;
|
|
camera_position_type pt;
|
|
|
|
#define PARSE_LOCATION(what,type,fmt,desc) do { \
|
|
pt.what = 0; \
|
|
const char *what##_str = mParameters.get("gps-"#what); \
|
|
LOGV("GPS PARM %s --> [%s]", "gps-"#what, what##_str); \
|
|
if (what##_str) { \
|
|
type what = 0; \
|
|
if (sscanf(what##_str, fmt, &what) == 1) \
|
|
pt.what = what; \
|
|
else { \
|
|
LOGE("GPS " #what " %s could not" \
|
|
" be parsed as a " #desc, what##_str); \
|
|
encode_location = false; \
|
|
} \
|
|
} \
|
|
else { \
|
|
LOGV("GPS " #what " not specified: " \
|
|
"defaulting to zero in EXIF header."); \
|
|
encode_location = false; \
|
|
} \
|
|
} while(0)
|
|
|
|
PARSE_LOCATION(timestamp, long, "%ld", "long");
|
|
if (!pt.timestamp) pt.timestamp = time(NULL);
|
|
PARSE_LOCATION(altitude, short, "%hd", "short");
|
|
PARSE_LOCATION(latitude, double, "%lf", "double float");
|
|
PARSE_LOCATION(longitude, double, "%lf", "double float");
|
|
|
|
#undef PARSE_LOCATION
|
|
|
|
if (encode_location) {
|
|
LOGD("setting image location ALT %d LAT %lf LON %lf",
|
|
pt.altitude, pt.latitude, pt.longitude);
|
|
if (!LINK_jpeg_encoder_setLocation(&pt)) {
|
|
LOGE("jpeg_set_location: LINK_jpeg_encoder_setLocation failed.");
|
|
}
|
|
}
|
|
else LOGV("not setting image location");
|
|
}
|
|
|
|
void QualcommCameraHardware::runFrameThread(void *data)
|
|
{
|
|
LOGV("runFrameThread E");
|
|
|
|
int cnt;
|
|
|
|
#if DLOPEN_LIBMMCAMERA
|
|
// We need to maintain a reference to libqcamera.so for the duration of the
|
|
// frame thread, because we do not know when it will exit relative to the
|
|
// lifetime of this object. We do not want to dlclose() libqcamera while
|
|
// LINK_cam_frame is still running.
|
|
void *libhandle = ::dlopen("libqcamera.so", RTLD_NOW);
|
|
LOGV("FRAME: loading libqcamera at %p", libhandle);
|
|
if (!libhandle) {
|
|
LOGE("FATAL ERROR: could not dlopen libqcamera.so: %s", dlerror());
|
|
}
|
|
if (libhandle)
|
|
#endif
|
|
{
|
|
LINK_cam_frame(data);
|
|
}
|
|
|
|
mPreviewHeap.clear();
|
|
|
|
#if DLOPEN_LIBMMCAMERA
|
|
if (libhandle) {
|
|
::dlclose(libhandle);
|
|
LOGV("FRAME: dlclose(libqcamera)");
|
|
}
|
|
#endif
|
|
|
|
mFrameThreadWaitLock.lock();
|
|
mFrameThreadRunning = false;
|
|
mFrameThreadWait.signal();
|
|
mFrameThreadWaitLock.unlock();
|
|
|
|
LOGV("runFrameThread X");
|
|
}
|
|
|
|
void *frame_thread(void *user)
|
|
{
|
|
LOGD("frame_thread E");
|
|
sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
|
|
if (obj != 0) {
|
|
obj->runFrameThread(user);
|
|
}
|
|
else LOGW("not starting frame thread: the object went away!");
|
|
LOGD("frame_thread X");
|
|
return NULL;
|
|
}
|
|
|
|
bool QualcommCameraHardware::initPreview()
|
|
{
|
|
// See comments in deinitPreview() for why we have to wait for the frame
|
|
// thread here, and why we can't use pthread_join().
|
|
LOGI("initPreview E: preview size=%dx%d", mPreviewWidth, mPreviewHeight);
|
|
mFrameThreadWaitLock.lock();
|
|
while (mFrameThreadRunning) {
|
|
LOGV("initPreview: waiting for old frame thread to complete.");
|
|
mFrameThreadWait.wait(mFrameThreadWaitLock);
|
|
LOGV("initPreview: old frame thread completed.");
|
|
}
|
|
mFrameThreadWaitLock.unlock();
|
|
|
|
mSnapshotThreadWaitLock.lock();
|
|
while (mSnapshotThreadRunning) {
|
|
LOGV("initPreview: waiting for old snapshot thread to complete.");
|
|
mSnapshotThreadWait.wait(mSnapshotThreadWaitLock);
|
|
LOGV("initPreview: old snapshot thread completed.");
|
|
}
|
|
mSnapshotThreadWaitLock.unlock();
|
|
|
|
int cnt = 0;
|
|
mPreviewFrameSize = mPreviewWidth * mPreviewHeight * 3/2;
|
|
mPreviewHeap = new PmemPool("/dev/pmem_adsp",
|
|
mCameraControlFd,
|
|
MSM_PMEM_OUTPUT2,
|
|
mPreviewFrameSize,
|
|
kPreviewBufferCount,
|
|
mPreviewFrameSize,
|
|
0,
|
|
"preview");
|
|
|
|
if (!mPreviewHeap->initialized()) {
|
|
mPreviewHeap.clear();
|
|
LOGE("initPreview X: could not initialize preview heap.");
|
|
return false;
|
|
}
|
|
|
|
mDimension.picture_width = DEFAULT_PICTURE_WIDTH;
|
|
mDimension.picture_height = DEFAULT_PICTURE_HEIGHT;
|
|
|
|
bool ret = native_set_dimension(mCameraControlFd);
|
|
|
|
if (ret) {
|
|
for (cnt = 0; cnt < kPreviewBufferCount; cnt++) {
|
|
frames[cnt].fd = mPreviewHeap->mHeap->getHeapID();
|
|
frames[cnt].buffer =
|
|
(uint32_t)mPreviewHeap->mHeap->base() + mPreviewFrameSize * cnt;
|
|
frames[cnt].y_off = 0;
|
|
frames[cnt].cbcr_off = mPreviewWidth * mPreviewHeight;
|
|
frames[cnt].path = MSM_FRAME_ENC;
|
|
}
|
|
|
|
mFrameThreadWaitLock.lock();
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
mFrameThreadRunning = !pthread_create(&mFrameThread,
|
|
&attr,
|
|
frame_thread,
|
|
&frames[kPreviewBufferCount-1]);
|
|
ret = mFrameThreadRunning;
|
|
mFrameThreadWaitLock.unlock();
|
|
}
|
|
|
|
LOGV("initPreview X: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
void QualcommCameraHardware::deinitPreview(void)
|
|
{
|
|
LOGI("deinitPreview E");
|
|
|
|
// When we call deinitPreview(), we signal to the frame thread that it
|
|
// needs to exit, but we DO NOT WAIT for it to complete here. The problem
|
|
// is that deinitPreview is sometimes called from the frame-thread's
|
|
// callback, when the refcount on the Camera client reaches zero. If we
|
|
// called pthread_join(), we would deadlock. So, we just call
|
|
// LINK_camframe_terminate() in deinitPreview(), which makes sure that
|
|
// after the preview callback returns, the camframe thread will exit. We
|
|
// could call pthread_join() in initPreview() to join the last frame
|
|
// thread. However, we would also have to call pthread_join() in release
|
|
// as well, shortly before we destoy the object; this would cause the same
|
|
// deadlock, since release(), like deinitPreview(), may also be called from
|
|
// the frame-thread's callback. This we have to make the frame thread
|
|
// detached, and use a separate mechanism to wait for it to complete.
|
|
|
|
if (LINK_camframe_terminate() < 0)
|
|
LOGE("failed to stop the camframe thread: %s",
|
|
strerror(errno));
|
|
LOGI("deinitPreview X");
|
|
}
|
|
|
|
bool QualcommCameraHardware::initRaw(bool initJpegHeap)
|
|
{
|
|
LOGV("initRaw E: picture size=%dx%d",
|
|
mRawWidth, mRawHeight);
|
|
|
|
mDimension.picture_width = mRawWidth;
|
|
mDimension.picture_height = mRawHeight;
|
|
mRawSize = mRawWidth * mRawHeight * 3 / 2;
|
|
mJpegMaxSize = mRawWidth * mRawHeight * 3 / 2;
|
|
|
|
if(!native_set_dimension(mCameraControlFd)) {
|
|
LOGE("initRaw X: failed to set dimension");
|
|
return false;
|
|
}
|
|
|
|
if (mJpegHeap != NULL) {
|
|
LOGV("initRaw: clearing old mJpegHeap.");
|
|
mJpegHeap.clear();
|
|
}
|
|
|
|
// Snapshot
|
|
|
|
LOGV("initRaw: initializing mRawHeap.");
|
|
mRawHeap =
|
|
new PmemPool("/dev/pmem_camera",
|
|
mCameraControlFd,
|
|
MSM_PMEM_MAINIMG,
|
|
mJpegMaxSize,
|
|
kRawBufferCount,
|
|
mRawSize,
|
|
0,
|
|
"snapshot camera");
|
|
|
|
if (!mRawHeap->initialized()) {
|
|
LOGE("initRaw X failed with pmem_camera, trying with pmem_adsp");
|
|
mRawHeap =
|
|
new PmemPool("/dev/pmem_adsp",
|
|
mCameraControlFd,
|
|
MSM_PMEM_MAINIMG,
|
|
mJpegMaxSize,
|
|
kRawBufferCount,
|
|
mRawSize,
|
|
0,
|
|
"snapshot camera");
|
|
if (!mRawHeap->initialized()) {
|
|
mRawHeap.clear();
|
|
LOGE("initRaw X: error initializing mRawHeap");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LOGV("do_mmap snapshot pbuf = %p, pmem_fd = %d",
|
|
(uint8_t *)mRawHeap->mHeap->base(), mRawHeap->mHeap->getHeapID());
|
|
|
|
// Jpeg
|
|
|
|
if (initJpegHeap) {
|
|
LOGV("initRaw: initializing mJpegHeap.");
|
|
mJpegHeap =
|
|
new AshmemPool(mJpegMaxSize,
|
|
kJpegBufferCount,
|
|
0, // we do not know how big the picture wil be
|
|
0,
|
|
"jpeg");
|
|
|
|
if (!mJpegHeap->initialized()) {
|
|
mJpegHeap.clear();
|
|
mRawHeap.clear();
|
|
LOGE("initRaw X failed: error initializing mJpegHeap.");
|
|
return false;
|
|
}
|
|
|
|
// Thumbnails
|
|
|
|
mThumbnailHeap =
|
|
new PmemPool("/dev/pmem_adsp",
|
|
mCameraControlFd,
|
|
MSM_PMEM_THUMBAIL,
|
|
THUMBNAIL_BUFFER_SIZE,
|
|
1,
|
|
THUMBNAIL_BUFFER_SIZE,
|
|
0,
|
|
"thumbnail");
|
|
|
|
if (!mThumbnailHeap->initialized()) {
|
|
mThumbnailHeap.clear();
|
|
mJpegHeap.clear();
|
|
mRawHeap.clear();
|
|
LOGE("initRaw X failed: error initializing mThumbnailHeap.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LOGV("initRaw X");
|
|
return true;
|
|
}
|
|
|
|
void QualcommCameraHardware::deinitRaw()
|
|
{
|
|
LOGV("deinitRaw E");
|
|
|
|
mThumbnailHeap.clear();
|
|
mJpegHeap.clear();
|
|
mRawHeap.clear();
|
|
|
|
LOGV("deinitRaw X");
|
|
}
|
|
|
|
void QualcommCameraHardware::release()
|
|
{
|
|
LOGD("release E");
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
#if DLOPEN_LIBMMCAMERA
|
|
if (libmmcamera == NULL) {
|
|
LOGE("ERROR: multiple release!");
|
|
return;
|
|
}
|
|
#else
|
|
#warning "Cannot detect multiple release when not dlopen()ing libqcamera!"
|
|
#endif
|
|
|
|
int cnt, rc;
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
if (mCameraRunning) {
|
|
if(mRecordingCallback != NULL) {
|
|
mRecordFrameLock.lock();
|
|
mReleasedRecordingFrame = true;
|
|
mRecordWait.signal();
|
|
mRecordFrameLock.unlock();
|
|
}
|
|
stopPreviewInternal();
|
|
}
|
|
|
|
LINK_jpeg_encoder_join();
|
|
deinitRaw();
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.length = 0;
|
|
ctrlCmd.type = (uint16_t)CAMERA_EXIT;
|
|
ctrlCmd.resp_fd = mCameraControlFd; // FIXME: this will be put in by the kernel
|
|
if (ioctl(mCameraControlFd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0)
|
|
LOGE("ioctl CAMERA_EXIT fd %d error %s",
|
|
mCameraControlFd, strerror(errno));
|
|
rc = pthread_join(mCamConfigThread, NULL);
|
|
if (rc)
|
|
LOGE("config_thread exit failure: %s", strerror(errno));
|
|
else
|
|
LOGV("pthread_join succeeded on config_thread");
|
|
|
|
close(mCameraControlFd);
|
|
mCameraControlFd = -1;
|
|
|
|
#if DLOPEN_LIBMMCAMERA
|
|
if (libmmcamera) {
|
|
::dlclose(libmmcamera);
|
|
LOGV("dlclose(libqcamera)");
|
|
libmmcamera = NULL;
|
|
}
|
|
#endif
|
|
|
|
Mutex::Autolock lock(&singleton_lock);
|
|
singleton_releasing = true;
|
|
|
|
LOGD("release X");
|
|
}
|
|
|
|
QualcommCameraHardware::~QualcommCameraHardware()
|
|
{
|
|
LOGD("~QualcommCameraHardware E");
|
|
Mutex::Autolock lock(&singleton_lock);
|
|
singleton.clear();
|
|
singleton_releasing = false;
|
|
singleton_wait.signal();
|
|
LOGD("~QualcommCameraHardware X");
|
|
}
|
|
|
|
sp<IMemoryHeap> QualcommCameraHardware::getRawHeap() const
|
|
{
|
|
LOGV("getRawHeap");
|
|
return mRawHeap != NULL ? mRawHeap->mHeap : NULL;
|
|
}
|
|
|
|
sp<IMemoryHeap> QualcommCameraHardware::getPreviewHeap() const
|
|
{
|
|
LOGV("getPreviewHeap");
|
|
return mPreviewHeap != NULL ? mPreviewHeap->mHeap : NULL;
|
|
}
|
|
|
|
status_t QualcommCameraHardware::startPreviewInternal()
|
|
{
|
|
if(mCameraRunning) {
|
|
LOGV("startPreview X: preview already running.");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
if (!mPreviewInitialized) {
|
|
mPreviewInitialized = initPreview();
|
|
if (!mPreviewInitialized) {
|
|
LOGE("startPreview X initPreview failed. Not starting preview.");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
mCameraRunning = native_start_preview(mCameraControlFd);
|
|
if(!mCameraRunning) {
|
|
deinitPreview();
|
|
mPreviewInitialized = false;
|
|
LOGE("startPreview X: native_start_preview failed!");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
setSensorPreviewEffect(mCameraControlFd, mParameters.get("effect"));
|
|
setSensorWBLighting(mCameraControlFd, mParameters.get("whitebalance"));
|
|
setAntiBanding(mCameraControlFd, mParameters.get("antibanding"));
|
|
setBrightness();
|
|
// FIXME: set nightshot, luma adaptatiom, zoom and check ranges
|
|
|
|
LOGV("startPreview X");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t QualcommCameraHardware::startPreview(preview_callback cb, void *user)
|
|
{
|
|
LOGV("startPreview E");
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
{
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
mPreviewCallback = cb;
|
|
mPreviewCallbackCookie = user;
|
|
}
|
|
|
|
return startPreviewInternal();
|
|
}
|
|
|
|
void QualcommCameraHardware::stopPreviewInternal()
|
|
{
|
|
LOGV("stopPreviewInternal E: %d", mCameraRunning);
|
|
if (mCameraRunning) {
|
|
// Cancel auto focus.
|
|
if (mAutoFocusCallback) {
|
|
{
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
mAutoFocusCallback = NULL;
|
|
mAutoFocusCallbackCookie = NULL;
|
|
}
|
|
cancelAutoFocus();
|
|
}
|
|
|
|
mCameraRunning = !native_stop_preview(mCameraControlFd);
|
|
if (!mCameraRunning && mPreviewInitialized) {
|
|
deinitPreview();
|
|
mPreviewInitialized = false;
|
|
}
|
|
else LOGE("stopPreviewInternal: failed to stop preview");
|
|
}
|
|
LOGV("stopPreviewInternal X: %d", mCameraRunning);
|
|
}
|
|
|
|
void QualcommCameraHardware::stopPreview()
|
|
{
|
|
LOGV("stopPreview: E");
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
{
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
mPreviewCallback = NULL;
|
|
mPreviewCallbackCookie = NULL;
|
|
if(mRecordingCallback != NULL)
|
|
return;
|
|
}
|
|
|
|
stopPreviewInternal();
|
|
|
|
LOGV("stopPreview: X");
|
|
}
|
|
|
|
void QualcommCameraHardware::runAutoFocus()
|
|
{
|
|
mAutoFocusThreadLock.lock();
|
|
mAutoFocusFd = open(MSM_CAMERA_CONTROL, O_RDWR);
|
|
if (mAutoFocusFd < 0) {
|
|
LOGE("autofocus: cannot open %s: %s",
|
|
MSM_CAMERA_CONTROL,
|
|
strerror(errno));
|
|
mAutoFocusThreadRunning = false;
|
|
mAutoFocusThreadLock.unlock();
|
|
return;
|
|
}
|
|
|
|
#if DLOPEN_LIBMMCAMERA
|
|
// We need to maintain a reference to libqcamera.so for the duration of the
|
|
// AF thread, because we do not know when it will exit relative to the
|
|
// lifetime of this object. We do not want to dlclose() libqcamera while
|
|
// LINK_cam_frame is still running.
|
|
void *libhandle = ::dlopen("libqcamera.so", RTLD_NOW);
|
|
LOGV("AF: loading libqcamera at %p", libhandle);
|
|
if (!libhandle) {
|
|
LOGE("FATAL ERROR: could not dlopen libqcamera.so: %s", dlerror());
|
|
close(mAutoFocusFd);
|
|
mAutoFocusFd = -1;
|
|
mAutoFocusThreadRunning = false;
|
|
mAutoFocusThreadLock.unlock();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* This will block until either AF completes or is cancelled. */
|
|
LOGV("af start (fd %d)", mAutoFocusFd);
|
|
bool status = native_set_afmode(mAutoFocusFd, AF_MODE_AUTO);
|
|
LOGV("af done: %d", (int)status);
|
|
mAutoFocusThreadRunning = false;
|
|
close(mAutoFocusFd);
|
|
mAutoFocusFd = -1;
|
|
mAutoFocusThreadLock.unlock();
|
|
|
|
mCallbackLock.lock();
|
|
autofocus_callback cb = mAutoFocusCallback;
|
|
void *data = mAutoFocusCallbackCookie;
|
|
mCallbackLock.unlock();
|
|
if (cb != NULL)
|
|
cb(status, data);
|
|
|
|
mCallbackLock.lock();
|
|
mAutoFocusCallback = NULL;
|
|
mAutoFocusCallbackCookie = NULL;
|
|
mCallbackLock.unlock();
|
|
|
|
#if DLOPEN_LIBMMCAMERA
|
|
if (libhandle) {
|
|
::dlclose(libhandle);
|
|
LOGV("AF: dlclose(libqcamera)");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void QualcommCameraHardware::cancelAutoFocus()
|
|
{
|
|
LOGV("cancelAutoFocus E");
|
|
native_cancel_afmode(mCameraControlFd, mAutoFocusFd);
|
|
LOGV("cancelAutoFocus X");
|
|
}
|
|
|
|
void *auto_focus_thread(void *user)
|
|
{
|
|
LOGV("auto_focus_thread E");
|
|
sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
|
|
if (obj != 0) {
|
|
obj->runAutoFocus();
|
|
}
|
|
else LOGW("not starting autofocus: the object went away!");
|
|
LOGV("auto_focus_thread X");
|
|
return NULL;
|
|
}
|
|
|
|
status_t QualcommCameraHardware::autoFocus(autofocus_callback af_cb,
|
|
void *user)
|
|
{
|
|
LOGV("autoFocus E");
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
if (mCameraControlFd < 0) {
|
|
LOGE("not starting autofocus: main control fd %d", mCameraControlFd);
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (mAutoFocusCallback != NULL) {
|
|
LOGW("Auto focus is already in progress");
|
|
return mAutoFocusCallback == af_cb ? NO_ERROR : INVALID_OPERATION;
|
|
}
|
|
|
|
{
|
|
Mutex::Autolock cbl(&mCallbackLock);
|
|
mAutoFocusCallback = af_cb;
|
|
mAutoFocusCallbackCookie = user;
|
|
}
|
|
|
|
{
|
|
mAutoFocusThreadLock.lock();
|
|
if (!mAutoFocusThreadRunning) {
|
|
// Create a detatched thread here so that we don't have to wait
|
|
// for it when we cancel AF.
|
|
pthread_t thr;
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
mAutoFocusThreadRunning =
|
|
!pthread_create(&thr, &attr,
|
|
auto_focus_thread, NULL);
|
|
if (!mAutoFocusThreadRunning) {
|
|
LOGE("failed to start autofocus thread");
|
|
mAutoFocusThreadLock.unlock();
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
mAutoFocusThreadLock.unlock();
|
|
}
|
|
|
|
LOGV("autoFocus X");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void QualcommCameraHardware::runSnapshotThread(void *data)
|
|
{
|
|
LOGV("runSnapshotThread E");
|
|
if (native_start_snapshot(mCameraControlFd))
|
|
receiveRawPicture();
|
|
else
|
|
LOGE("main: native_start_snapshot failed!");
|
|
|
|
mSnapshotThreadWaitLock.lock();
|
|
mSnapshotThreadRunning = false;
|
|
mSnapshotThreadWait.signal();
|
|
mSnapshotThreadWaitLock.unlock();
|
|
|
|
LOGV("runSnapshotThread X");
|
|
}
|
|
|
|
void *snapshot_thread(void *user)
|
|
{
|
|
LOGD("snapshot_thread E");
|
|
sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
|
|
if (obj != 0) {
|
|
obj->runSnapshotThread(user);
|
|
}
|
|
else LOGW("not starting snapshot thread: the object went away!");
|
|
LOGD("snapshot_thread X");
|
|
return NULL;
|
|
}
|
|
|
|
status_t QualcommCameraHardware::takePicture(shutter_callback shutter_cb,
|
|
raw_callback raw_cb,
|
|
jpeg_callback jpeg_cb,
|
|
void *user)
|
|
{
|
|
LOGV("takePicture: E raw_cb = %p, jpeg_cb = %p",
|
|
raw_cb, jpeg_cb);
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
stopPreviewInternal();
|
|
|
|
if (!initRaw(jpeg_cb != NULL)) {
|
|
LOGE("initRaw failed. Not taking picture.");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
{
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
mShutterCallback = shutter_cb;
|
|
mRawPictureCallback = raw_cb;
|
|
mJpegPictureCallback = jpeg_cb;
|
|
mPictureCallbackCookie = user;
|
|
}
|
|
|
|
mSnapshotThreadWaitLock.lock();
|
|
while (mSnapshotThreadRunning) {
|
|
LOGV("takePicture: waiting for old snapshot thread to complete.");
|
|
mSnapshotThreadWait.wait(mSnapshotThreadWaitLock);
|
|
LOGV("takePicture: old snapshot thread completed.");
|
|
}
|
|
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
mSnapshotThreadRunning = !pthread_create(&mSnapshotThread,
|
|
&attr,
|
|
snapshot_thread,
|
|
NULL);
|
|
mSnapshotThreadWaitLock.unlock();
|
|
|
|
LOGV("takePicture: X");
|
|
return mSnapshotThreadRunning ? NO_ERROR : UNKNOWN_ERROR;
|
|
}
|
|
|
|
status_t QualcommCameraHardware::cancelPicture(
|
|
bool cancel_shutter, bool cancel_raw, bool cancel_jpeg)
|
|
{
|
|
LOGV("cancelPicture: E cancel_shutter = %d, "
|
|
"cancel_raw = %d, cancel_jpeg = %d",
|
|
cancel_shutter, cancel_raw, cancel_jpeg);
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
{
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
if (cancel_shutter) mShutterCallback = NULL;
|
|
if (cancel_raw) mRawPictureCallback = NULL;
|
|
if (cancel_jpeg) mJpegPictureCallback = NULL;
|
|
}
|
|
|
|
LOGV("cancelPicture: X");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t QualcommCameraHardware::setParameters(
|
|
const CameraParameters& params)
|
|
{
|
|
LOGV("setParameters: E params = %p", ¶ms);
|
|
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
preview_size_type *ps = preview_sizes;
|
|
|
|
{
|
|
int width, height;
|
|
params.getPreviewSize(&width, &height);
|
|
LOGV("requested size %d x %d", width, height);
|
|
// Validate the preview size
|
|
size_t i;
|
|
for (i = 0; i < PREVIEW_SIZE_COUNT; ++i, ++ps) {
|
|
if (width == ps->width && height == ps->height)
|
|
break;
|
|
}
|
|
if (i == PREVIEW_SIZE_COUNT) {
|
|
LOGE("Invalid preview size requested: %dx%d",
|
|
width, height);
|
|
return BAD_VALUE;
|
|
}
|
|
}
|
|
|
|
mPreviewWidth = mDimension.display_width = ps->width;
|
|
mPreviewHeight = mDimension.display_height = ps->height;
|
|
|
|
// FIXME: validate snapshot sizes,
|
|
|
|
params.getPictureSize(&mRawWidth, &mRawHeight);
|
|
mDimension.picture_width = mRawWidth;
|
|
mDimension.picture_height = mRawHeight;
|
|
|
|
// Set up the jpeg-thumbnail-size parameters.
|
|
|
|
{
|
|
int val;
|
|
|
|
val = params.getInt("jpeg-thumbnail-width");
|
|
if (val < 0) {
|
|
mDimension.ui_thumbnail_width= THUMBNAIL_WIDTH;
|
|
LOGW("jpeg-thumbnail-width is not specified: defaulting to %d",
|
|
THUMBNAIL_WIDTH);
|
|
}
|
|
else mDimension.ui_thumbnail_width = val;
|
|
|
|
val = params.getInt("jpeg-thumbnail-height");
|
|
if (val < 0) {
|
|
mDimension.ui_thumbnail_height= THUMBNAIL_HEIGHT;
|
|
LOGW("jpeg-thumbnail-height is not specified: defaulting to %d",
|
|
THUMBNAIL_HEIGHT);
|
|
}
|
|
else mDimension.ui_thumbnail_height = val;
|
|
}
|
|
|
|
mParameters = params;
|
|
|
|
if (mCameraRunning)
|
|
{
|
|
setBrightness();
|
|
|
|
mZoomValueCurr = mParameters.getInt("zoom");
|
|
if(mZoomValueCurr >= 0 && mZoomValueCurr <= ZOOM_MAX &&
|
|
mZoomValuePrev != mZoomValueCurr)
|
|
{
|
|
bool ZoomDirectionIn = true;
|
|
if(mZoomValuePrev > mZoomValueCurr)
|
|
{
|
|
ZoomDirectionIn = false;
|
|
}
|
|
else
|
|
{
|
|
ZoomDirectionIn = true;
|
|
}
|
|
LOGV("new zoom value: %d direction = %s",
|
|
mZoomValueCurr, (ZoomDirectionIn ? "in" : "out"));
|
|
mZoomValuePrev = mZoomValueCurr;
|
|
performZoom(ZoomDirectionIn);
|
|
}
|
|
|
|
setSensorPreviewEffect(mCameraControlFd, mParameters.get("effect"));
|
|
setSensorWBLighting(mCameraControlFd, mParameters.get("whitebalance"));
|
|
setAntiBanding(mCameraControlFd, mParameters.get("antibanding"));
|
|
// FIXME: set nightshot, luma adaptatiom, zoom and check ranges
|
|
}
|
|
|
|
LOGV("setParameters: X");
|
|
return NO_ERROR ;
|
|
}
|
|
|
|
CameraParameters QualcommCameraHardware::getParameters() const
|
|
{
|
|
LOGV("getParameters: EX");
|
|
return mParameters;
|
|
}
|
|
|
|
extern "C" sp<CameraHardwareInterface> openCameraHardware()
|
|
{
|
|
LOGV("openCameraHardware: call createInstance");
|
|
return QualcommCameraHardware::createInstance();
|
|
}
|
|
|
|
wp<QualcommCameraHardware> QualcommCameraHardware::singleton;
|
|
|
|
// If the hardware already exists, return a strong pointer to the current
|
|
// object. If not, create a new hardware object, put it in the singleton,
|
|
// and return it.
|
|
sp<CameraHardwareInterface> QualcommCameraHardware::createInstance()
|
|
{
|
|
LOGD("createInstance: E");
|
|
|
|
Mutex::Autolock lock(&singleton_lock);
|
|
|
|
// Wait until the previous release is done.
|
|
while (singleton_releasing) {
|
|
LOGD("Wait for previous release.");
|
|
singleton_wait.wait(singleton_lock);
|
|
}
|
|
|
|
if (singleton != 0) {
|
|
sp<CameraHardwareInterface> hardware = singleton.promote();
|
|
if (hardware != 0) {
|
|
LOGD("createInstance: X return existing hardware=%p", &(*hardware));
|
|
return hardware;
|
|
}
|
|
}
|
|
|
|
{
|
|
struct stat st;
|
|
int rc = stat("/dev/oncrpc", &st);
|
|
if (rc < 0) {
|
|
LOGD("createInstance: X failed to create hardware: %s", strerror(errno));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
QualcommCameraHardware *cam = new QualcommCameraHardware();
|
|
sp<QualcommCameraHardware> hardware(cam);
|
|
singleton = hardware;
|
|
|
|
cam->startCamera();
|
|
cam->initDefaultParameters();
|
|
LOGD("createInstance: X created hardware=%p", &(*hardware));
|
|
return hardware;
|
|
}
|
|
|
|
// For internal use only, hence the strong pointer to the derived type.
|
|
sp<QualcommCameraHardware> QualcommCameraHardware::getInstance()
|
|
{
|
|
sp<CameraHardwareInterface> hardware = singleton.promote();
|
|
if (hardware != 0) {
|
|
// LOGV("getInstance: X old instance of hardware");
|
|
return sp<QualcommCameraHardware>(static_cast<QualcommCameraHardware*>(hardware.get()));
|
|
} else {
|
|
LOGV("getInstance: X new instance of hardware");
|
|
return sp<QualcommCameraHardware>();
|
|
}
|
|
}
|
|
|
|
void QualcommCameraHardware::receivePreviewFrame(struct msm_frame *frame)
|
|
{
|
|
// LOGV("receivePreviewFrame E");
|
|
|
|
if (!mCameraRunning) {
|
|
LOGE("ignoring preview callback--camera has been stopped");
|
|
return;
|
|
}
|
|
|
|
mCallbackLock.lock();
|
|
preview_callback pcb = mPreviewCallback;
|
|
void *pdata = mPreviewCallbackCookie;
|
|
recording_callback rcb = mRecordingCallback;
|
|
void *rdata = mRecordingCallbackCookie;
|
|
mCallbackLock.unlock();
|
|
|
|
// Find the offset within the heap of the current buffer.
|
|
ssize_t offset =
|
|
(ssize_t)frame->buffer - (ssize_t)mPreviewHeap->mHeap->base();
|
|
offset /= mPreviewFrameSize;
|
|
|
|
//LOGV("%d\n", offset);
|
|
|
|
mInPreviewCallback = true;
|
|
if (pcb != NULL)
|
|
pcb(mPreviewHeap->mBuffers[offset],
|
|
pdata);
|
|
|
|
if(rcb != NULL) {
|
|
Mutex::Autolock rLock(&mRecordFrameLock);
|
|
rcb(systemTime(), mPreviewHeap->mBuffers[offset], rdata);
|
|
if (mReleasedRecordingFrame != true) {
|
|
LOGV("block for release frame request/command");
|
|
mRecordWait.wait(mRecordFrameLock);
|
|
}
|
|
mReleasedRecordingFrame = false;
|
|
}
|
|
mInPreviewCallback = false;
|
|
|
|
// LOGV("receivePreviewFrame X");
|
|
}
|
|
|
|
status_t QualcommCameraHardware::startRecording(
|
|
recording_callback rcb, void *ruser)
|
|
{
|
|
LOGV("startRecording E");
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
{
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
mRecordingCallback = rcb;
|
|
mRecordingCallbackCookie = ruser;
|
|
}
|
|
|
|
mReleasedRecordingFrame = false;
|
|
|
|
return startPreviewInternal();
|
|
}
|
|
|
|
void QualcommCameraHardware::stopRecording()
|
|
{
|
|
LOGV("stopRecording: E");
|
|
Mutex::Autolock l(&mLock);
|
|
|
|
{
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
mRecordingCallback = NULL;
|
|
mRecordingCallbackCookie = NULL;
|
|
|
|
mRecordFrameLock.lock();
|
|
mReleasedRecordingFrame = true;
|
|
mRecordWait.signal();
|
|
mRecordFrameLock.unlock();
|
|
|
|
if(mPreviewCallback != NULL) {
|
|
LOGV("stopRecording: X, preview still in progress");
|
|
return;
|
|
}
|
|
}
|
|
|
|
stopPreviewInternal();
|
|
LOGV("stopRecording: X");
|
|
}
|
|
|
|
void QualcommCameraHardware::releaseRecordingFrame(
|
|
const sp<IMemory>& mem __attribute__((unused)))
|
|
{
|
|
LOGV("releaseRecordingFrame E");
|
|
Mutex::Autolock l(&mLock);
|
|
Mutex::Autolock rLock(&mRecordFrameLock);
|
|
mReleasedRecordingFrame = true;
|
|
mRecordWait.signal();
|
|
LOGV("releaseRecordingFrame X");
|
|
}
|
|
|
|
bool QualcommCameraHardware::recordingEnabled()
|
|
{
|
|
Mutex::Autolock l(&mLock);
|
|
return mCameraRunning && mRecordingCallback != NULL;
|
|
}
|
|
|
|
void QualcommCameraHardware::notifyShutter()
|
|
{
|
|
if (mShutterCallback)
|
|
mShutterCallback(mPictureCallbackCookie);
|
|
}
|
|
|
|
static void receive_shutter_callback()
|
|
{
|
|
LOGV("receive_shutter_callback: E");
|
|
sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
|
|
if (obj != 0) {
|
|
obj->notifyShutter();
|
|
}
|
|
LOGV("receive_shutter_callback: X");
|
|
}
|
|
|
|
void QualcommCameraHardware::receiveRawPicture()
|
|
{
|
|
LOGV("receiveRawPicture: E");
|
|
|
|
int ret,rc,rete;
|
|
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
if (mRawPictureCallback != NULL) {
|
|
if(native_get_picture(mCameraControlFd, &mCrop)== false) {
|
|
LOGE("getPicture failed!");
|
|
return;
|
|
}
|
|
mRawPictureCallback(mRawHeap->mBuffers[0],
|
|
mPictureCallbackCookie);
|
|
}
|
|
else LOGV("Raw-picture callback was canceled--skipping.");
|
|
|
|
if (mJpegPictureCallback != NULL) {
|
|
mJpegSize = 0;
|
|
if (LINK_jpeg_encoder_init()) {
|
|
if(native_jpeg_encode()) {
|
|
LOGV("receiveRawPicture: X (success)");
|
|
return;
|
|
}
|
|
LOGE("jpeg encoding failed");
|
|
}
|
|
else LOGE("receiveRawPicture X: jpeg_encoder_init failed.");
|
|
}
|
|
else LOGV("JPEG callback is NULL, not encoding image.");
|
|
deinitRaw();
|
|
LOGV("receiveRawPicture: X");
|
|
}
|
|
|
|
void QualcommCameraHardware::receiveJpegPictureFragment(
|
|
uint8_t *buff_ptr, uint32_t buff_size)
|
|
{
|
|
uint32_t remaining = mJpegHeap->mHeap->virtualSize();
|
|
remaining -= mJpegSize;
|
|
uint8_t *base = (uint8_t *)mJpegHeap->mHeap->base();
|
|
|
|
LOGV("receiveJpegPictureFragment size %d", buff_size);
|
|
if (buff_size > remaining) {
|
|
LOGE("receiveJpegPictureFragment: size %d exceeds what "
|
|
"remains in JPEG heap (%d), truncating",
|
|
buff_size,
|
|
remaining);
|
|
buff_size = remaining;
|
|
}
|
|
memcpy(base + mJpegSize, buff_ptr, buff_size);
|
|
mJpegSize += buff_size;
|
|
}
|
|
|
|
void QualcommCameraHardware::receiveJpegPicture(void)
|
|
{
|
|
LOGV("receiveJpegPicture: E image (%d uint8_ts out of %d)",
|
|
mJpegSize, mJpegHeap->mBufferSize);
|
|
Mutex::Autolock cbLock(&mCallbackLock);
|
|
|
|
int index = 0, rc;
|
|
|
|
if (mJpegPictureCallback) {
|
|
// The reason we do not allocate into mJpegHeap->mBuffers[offset] is
|
|
// that the JPEG image's size will probably change from one snapshot
|
|
// to the next, so we cannot reuse the MemoryBase object.
|
|
sp<MemoryBase> buffer = new
|
|
MemoryBase(mJpegHeap->mHeap,
|
|
index * mJpegHeap->mBufferSize +
|
|
mJpegHeap->mFrameOffset,
|
|
mJpegSize);
|
|
|
|
mJpegPictureCallback(buffer, mPictureCallbackCookie);
|
|
buffer = NULL;
|
|
}
|
|
else LOGV("JPEG callback was cancelled--not delivering image.");
|
|
|
|
LINK_jpeg_encoder_join();
|
|
deinitRaw();
|
|
|
|
LOGV("receiveJpegPicture: X callback done.");
|
|
}
|
|
|
|
bool QualcommCameraHardware::previewEnabled()
|
|
{
|
|
// Mutex::Autolock l(&mLock);
|
|
return mCameraRunning && mPreviewCallback != NULL;
|
|
}
|
|
|
|
void QualcommCameraHardware::setSensorPreviewEffect(int camfd, const char *effect)
|
|
{
|
|
LOGV("In setSensorPreviewEffect...");
|
|
int effectsValue = 1;
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_SET_PARM_EFFECT;
|
|
ctrlCmd.length = sizeof(uint32_t);
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
effectsValue = attr_lookup(color_effects, effect, CAMERA_EFFECT_OFF);
|
|
ctrlCmd.value = (void *)&effectsValue;
|
|
LOGV("In setSensorPreviewEffect, color effect match %s %d",
|
|
effect, effectsValue);
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0)
|
|
LOGE("setSensorPreviewEffect fd %d error %s", camfd, strerror(errno));
|
|
}
|
|
|
|
void QualcommCameraHardware::setSensorWBLighting(int camfd, const char *lighting)
|
|
{
|
|
int lightingValue = 1;
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_SET_PARM_WB;
|
|
ctrlCmd.length = sizeof(uint32_t);
|
|
lightingValue = attr_lookup(whitebalance, lighting, CAMERA_WB_AUTO);
|
|
ctrlCmd.value = (void *)&lightingValue;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
LOGV("In setSensorWBLighting: match: %s: %d",
|
|
lighting, lightingValue);
|
|
if (ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0)
|
|
LOGE("setSensorWBLighting: ioctl fd %d error %s",
|
|
camfd, strerror(errno));
|
|
}
|
|
|
|
void QualcommCameraHardware::setAntiBanding(int camfd, const char *antibanding)
|
|
{
|
|
int antibandvalue = 0;
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_SET_PARM_ANTIBANDING;
|
|
ctrlCmd.length = sizeof(int32_t);
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
antibandvalue = attr_lookup(anti_banding,
|
|
antibanding,
|
|
/* FIXME:
|
|
* CAMERA_ANTIBANDING_60HZ broke the barcode scanner
|
|
* somehow. turn it off and revert it back to off
|
|
* for now until we figure out what is the best
|
|
* solution.
|
|
*/
|
|
CAMERA_ANTIBANDING_OFF /*CAMERA_ANTIBANDING_60HZ */);
|
|
ctrlCmd.value = (void *)&antibandvalue;
|
|
LOGV("In setAntiBanding: match: %s: %d",
|
|
antibanding, antibandvalue);
|
|
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0)
|
|
LOGE("setAntiBanding: ioctl %d error %s",
|
|
camfd, strerror(errno));
|
|
}
|
|
|
|
void QualcommCameraHardware::setBrightness()
|
|
{
|
|
int val = mParameters.getInt("exposure-offset");
|
|
if (val < 0)
|
|
val = BRIGHTNESS_DEF;
|
|
else if (val > BRIGHTNESS_MAX)
|
|
val = BRIGHTNESS_MAX;
|
|
|
|
if (mBrightness != val) {
|
|
LOGV("new brightness value %d", val);
|
|
mBrightness = val;
|
|
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
LOGV("In setBrightness: %d", val);
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.type = CAMERA_SET_PARM_BRIGHTNESS;
|
|
ctrlCmd.length = sizeof(int);
|
|
ctrlCmd.value = (void *)&val;
|
|
// FIXME: this will be put in by the kernel
|
|
ctrlCmd.resp_fd = mCameraControlFd;
|
|
|
|
if(ioctl(mCameraControlFd,
|
|
MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0)
|
|
LOGE("setBrightness: ioctl fd %d error %s",
|
|
mCameraControlFd, strerror(errno));
|
|
}
|
|
}
|
|
|
|
static bool native_get_zoom(int camfd, void *pZm)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
cam_parm_info_t *pZoom = (cam_parm_info_t *)pZm;
|
|
ctrlCmd.type = CAMERA_GET_PARM_ZOOM;
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.length = sizeof(cam_parm_info_t);
|
|
ctrlCmd.value = pZoom;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
|
|
LOGE("native_get_zoom: ioctl fd %d error %s",
|
|
camfd, strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
memcpy(pZoom, *(cam_parm_info_t **)ctrlCmd.value, sizeof(cam_parm_info_t));
|
|
|
|
LOGD("native_get_zoom::current val=%d max=%d min=%d step val=%d",
|
|
pZoom->current_value,
|
|
pZoom->maximum_value,
|
|
pZoom->minimum_value,
|
|
pZoom->step_value);
|
|
|
|
return ctrlCmd.status;
|
|
}
|
|
|
|
static bool native_set_zoom(int camfd, void *pZm)
|
|
{
|
|
struct msm_ctrl_cmd ctrlCmd;
|
|
|
|
int32_t *pZoom = (int32_t *)pZm;
|
|
|
|
ctrlCmd.type = CAMERA_SET_PARM_ZOOM;
|
|
ctrlCmd.timeout_ms = 5000;
|
|
ctrlCmd.length = sizeof(int32_t);
|
|
ctrlCmd.value = pZoom;
|
|
ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
|
|
|
|
if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
|
|
LOGE("native_set_zoom: ioctl fd %d error %s",
|
|
camfd, strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
memcpy(pZoom, (int32_t *)ctrlCmd.value, sizeof(int32_t));
|
|
return ctrlCmd.status;
|
|
}
|
|
|
|
void QualcommCameraHardware::performZoom(bool ZoomDir)
|
|
{
|
|
if(mZoomInitialised == false) {
|
|
native_get_zoom(mCameraControlFd, (void *)&mZoom);
|
|
if(mZoom.maximum_value != 0) {
|
|
mZoomInitialised = true;
|
|
mZoom.step_value = (int) (mZoom.maximum_value/MAX_ZOOM_STEPS);
|
|
if( mZoom.step_value > 3 )
|
|
mZoom.step_value = 3;
|
|
}
|
|
}
|
|
|
|
if (ZoomDir) {
|
|
LOGV("performZoom::got zoom value of %d %d %d zoom in",
|
|
mZoom.current_value,
|
|
mZoom.step_value,
|
|
mZoom.maximum_value);
|
|
if((mZoom.current_value + mZoom.step_value) < mZoom.maximum_value) {
|
|
mZoom.current_value += mZoom.step_value;
|
|
LOGV("performZoom::Setting Zoom value of %d ",mZoom.current_value);
|
|
native_set_zoom(mCameraControlFd, (void *)&mZoom.current_value);
|
|
}
|
|
else {
|
|
LOGV("performZoom::not able to zoom in %d %d %d",
|
|
mZoom.current_value,
|
|
mZoom.step_value,
|
|
mZoom.maximum_value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGV("performZoom::got zoom value of %d %d %d zoom out",
|
|
mZoom.current_value,
|
|
mZoom.step_value,
|
|
mZoom.minimum_value);
|
|
if((mZoom.current_value - mZoom.step_value) >= mZoom.minimum_value)
|
|
{
|
|
mZoom.current_value -= mZoom.step_value;
|
|
LOGV("performZoom::setting zoom value of %d ",
|
|
mZoom.current_value);
|
|
native_set_zoom(mCameraControlFd, (void *)&mZoom.current_value);
|
|
}
|
|
else
|
|
{
|
|
LOGV("performZoom::not able to zoom out %d %d %d",
|
|
mZoom.current_value,
|
|
mZoom.step_value,
|
|
mZoom.maximum_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
QualcommCameraHardware::MemPool::MemPool(int buffer_size, int num_buffers,
|
|
int frame_size,
|
|
int frame_offset,
|
|
const char *name) :
|
|
mBufferSize(buffer_size),
|
|
mNumBuffers(num_buffers),
|
|
mFrameSize(frame_size),
|
|
mFrameOffset(frame_offset),
|
|
mBuffers(NULL), mName(name)
|
|
{
|
|
// empty
|
|
}
|
|
|
|
void QualcommCameraHardware::MemPool::completeInitialization()
|
|
{
|
|
// If we do not know how big the frame will be, we wait to allocate
|
|
// the buffers describing the individual frames until we do know their
|
|
// size.
|
|
|
|
if (mFrameSize > 0) {
|
|
mBuffers = new sp<MemoryBase>[mNumBuffers];
|
|
for (int i = 0; i < mNumBuffers; i++) {
|
|
mBuffers[i] = new
|
|
MemoryBase(mHeap,
|
|
i * mBufferSize + mFrameOffset,
|
|
mFrameSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
QualcommCameraHardware::AshmemPool::AshmemPool(int buffer_size, int num_buffers,
|
|
int frame_size,
|
|
int frame_offset,
|
|
const char *name) :
|
|
QualcommCameraHardware::MemPool(buffer_size,
|
|
num_buffers,
|
|
frame_size,
|
|
frame_offset,
|
|
name)
|
|
{
|
|
LOGV("constructing MemPool %s backed by ashmem: "
|
|
"%d frames @ %d uint8_ts, offset %d, "
|
|
"buffer size %d",
|
|
mName,
|
|
num_buffers, frame_size, frame_offset, buffer_size);
|
|
|
|
int page_mask = getpagesize() - 1;
|
|
int ashmem_size = buffer_size * num_buffers;
|
|
ashmem_size += page_mask;
|
|
ashmem_size &= ~page_mask;
|
|
|
|
mHeap = new MemoryHeapBase(ashmem_size);
|
|
|
|
completeInitialization();
|
|
}
|
|
|
|
static bool register_buf(int camfd,
|
|
int size,
|
|
int pmempreviewfd,
|
|
uint32_t offset,
|
|
uint8_t *buf,
|
|
int pmem_type,
|
|
bool register_buffer = true);
|
|
|
|
QualcommCameraHardware::PmemPool::PmemPool(const char *pmem_pool,
|
|
int camera_control_fd,
|
|
int pmem_type,
|
|
int buffer_size, int num_buffers,
|
|
int frame_size,
|
|
int frame_offset,
|
|
const char *name) :
|
|
QualcommCameraHardware::MemPool(buffer_size,
|
|
num_buffers,
|
|
frame_size,
|
|
frame_offset,
|
|
name),
|
|
mPmemType(pmem_type),
|
|
mCameraControlFd(dup(camera_control_fd))
|
|
{
|
|
LOGV("constructing MemPool %s backed by pmem pool %s: "
|
|
"%d frames @ %d bytes, offset %d, buffer size %d",
|
|
mName,
|
|
pmem_pool, num_buffers, frame_size, frame_offset,
|
|
buffer_size);
|
|
|
|
LOGV("%s: duplicating control fd %d --> %d",
|
|
__FUNCTION__,
|
|
camera_control_fd, mCameraControlFd);
|
|
|
|
// Make a new mmap'ed heap that can be shared across processes.
|
|
|
|
mAlignedSize = clp2(buffer_size * num_buffers);
|
|
|
|
sp<MemoryHeapBase> masterHeap =
|
|
new MemoryHeapBase(pmem_pool, mAlignedSize, 0);
|
|
sp<MemoryHeapPmem> pmemHeap = new MemoryHeapPmem(masterHeap, 0);
|
|
if (pmemHeap->getHeapID() >= 0) {
|
|
pmemHeap->slap();
|
|
masterHeap.clear();
|
|
mHeap = pmemHeap;
|
|
pmemHeap.clear();
|
|
|
|
mFd = mHeap->getHeapID();
|
|
if (::ioctl(mFd, PMEM_GET_SIZE, &mSize)) {
|
|
LOGE("pmem pool %s ioctl(PMEM_GET_SIZE) error %s (%d)",
|
|
pmem_pool,
|
|
::strerror(errno), errno);
|
|
mHeap.clear();
|
|
return;
|
|
}
|
|
|
|
LOGV("pmem pool %s ioctl(fd = %d, PMEM_GET_SIZE) is %ld",
|
|
pmem_pool,
|
|
mFd,
|
|
mSize.len);
|
|
|
|
// Unregister preview buffers with the camera drivers.
|
|
for (int cnt = 0; cnt < num_buffers; ++cnt) {
|
|
register_buf(mCameraControlFd,
|
|
buffer_size,
|
|
mHeap->getHeapID(),
|
|
buffer_size * cnt,
|
|
(uint8_t *)mHeap->base() + buffer_size * cnt,
|
|
pmem_type);
|
|
}
|
|
|
|
completeInitialization();
|
|
}
|
|
else LOGE("pmem pool %s error: could not create master heap!",
|
|
pmem_pool);
|
|
}
|
|
|
|
QualcommCameraHardware::PmemPool::~PmemPool()
|
|
{
|
|
LOGV("%s: %s E", __FUNCTION__, mName);
|
|
// Unregister preview buffers with the camera drivers.
|
|
for (int cnt = 0; cnt < mNumBuffers; ++cnt) {
|
|
register_buf(mCameraControlFd,
|
|
mBufferSize,
|
|
mHeap->getHeapID(),
|
|
mBufferSize * cnt,
|
|
(uint8_t *)mHeap->base() + mBufferSize * cnt,
|
|
mPmemType,
|
|
false /* unregister */);
|
|
}
|
|
LOGV("destroying PmemPool %s: closing control fd %d",
|
|
mName,
|
|
mCameraControlFd);
|
|
close(mCameraControlFd);
|
|
LOGV("%s: %s X", __FUNCTION__, mName);
|
|
}
|
|
|
|
QualcommCameraHardware::MemPool::~MemPool()
|
|
{
|
|
LOGV("destroying MemPool %s", mName);
|
|
if (mFrameSize > 0)
|
|
delete [] mBuffers;
|
|
mHeap.clear();
|
|
LOGV("destroying MemPool %s completed", mName);
|
|
}
|
|
|
|
static bool register_buf(int camfd,
|
|
int size,
|
|
int pmempreviewfd,
|
|
uint32_t offset,
|
|
uint8_t *buf,
|
|
int pmem_type,
|
|
bool register_buffer)
|
|
{
|
|
struct msm_pmem_info pmemBuf;
|
|
|
|
pmemBuf.type = pmem_type;
|
|
pmemBuf.fd = pmempreviewfd;
|
|
pmemBuf.offset = offset;
|
|
pmemBuf.len = size;
|
|
pmemBuf.vaddr = buf;
|
|
pmemBuf.y_off = 0;
|
|
pmemBuf.cbcr_off = PAD_TO_WORD(size * 2 / 3);
|
|
pmemBuf.active = true;
|
|
|
|
LOGV("register_buf: camfd = %d, reg = %d buffer = %p",
|
|
camfd, !register_buffer, buf);
|
|
if (ioctl(camfd,
|
|
register_buffer ?
|
|
MSM_CAM_IOCTL_REGISTER_PMEM :
|
|
MSM_CAM_IOCTL_UNREGISTER_PMEM,
|
|
&pmemBuf) < 0) {
|
|
LOGE("register_buf: MSM_CAM_IOCTL_(UN)REGISTER_PMEM fd %d error %s",
|
|
camfd,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
status_t QualcommCameraHardware::MemPool::dump(int fd, const Vector<String16>& args) const
|
|
{
|
|
const size_t SIZE = 256;
|
|
char buffer[SIZE];
|
|
String8 result;
|
|
snprintf(buffer, 255, "QualcommCameraHardware::AshmemPool::dump\n");
|
|
result.append(buffer);
|
|
if (mName) {
|
|
snprintf(buffer, 255, "mem pool name (%s)\n", mName);
|
|
result.append(buffer);
|
|
}
|
|
if (mHeap != 0) {
|
|
snprintf(buffer, 255, "heap base(%p), size(%d), flags(%d), device(%s)\n",
|
|
mHeap->getBase(), mHeap->getSize(),
|
|
mHeap->getFlags(), mHeap->getDevice());
|
|
result.append(buffer);
|
|
}
|
|
snprintf(buffer, 255, "buffer size (%d), number of buffers (%d),"
|
|
" frame size(%d), and frame offset(%d)\n",
|
|
mBufferSize, mNumBuffers, mFrameSize, mFrameOffset);
|
|
result.append(buffer);
|
|
write(fd, result.string(), result.size());
|
|
return NO_ERROR;
|
|
}
|
|
|
|
static void receive_camframe_callback(struct msm_frame *frame)
|
|
{
|
|
sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
|
|
if (obj != 0) {
|
|
obj->receivePreviewFrame(frame);
|
|
}
|
|
}
|
|
|
|
static void receive_jpeg_fragment_callback(uint8_t *buff_ptr, uint32_t buff_size)
|
|
{
|
|
LOGV("receive_jpeg_fragment_callback E");
|
|
sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
|
|
if (obj != 0) {
|
|
obj->receiveJpegPictureFragment(buff_ptr, buff_size);
|
|
}
|
|
LOGV("receive_jpeg_fragment_callback X");
|
|
}
|
|
|
|
static void receive_jpeg_callback(jpeg_event_t status)
|
|
{
|
|
LOGV("receive_jpeg_callback E (completion status %d)", status);
|
|
if (status == JPEG_EVENT_DONE) {
|
|
sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
|
|
if (obj != 0) {
|
|
obj->receiveJpegPicture();
|
|
}
|
|
}
|
|
LOGV("receive_jpeg_callback X");
|
|
}
|
|
|
|
}; // namespace android
|