blob: 2a39f2007d5d9b64482fabf817fb5c02d719ce13 [file] [log] [blame]
/*
** 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 PREVIEW_SIZE_COUNT (sizeof(preview_sizes)/sizeof(preview_size_type))
#define NOT_FOUND -1
#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
};
static int attr_lookup(const struct str_map *const arr, const char *name)
{
if (name) {
const struct str_map *trav = arr;
while (trav->desc) {
if (!strcmp(trav->desc, name))
return trav->val;
trav++;
}
}
return NOT_FOUND;
}
#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 },
{ "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 effect[] = {
{ "none", CAMERA_EFFECT_OFF }, /* This list must match aeecamera.h */
{ "mono", CAMERA_EFFECT_MONO },
{ "negative", CAMERA_EFFECT_NEGATIVE },
{ "solarize", CAMERA_EFFECT_SOLARIZE },
{ "sepia", CAMERA_EFFECT_SEPIA },
{ "posterize", CAMERA_EFFECT_POSTERIZE },
{ "whiteboard", CAMERA_EFFECT_WHITEBOARD },
{ "blackboard", CAMERA_EFFECT_BLACKBOARD },
{ "aqua", CAMERA_EFFECT_AQUA },
{ NULL, 0 }
};
static char *effect_values;
// from qcamera/common/camera.h
static const str_map antibanding[] = {
{ "off", CAMERA_ANTIBANDING_OFF },
{ NULL, 0 }
};
static char *antibanding_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),
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(&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-quality", "100"); // maximum quality
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);
p.set("antibanding", "off");
p.set("effect", "none");
p.set("whitebalance", "auto");
#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(antibanding);
INIT_VALUES_FOR(effect);
INIT_VALUES_FOR(whitebalance);
p.set("antibanding-values", antibanding_values);
p.set("effect-values", effect_values);
p.set("whitebalance-values", whitebalance_values);
p.set("picture-size-values", "2048x1536,1600x1200,1024x768");
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;
}
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;
}
bool QualcommCameraHardware::native_set_dimension(cam_ctrl_dimension_t *value)
{
return native_set_parm(CAMERA_SET_PARM_DIMENSION,
sizeof(cam_ctrl_dimension_t), value);
}
bool QualcommCameraHardware::native_set_parm(
cam_ctrl_type type, uint16_t length, void *value)
{
int rc = true;
struct msm_ctrl_cmd ctrlCmd;
ctrlCmd.timeout_ms = 5000;
ctrlCmd.type = (uint16_t)type;
ctrlCmd.length = length;
// FIXME: this will be put in by the kernel
ctrlCmd.resp_fd = mCameraControlFd;
ctrlCmd.value = value;
LOGV("native_set_parm. camfd=%d, type=%d, length=%d",
mCameraControlFd, type, length);
rc = ioctl(mCameraControlFd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd);
if(rc < 0 || ctrlCmd.status != CAM_CTRL_SUCCESS) {
LOGE("ioctl error. camfd=%d, type=%d, length=%d, rc=%d, ctrlCmd.status=%d, %s",
mCameraControlFd, type, length, rc, ctrlCmd.status, strerror(errno));
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(&mDimension);
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(&mDimension)) {
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;
}
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);
// Wait for old snapshot thread to complete.
mSnapshotThreadWaitLock.lock();
while (mSnapshotThreadRunning) {
LOGV("takePicture: waiting for old snapshot thread to complete.");
mSnapshotThreadWait.wait(mSnapshotThreadWaitLock);
LOGV("takePicture: old snapshot thread completed.");
}
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;
}
mShutterLock.lock();
mShutterPending = true;
mShutterLock.unlock();
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", &params);
Mutex::Autolock l(&mLock);
// Set preview size.
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;
setAntibanding();
setEffect();
setWhiteBalance();
// FIXME: set nightshot and luma adaptatiom
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()
{
mShutterLock.lock();
if (mShutterPending && mShutterCallback) {
mShutterCallback(mPictureCallbackCookie);
mShutterPending = false;
}
mShutterLock.unlock();
}
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");
Mutex::Autolock cbLock(&mCallbackLock);
if (mRawPictureCallback != NULL) {
if(native_get_picture(mCameraControlFd, &mCrop) == false) {
LOGE("getPicture failed!");
return;
}
// By the time native_get_picture returns, picture is taken. Call
// shutter callback if cam config thread has not done that.
notifyShutter();
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;
}
int QualcommCameraHardware::getParm(
const char *parm_str, const struct str_map *const parm_map)
{
// Check if the parameter exists.
const char *str = mParameters.get(parm_str);
if (str == NULL) return NOT_FOUND;
// Look up the parameter value.
return attr_lookup(parm_map, str);
}
void QualcommCameraHardware::setEffect()
{
int32_t value = getParm("effect", effect);
if (value != NOT_FOUND) {
native_set_parm(CAMERA_SET_PARM_EFFECT, sizeof(value), (void *)&value);
}
}
void QualcommCameraHardware::setWhiteBalance()
{
int32_t value = getParm("whitebalance", whitebalance);
if (value != NOT_FOUND) {
native_set_parm(CAMERA_SET_PARM_WB, sizeof(value), (void *)&value);
}
}
void QualcommCameraHardware::setAntibanding()
{
camera_antibanding_type value =
(camera_antibanding_type) getParm("antibanding", antibanding);
native_set_parm(CAMERA_SET_PARM_ANTIBANDING, sizeof(value), (void *)&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