diff --git a/Android.mk b/Android.mk index 9beabbb..ae4ced6 100644 --- a/Android.mk +++ b/Android.mk @@ -17,6 +17,7 @@ subdir_makefiles := \ $(LOCAL_PATH)/libcopybit/Android.mk \ $(LOCAL_PATH)/libgralloc/Android.mk \ $(LOCAL_PATH)/liblights/Android.mk \ + $(LOCAL_PATH)/libcamera/Android.mk \ $(LOCAL_PATH)/libsensors/Android.mk include $(subdir_makefiles) diff --git a/libcamera/Android.mk b/libcamera/Android.mk new file mode 100644 index 0000000..36ec046 --- /dev/null +++ b/libcamera/Android.mk @@ -0,0 +1,41 @@ +BOARD_USES_OLD_CAMERA_HACK:=true + +ifeq ($(BOARD_USES_OLD_CAMERA_HACK),true) +BUILD_LIBCAMERA:= true +ifeq ($(BUILD_LIBCAMERA),true) + +# When zero we link against libmmcamera; when 1, we dlopen libmmcamera. +DLOPEN_LIBMMCAMERA:=1 + +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS:=optional + +LOCAL_SRC_FILES:= QualcommCameraHardware.cpp exifwriter.c + +LOCAL_CFLAGS:= -DDLOPEN_LIBMMCAMERA=$(DLOPEN_LIBMMCAMERA) + +LOCAL_C_INCLUDES+= \ + vendor/qcom/proprietary/mm-camera/common \ + vendor/qcom/proprietary/mm-camera/apps/appslib \ + external/jhead \ + vendor/qcom/proprietary/mm-camera/jpeg/inc + +LOCAL_SHARED_LIBRARIES:= libbinder libutils libcamera_client liblog + +ifneq ($(DLOPEN_LIBMMCAMERA),1) +LOCAL_SHARED_LIBRARIES+= libmmcamera libmm-qcamera-tgt +else +LOCAL_SHARED_LIBRARIES+= libdl libexif +endif + +LOCAL_MODULE:= libcamera +include $(BUILD_SHARED_LIBRARY) + +endif # BUILD_TINY_ANDROID +endif # BUILD_LIBCAMERA +endif # BOARD_USES_OLD_CAMERA_HACK diff --git a/libcamera/QualcommCameraHardware.cpp b/libcamera/QualcommCameraHardware.cpp new file mode 100644 index 0000000..4495047 --- /dev/null +++ b/libcamera/QualcommCameraHardware.cpp @@ -0,0 +1,2135 @@ +/* +** 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 + +#include "QualcommCameraHardware.h" + +#include +#include +#include +#include +#include +#include +#if HAVE_ANDROID_OS +#include +#endif +#include + +#define LIKELY(exp) __builtin_expect(!!(exp), 1) +#define UNLIKELY(exp) __builtin_expect(!!(exp), 0) + +#define iLog(fmt, args...) LOGD(fmt, ##args) // enable debug + +extern "C" { +#include "exifwriter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_camera.h" // HTC kernel header + +#define THUMBNAIL_WIDTH 512 // got from Donut locat +#define THUMBNAIL_HEIGHT 384 +#define THUMBNAIL_WIDTH_STR "512" +#define THUMBNAIL_HEIGHT_STR "384" +#define DEFAULT_PICTURE_WIDTH 2048 +#define DEFAULT_PICTURE_HEIGHT 1536 +#define THUMBNAIL_BUFFER_SIZE (THUMBNAIL_WIDTH * THUMBNAIL_HEIGHT * 3/2) +#define DEFAULT_PREVIEW_SETTING 5 // QVGA +#define DEFAULT_FRAMERATE 15 +#define PREVIEW_SIZE_COUNT (sizeof(preview_sizes)/sizeof(preview_size_type)) + +#define NOT_FOUND -1 + +#if DLOPEN_LIBMMCAMERA +#include + +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);*/ +unsigned char (*LINK_jpeg_encoder_encode)(const char* file_name, const cam_ctrl_dimension_t *dimen, + const unsigned char* thumbnailbuf, int thumbnailfd, + const unsigned char* snapshotbuf, int snapshotfd, common_crop_t *cropInfo); +int (*LINK_camframe_terminate)(void); +int8_t (*LINK_jpeg_encoder_setMainImageQuality)(uint32_t quality); +// Tattoo +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_t *frame); +void (**LINK_mmcamera_jpegfragment_callback)(uint8_t *buff_ptr, + uint32_t buff_size); +void (**LINK_mmcamera_jpeg_callback)(jpeg_event_t status); +#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_t *frame); +extern void (*mmcamera_jpegfragment_callback)(uint8_t *buff_ptr, + uint32_t buff_size); +extern void (*mmcamera_jpeg_callback)(jpeg_event_t status); +#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 + { 192, 144 }, // Donut Tattoo + { 252, 189 }, // Donut Tattoo +}; + +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 }, + { "fluorescent", 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 }, + { "50hz", CAMERA_ANTIBANDING_50HZ }, + { "60hz", CAMERA_ANTIBANDING_60HZ }, + { "auto", CAMERA_ANTIBANDING_AUTO }, + { 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_t *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 int camerafd; +pthread_t w_thread; + +void *opencamerafd(void *arg) { + camerafd = open(MSM_CAMERA_CONTROL, O_RDWR); + if (camerafd < 0) + LOGE("Camera control %s open failed: %s!", MSM_CAMERA_CONTROL, strerror(errno)); + else + iLog("opening %s fd: %d", MSM_CAMERA_CONTROL, camerafd); + + return NULL; +} + +QualcommCameraHardware::QualcommCameraHardware() + : mParameters(), + mPreviewHeight(-1), + mPreviewWidth(-1), + mRawHeight(-1), + mRawWidth(-1), + mCameraRunning(false), + mPreviewInitialized(false), + // mRawInitialized(false), + mFrameThreadRunning(false), + mSnapshotThreadRunning(false), + mReleasedRecordingFrame(false), + mNotifyCb(0), + mDataCb(0), + mDataCbTimestamp(0), + mCallbackCookie(0), + mMsgEnabled(0), + mPreviewFrameSize(0), + mRawSize(0), + mCameraControlFd(-1), + mAutoFocusThreadRunning(false), + mAutoFocusFd(-1), + mInPreviewCallback(false), + mCameraRecording(false) +{ + if((pthread_create(&w_thread, NULL, opencamerafd, NULL)) != 0) { + LOGE("Camera open thread creation failed") ; + } + memset(&mDimension, 0, sizeof(mDimension)); + memset(&mCrop, 0, sizeof(mCrop)); + iLog("constructor EX"); +} + +void QualcommCameraHardware::initDefaultParameters() +{ + CameraParameters p; + + iLog("initDefaultParameters E"); + + preview_size_type *ps = &preview_sizes[DEFAULT_PREVIEW_SETTING]; + p.setPreviewSize(ps->width, ps->height); + + p.setPreviewFrameRate(DEFAULT_FRAMERATE); + p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP); // informative + p.setPictureFormat(CameraParameters::PIXEL_FORMAT_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", "85"); + + + p.setPictureSize(DEFAULT_PICTURE_WIDTH, DEFAULT_PICTURE_HEIGHT); + p.set(CameraParameters::KEY_ANTIBANDING, CameraParameters::ANTIBANDING_OFF); + p.set(CameraParameters::KEY_EFFECT, CameraParameters::EFFECT_NONE); + p.set(CameraParameters::KEY_WHITE_BALANCE, CameraParameters::WHITE_BALANCE_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); + + // Create de supported camera values + p.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING, antibanding_values); + p.set(CameraParameters::KEY_SUPPORTED_EFFECTS, effect_values); + p.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE, whitebalance_values); + + // Set display camera supported sizes + // ----------------------------------------------------- + p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "2048x1536,1600x1200,1024x768"); + p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "512x348,320x240,192x144,252x189"); + + if (setParameters(p) != NO_ERROR) { + LOGE("Failed to set default parameters?!"); + } + + iLog("initDefaultParameters X"); + +} + +void QualcommCameraHardware::setCallbacks(notify_callback notify_cb, + data_callback data_cb, + data_callback_timestamp data_cb_timestamp, + void* user) +{ + Mutex::Autolock lock(mLock); + mNotifyCb = notify_cb; + mDataCb = data_cb; + mDataCbTimestamp = data_cb_timestamp; + mCallbackCookie = user; +} + +void QualcommCameraHardware::enableMsgType(int32_t msgType) +{ + Mutex::Autolock lock(mLock); + LOGD("enableMsgType( %d )", msgType); + mMsgEnabled |= msgType; +} + +void QualcommCameraHardware::disableMsgType(int32_t msgType) +{ + Mutex::Autolock lock(mLock); + LOGD("DisableMsgType( %d )", msgType); + mMsgEnabled &= ~msgType; +} + +bool QualcommCameraHardware::msgTypeEnabled(int32_t msgType) +{ + Mutex::Autolock lock(mLock); + LOGD("msgTypeEnabled( %d )", msgType); + return (mMsgEnabled & msgType); +} + + +#define ROUND_TO_PAGE(x) (((x)+0xfff)&~0xfff) + +void QualcommCameraHardware::startCamera() +{ + unsigned char sync_value; + + iLog("startCamera E"); +#if DLOPEN_LIBMMCAMERA + libmmcamera = ::dlopen("libmmcamera.so", RTLD_NOW); + iLog("loading libmmcamera at %p", libmmcamera); + if (!libmmcamera) { + LOGE("FATAL ERROR: could not dlopen liboemcamera.so: %s", dlerror()); + return; + } + + libmmcamera_target = ::dlopen("libmm-qcamera-tgt.so", RTLD_NOW); + iLog("loading libmm-qcamera-tgt at %p", libmmcamera_target); + if (!libmmcamera_target) { + LOGE("FATAL ERROR: could not dlopen libmm-qcamera_target.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, "mmframe_cb"); + + *(void **)&LINK_mmcamera_camframe_callback = + ::dlsym(libmmcamera, "mmframe_cb"); + + + *LINK_mmcamera_camframe_callback = receive_camframe_callback; + + *(void **)&LINK_mmcamera_jpegfragment_callback = + ::dlsym(libmmcamera, "mm_jpegfragment_callback"); + + *LINK_mmcamera_jpegfragment_callback = receive_jpeg_fragment_callback; + + *(void **)&LINK_mmcamera_jpeg_callback = + ::dlsym(libmmcamera, "mm_jpeg_callback"); + + *LINK_mmcamera_jpeg_callback = receive_jpeg_callback; + + *(void**)&LINK_jpeg_encoder_setMainImageQuality = + ::dlsym(libmmcamera, "jpeg_encoder_setMainImageQuality"); + + *(void **)&LINK_cam_conf = + ::dlsym(libmmcamera_target, "cam_conf"); +#else + mmcamera_camframe_callback = receive_camframe_callback; + mmcamera_jpegfragment_callback = receive_jpeg_fragment_callback; + mmcamera_jpeg_callback = receive_jpeg_callback; +#endif // DLOPEN_LIBMMCAMERA + + /* The control thread is in libcamera itself. */ + iLog("pthread_join on control thread"); + if (pthread_join(w_thread, NULL) != 0) { + LOGE("Camera open thread exit failed"); + return; + } + + mCameraControlFd = camerafd; + + if (!LINK_jpeg_encoder_init()) { + LOGE("jpeg_encoding_init failed.\n"); + } + + if ((pthread_create(&mCamConfigThread, NULL, LINK_cam_conf, NULL)) != 0) + LOGE("Config thread creation failed!"); + else + iLog("cam_conf thread created"); + + iLog("startCamera X"); +} + +status_t QualcommCameraHardware::dump(int fd, + const Vector& 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_t ctrlCmd; + + ctrlCmd.timeout_ms = 5000; + ctrlCmd.type = CAMERA_SET_PARM_AUTO_FOCUS; + ctrlCmd.length = sizeof(af_type); + ctrlCmd.value = &af_type; + + if ((rc = ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd)) < 0) + LOGE("native_set_afmode: ioctl fd %d error %s\n", + camfd, + strerror(errno)); + + iLog("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_t ctrlCmd; + + ctrlCmd.timeout_ms = 5000; + ctrlCmd.type = CAMERA_AUTO_FOCUS_CANCEL; + ctrlCmd.length = 0; + + if ((rc = ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &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_t ctrlCmd; + + ctrlCmd.timeout_ms = 5000; + ctrlCmd.type = CAMERA_START_PREVIEW; + ctrlCmd.length = 0; + + 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_t 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; + } + + iLog("crop: in1_w %d", crop->in1_w); + iLog("crop: in1_h %d", crop->in1_h); + iLog("crop: out1_w %d", crop->out1_w); + iLog("crop: out1_h %d", crop->out1_h); + + iLog("crop: in2_w %d", crop->in2_w); + iLog("crop: in2_h %d", crop->in2_h); + iLog("crop: out2_w %d", crop->out2_w); + iLog("crop: out2_h %d", crop->out2_h); + + iLog("crop: update %d", crop->update_flag); + + + return true; +} + +static bool native_stop_preview(int camfd) +{ + struct msm_ctrl_cmd_t ctrlCmd; + ctrlCmd.timeout_ms = 5000; + ctrlCmd.type = CAMERA_STOP_PREVIEW; + ctrlCmd.length = 0; + + 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_t ctrlCmd; + + ctrlCmd.timeout_ms = 5000; + ctrlCmd.type = CAMERA_START_SNAPSHOT; + ctrlCmd.length = 0; + + 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_t ctrlCmd; + + ctrlCmd.timeout_ms = 5000; + ctrlCmd.type = CAMERA_STOP_SNAPSHOT; + ctrlCmd.length = 0; + + 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; +} + +void *jpeg_encoder_thread( void *user ) +{ + LOGD("jpeg_encoder_thread E"); + sp obj = QualcommCameraHardware::getInstance(); + if (obj != 0) { + obj->runJpegEncodeThread(user); + } + else LOGW("not starting frame thread: the object went away!"); + LOGD("jpeg_encoder_thread X"); + return NULL; +} + + +bool QualcommCameraHardware::native_jpeg_encode(void) +{ + int jpeg_quality = mParameters.getInt("jpeg-quality"); + if (jpeg_quality >= 0) { + iLog("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) { + iLog("native_jpeg_encode, current jpeg thumbnail quality =%d", + thumbnail_quality); + } + + int rotation = mParameters.getInt("rotation"); + if (rotation >= 0) { + iLog("native_jpeg_encode, rotation = %d", rotation); + } + char jpegFileName[256] = {0}; + static int snapshotCntr = 0; + + mDimension.filler7 = 2560 ; + mDimension.filler8 = 1920 ; + + + // ******************************** SACADO DE NOPY ********************************************* + LOGD("picture_width %d, picture_height = %d, display_width = %d, display_height = %d, filler = %d, filler2 = %d, ui_thumbnail_height = %d , ui_thumbnail_width = %d, filler3 = %d, filler4 = %d, filler5 = %d, filler6 = %d, filler7 = %d, filler8 = %d\n" , + mDimension.picture_width,mDimension.picture_height, + mDimension.display_width,mDimension.display_height, + mDimension.filler, mDimension.filler2, + mDimension.ui_thumbnail_height, mDimension.ui_thumbnail_width, + mDimension.filler3, mDimension.filler4, mDimension.filler5, mDimension.filler6, + mDimension.filler7, mDimension.filler8 ); + + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_t jpegThread ; + + int ret = !pthread_create(&jpegThread, + &attr, //NULL, + jpeg_encoder_thread, + NULL); + // ********************************************************************************************* + +/* sprintf(jpegFileName, "snapshot_%d.jpg", ++snapshotCntr); + if ( !LINK_jpeg_encoder_encode(jpegFileName, &mDimension, + (uint8_t *)mThumbnailHeap->mHeap->base(), mThumbnailHeap->mHeap->getHeapID(), + (uint8_t *)mRawHeap->mHeap->base(), mRawHeap->mHeap->getHeapID(), + &mCrop)) { + LOGE("native_jpeg_encode:%d@%s: jpeg_encoder_encode failed.\n", __LINE__, __FILE__); + 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."); + iLog("FAKE ENCODER "); + return true; + // FAKE return false; + }*/ + return true; +} + +bool QualcommCameraHardware::native_set_dimension(cam_ctrl_dimension_t *value) +{ + iLog("native_set_dimension: E"); + return native_set_parm(CAMERA_SET_PARM_DIMENSION, + sizeof(cam_ctrl_dimension_t), value); + iLog("native_set_dimension: X"); +} + +bool QualcommCameraHardware::native_set_parm( + cam_ctrl_type type, uint16_t length, void *value) +{ + int rc = true; + struct msm_ctrl_cmd_t ctrlCmd; + + ctrlCmd.timeout_ms = 5000; + ctrlCmd.type = (uint16_t)type; + ctrlCmd.length = length; + ctrlCmd.value = value; + + iLog("native_set_parm: type: %d, length=%d", 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); \ + iLog("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 { \ + iLog("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); + } + else iLog("not setting image location"); +} + +void QualcommCameraHardware::runFrameThread(void *data) +{ + iLog("runFrameThread E"); + +#if DLOPEN_LIBMMCAMERA + // We need to maintain a reference to libmmcamera.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() libmmcamera while + // LINK_cam_frame is still running. + void *libhandle = ::dlopen("libmmcamera.so", RTLD_NOW); + iLog("FRAME: loading libmmcamera at %p", libhandle); + if (!libhandle) { + LOGE("FATAL ERROR: could not dlopen libmmcamera.so: %s", dlerror()); + } + if (libhandle) +#endif + { + iLog("Before LINK_cam_frame"); + LINK_cam_frame(data); + iLog("After LINK_cam_frame"); + } + +#if DLOPEN_LIBMMCAMERA + if (libhandle) { + ::dlclose(libhandle); + iLog("FRAME: dlclose(libmmcamera)"); + } +#endif + + mFrameThreadWaitLock.lock(); + mFrameThreadRunning = false; + mFrameThreadWait.signal(); + mFrameThreadWaitLock.unlock(); + + iLog("runFrameThread X"); +} + +void QualcommCameraHardware::runJpegEncodeThread(void *data) +{ + unsigned char *buffer ; + +// readFromMemory( (unsigned char *)mRawHeap->mHeap->base(), 2097152, buffer ) ; +// writeToMemory( buffer, 2560, 1920, (unsigned char *)mJpegHeap->mHeap->base(), (int *)&mJpegSize ) ; + + int rotation = mParameters.getInt("rotation"); + LOGD("native_jpeg_encode, rotation = %d", rotation); + + 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); \ + LOGD("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 { \ + LOGD("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); + } + else { + LOGV("not setting image location"); + + } + + LOGD("mJpegSize %d" , mJpegSize ) ; + + camera_position_type *npt = &pt ; + if( ! encode_location ) { + npt = NULL ; + } + writeExif( mRawHeap->mHeap->base(), mJpegHeap->mHeap->base(), mJpegSize, &mJpegSize, rotation , npt ) ; + + receiveJpegPicture(); +} + +void *frame_thread(void *user) +{ + iLog("frame_thread E"); + sp obj = QualcommCameraHardware::getInstance(); + if (obj != 0) { + obj->runFrameThread(user); + } + else LOGW("not starting frame thread: the object went away!"); + iLog("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(). + iLog("initPreview E: preview size=%dx%d", mPreviewWidth, mPreviewHeight); + + mFrameThreadWaitLock.lock(); + mFrameThreadRunning = false; + while (mFrameThreadRunning) { + iLog("initPreview: waiting for old frame thread to complete."); + mFrameThreadWait.wait(mFrameThreadWaitLock); + iLog("initPreview: old frame thread completed."); + } + mFrameThreadWaitLock.unlock(); + + mSnapshotThreadWaitLock.lock(); + while (mSnapshotThreadRunning) { + iLog("initPreview: waiting for old snapshot thread to complete."); + mSnapshotThreadWait.wait(mSnapshotThreadWaitLock); + iLog("initPreview: old snapshot thread completed."); + } + mSnapshotThreadWaitLock.unlock(); + + mPreviewFrameSize = mPreviewWidth * mPreviewHeight * 3/2; // actual + mPreviewHeap = new PreviewPmemPool(mCameraControlFd, + mPreviewWidth * mPreviewHeight * 2, // worst + 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 (int 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; + if (ret) + iLog("Preview thread created"); + mFrameThreadWaitLock.unlock(); + } + + iLog("initPreview X"); + return ret; +} + +void QualcommCameraHardware::deinitPreview(void) +{ + iLog("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)); + else + iLog("terminate frame_thread successfully"); + + iLog("deinitPreview X"); +} + +bool QualcommCameraHardware::initRaw(bool initJpegHeap) +{ + iLog("initRaw E: picture size=%dx%d", + mRawWidth, mRawHeight); // 2048x1536 + + 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) { + iLog("initRaw: clearing old mJpegHeap."); + mJpegHeap.clear(); + } + +/* + * Order sequence found in Donut logcat: + * 1: Thumbnail camera backed by pmem pool /dev/msm_adsp + * 2: Snapshot camera backed by pmem pool /dev/msm_camera + * 3: Jpeg backed by ashmem + */ + + // Thumbnails + + iLog("initRaw: initializing mThumbHeap. with size %d", THUMBNAIL_BUFFER_SIZE); + mThumbnailHeap = + new PmemPool("/dev/pmem_adsp", + mCameraControlFd, + MSM_PMEM_THUMBNAIL, + THUMBNAIL_BUFFER_SIZE, + 1, + THUMBNAIL_BUFFER_SIZE, + 0, + "thumbnail camera"); + + if (!mThumbnailHeap->initialized()) { + mThumbnailHeap.clear(); + mJpegHeap.clear(); + mRawHeap.clear(); + LOGE("initRaw X failed: error initializing mThumbnailHeap."); + return false; + } + + // Snapshot + + iLog("initRaw: initializing mRawHeap. with size %d", mRawSize); // 4718592 + 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; + } + } + + iLog("do_mmap snapshot pbuf = %p, pmem_fd = %d", + (uint8_t *)mRawHeap->mHeap->base(), mRawHeap->mHeap->getHeapID()); + + // Jpeg + + if (initJpegHeap) { + iLog("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; + } + } + + mRawInitialized = true; + + iLog("initRaw X success"); + return true; +} + +void QualcommCameraHardware::deinitRaw() +{ + iLog("deinitRaw E"); + mThumbnailHeap.clear(); + mJpegHeap.clear(); + mRawHeap.clear(); + + mRawInitialized = false; + iLog("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 liboemcamera!" +#endif + + int rc; + struct msm_ctrl_cmd_t ctrlCmd; + + if (mCameraRunning) { + if (mMsgEnabled & CAMERA_MSG_VIDEO_FRAME) { + mRecordFrameLock.lock(); + mReleasedRecordingFrame = true; + mRecordWait.signal(); + mRecordFrameLock.unlock(); + } + stopPreviewInternal(); + } + + //FIXME: crash when released + // LINK_jpeg_encoder_join(); + + if (mRawInitialized) deinitRaw(); + + iLog("CAMERA_EXIT"); + + ctrlCmd.timeout_ms = 5000; + ctrlCmd.length = 0; + ctrlCmd.type = (uint16_t)CAMERA_EXIT; + + if (ioctl(mCameraControlFd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) + LOGE("ioctl CAMERA_EXIT fd %d error %s", + mCameraControlFd, strerror(errno)); + + iLog("Stopping the conf thread"); + rc = pthread_join(mCamConfigThread, NULL); + if (rc) + LOGE("config_thread exit failure: %s", strerror(errno)); + else { + iLog("pthread_join on config_thread"); + } + + close(mCameraControlFd); + mCameraControlFd = -1; + +#if DLOPEN_LIBMMCAMERA + if (libmmcamera) { + ::dlclose(libmmcamera); + iLog("dlclose(libmmcamera)"); + libmmcamera = NULL; + } + if (libmmcamera_target) { + ::dlclose(libmmcamera_target); + iLog("dlclose(libmmcamera_target)"); + libmmcamera_target = 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 QualcommCameraHardware::getRawHeap() const +{ + iLog("getRawHeap"); + return mRawHeap != NULL ? mRawHeap->mHeap : NULL; +} + +sp QualcommCameraHardware::getPreviewHeap() const +{ + iLog("getPreviewHeap"); + return mPreviewHeap != NULL ? mPreviewHeap->mHeap : NULL; +} + +status_t QualcommCameraHardware::startPreviewInternal() +{ + iLog("startPreview E"); + + if(mCameraRunning) { + iLog("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; + } + } + +/* + int ret; + unsigned long timeout = 2000; // 2s + struct pollfd fds[] = { + { mCameraControlFd, POLLIN | POLLRDNORM }, + }; + + // polling, found in Donut dmesg. + ret = poll(fds, 1, timeout); + switch (ret) { + case -1: + LOGE("poll error"); + break; + case 0: + LOGE("poll timeout"); + break; + default: + if (fds[0].revents & POLLIN) { + iLog("frame is ready!"); + } + } + */ + + mCameraRunning = native_start_preview(mCameraControlFd); + if (!mCameraRunning) { + deinitPreview(); + mPreviewInitialized = false; + LOGE("startPreview X: native_start_preview failed!"); + return UNKNOWN_ERROR; + } + + iLog("startPreview X"); + return NO_ERROR; +} + +status_t QualcommCameraHardware::startPreview() +{ + Mutex::Autolock l(&mLock); + return startPreviewInternal(); +} + +void QualcommCameraHardware::stopPreviewInternal() +{ + iLog("stopPreviewInternal E: %d", mCameraRunning); + if (mCameraRunning) { + // Cancel auto focus. + if (mMsgEnabled & CAMERA_MSG_FOCUS) { + iLog("canceling autofocus"); + cancelAutoFocus(); + } + + iLog("Stopping preview"); + mCameraRunning = !native_stop_preview(mCameraControlFd); + if (!mCameraRunning && mPreviewInitialized) { + deinitPreview(); + mPreviewInitialized = false; + } + else LOGE("stopPreviewInternal: failed to stop preview"); + } + iLog("stopPreviewInternal X: %d", mCameraRunning); +} + +void QualcommCameraHardware::stopPreview() +{ + iLog("stopPreview: E"); + Mutex::Autolock l(&mLock); + + if(mMsgEnabled & CAMERA_MSG_VIDEO_FRAME) + return; + + stopPreviewInternal(); + + iLog("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 libmmcamera.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() libmmcamera while + // LINK_cam_frame is still running. + void *libhandle = ::dlopen("libmmcamera.so", RTLD_NOW); + iLog("AF: loading libmmcamera at %p", libhandle); + if (!libhandle) { + LOGE("FATAL ERROR: could not dlopen libmmcamera.so: %s", dlerror()); + close(mAutoFocusFd); + mAutoFocusFd = -1; + mAutoFocusThreadRunning = false; + mAutoFocusThreadLock.unlock(); + return; + } +#endif + + /* This will block until either AF completes or is cancelled. */ + iLog("af start (fd %d)", mAutoFocusFd); + bool status = native_set_afmode(mAutoFocusFd, AF_MODE_AUTO); + iLog("af done: %d", (int)status); + mAutoFocusThreadRunning = false; + close(mAutoFocusFd); + mAutoFocusFd = -1; + mAutoFocusThreadLock.unlock(); + + if (mMsgEnabled & CAMERA_MSG_FOCUS) + mNotifyCb(CAMERA_MSG_FOCUS, status, 0, mCallbackCookie); + +#if DLOPEN_LIBMMCAMERA + if (libhandle) { + ::dlclose(libhandle); + iLog("AF: dlclose(libmmcamera)"); + } +#endif +} + +status_t QualcommCameraHardware::cancelAutoFocus() +{ + iLog("cancelAutoFocus E"); + native_cancel_afmode(mCameraControlFd, mAutoFocusFd); + iLog("cancelAutoFocus X"); + + /* Needed for eclair camera PAI */ + return NO_ERROR; +} + +void *auto_focus_thread(void *user) +{ + iLog("auto_focus_thread E"); + sp obj = QualcommCameraHardware::getInstance(); + if (obj != 0) { + obj->runAutoFocus(); + } + else LOGW("not starting autofocus: the object went away!"); + iLog("auto_focus_thread X"); + return NULL; +} + +status_t QualcommCameraHardware::autoFocus() +{ + iLog("autoFocus E"); + Mutex::Autolock l(&mLock); + + if (mCameraControlFd < 0) { + LOGE("not starting autofocus: main control fd %d", mCameraControlFd); + return UNKNOWN_ERROR; + } + + /* Not sure this is still needed with new APIs .. + if (mMsgEnabled & CAMERA_MSG_FOCUS) { + LOGW("Auto focus is already in progress"); + return NO_ERROR; + // No idea how to rewrite this + //return mAutoFocusCallback == af_cb ? NO_ERROR : INVALID_OPERATION; + }*/ + + { + 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(); + } + + iLog("autoFocus X"); + return NO_ERROR; +} + +void QualcommCameraHardware::runSnapshotThread(void *data) +{ + iLog("runSnapshotThread E"); + if (native_start_snapshot(mCameraControlFd)) + receiveRawPicture(); + else + LOGE("main: native_start_snapshot failed!"); + + mSnapshotThreadWaitLock.lock(); + mSnapshotThreadRunning = false; + mSnapshotThreadWait.signal(); + mSnapshotThreadWaitLock.unlock(); + + iLog("runSnapshotThread X"); +} + +void *snapshot_thread(void *user) +{ + LOGD("snapshot_thread E"); + sp 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() +{ + iLog("takePicture: E"); + Mutex::Autolock l(&mLock); + + // Wait for old snapshot thread to complete. + mSnapshotThreadWaitLock.lock(); + while (mSnapshotThreadRunning) { + iLog("takePicture: waiting for old snapshot thread to complete."); + mSnapshotThreadWait.wait(mSnapshotThreadWaitLock); + iLog("takePicture: old snapshot thread completed."); + } + + stopPreviewInternal(); + + if (!initRaw(mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE)) { /* not sure if this is right */ + LOGE("initRaw failed. Not taking picture."); + return UNKNOWN_ERROR; + } + + 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(); + + iLog("takePicture: X"); + return mSnapshotThreadRunning ? NO_ERROR : UNKNOWN_ERROR; +} + +status_t QualcommCameraHardware::cancelPicture() +{ + iLog("cancelPicture: EX"); + + return NO_ERROR; +} + +status_t QualcommCameraHardware::setParameters( + const CameraParameters& params) +{ + iLog("setParameters: E params = %p", ¶ms); + + Mutex::Autolock l(&mLock); + + // Set preview size. + preview_size_type *ps = preview_sizes; + { + int width, height; + params.getPreviewSize(&width, &height); + iLog("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; + } + } + + // hardcoded sizes got from Donut logcat + mPreviewWidth = mDimension.display_width = 240; //ps->width; + mPreviewHeight = mDimension.display_height = 160; //ps->height; + + iLog("actual size %d x %d", mPreviewWidth, mPreviewHeight); + + // 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; + + setWhiteBalance(); + setEffect(); + setAntibanding(); + // FIXME: set nightshot and luma adaptatiom + + iLog("setParameters: X"); + return NO_ERROR ; +} + +CameraParameters QualcommCameraHardware::getParameters() const +{ + iLog("getParameters: EX"); + return mParameters; +} + +extern "C" sp openCameraHardware() +{ + iLog("[M a@openCameraHardware: call createInstance"); + return QualcommCameraHardware::createInstance(); +} + +wp 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 QualcommCameraHardware::createInstance() +{ + LOGD("createInstance: E"); + + iLog("get into singleton lock"); + 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 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 hardware(cam); + singleton = hardware; + + cam->startCamera(); // mCameraControlFd evaluated in this function + cam->initDefaultParameters(); // mCameraControlFd would be used to setParameters + + LOGD("createInstance: X created hardware=%p", &(*hardware)); + return hardware; +} + +// For internal use only, hence the strong pointer to the derived type. +sp QualcommCameraHardware::getInstance() +{ + iLog("getInstance: E"); + sp hardware = singleton.promote(); + if (hardware != 0) { + iLog("getInstance: X Search instance of hardware"); + return sp(static_cast(hardware.get())); + } else { + iLog("getInstance: X new instance of hardware"); + return sp(); + } + LOGE("Fail getInstance: X"); +} + +void QualcommCameraHardware::receivePreviewFrame(struct msm_frame_t *frame) +{ + iLog("receivePreviewFrame E"); + + if (!mCameraRunning) { + LOGE("ignoring preview callback--camera has been stopped"); + return; + } + + // Find the offset within the heap of the current buffer. + ssize_t offset = + (ssize_t)frame->buffer - (ssize_t)mPreviewHeap->mHeap->base(); + offset /= mPreviewFrameSize; + + iLog("offset: %lu\n", (unsigned long int)offset); + + mInPreviewCallback = true; + if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) + mDataCb(CAMERA_MSG_PREVIEW_FRAME, mPreviewHeap->mBuffers[offset], mCallbackCookie); + + if (mMsgEnabled & CAMERA_MSG_VIDEO_FRAME) { + Mutex::Autolock rLock(&mRecordFrameLock); + mDataCbTimestamp(systemTime(), CAMERA_MSG_VIDEO_FRAME, mPreviewHeap->mBuffers[offset], mCallbackCookie); /* guess? */ + //mDataCb(CAMERA_MSG_VIDEO_FRAME, mPreviewHeap->mBuffers[offset], mCallbackCookie); + + if (mReleasedRecordingFrame != true) { + iLog("block for release frame request/command"); + mRecordWait.wait(mRecordFrameLock); + } + mReleasedRecordingFrame = false; + } + + mInPreviewCallback = false; + + iLog("receivePreviewFrame X"); +} + +status_t QualcommCameraHardware::startRecording() +{ + iLog("startRecording E"); + Mutex::Autolock l(&mLock); + + mReleasedRecordingFrame = false; + mCameraRecording = true; + + return startPreviewInternal(); +} + +void QualcommCameraHardware::stopRecording() +{ + iLog("stopRecording: E"); + Mutex::Autolock l(&mLock); + + { + mRecordFrameLock.lock(); + mReleasedRecordingFrame = true; + mRecordWait.signal(); + mRecordFrameLock.unlock(); + + mCameraRecording = false; + + if(mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) { + iLog("stopRecording: X, preview still in progress"); + return; + } + } + + stopPreviewInternal(); + iLog("stopRecording: X"); +} + +void QualcommCameraHardware::releaseRecordingFrame( + const sp& mem __attribute__((unused))) +{ + iLog("releaseRecordingFrame E"); + Mutex::Autolock l(&mLock); + Mutex::Autolock rLock(&mRecordFrameLock); + mReleasedRecordingFrame = true; + mRecordWait.signal(); + iLog("releaseRecordingFrame X"); +} + +bool QualcommCameraHardware::recordingEnabled() +{ + return (mCameraRunning && mCameraRecording); +} + +void QualcommCameraHardware::notifyShutter() +{ + mShutterLock.lock(); + if (mShutterPending && (mMsgEnabled & CAMERA_MSG_SHUTTER)) { + mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie); + mShutterPending = false; + } + mShutterLock.unlock(); +} + +void QualcommCameraHardware::receiveRawPicture() +{ + iLog("receiveRawPicture: E"); + + notifyShutter(); + + if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) { + if(native_get_picture(mCameraControlFd, &mCrop) == false) { + LOGE("getPicture failed!"); + return; + } + + // By the time native_get_picture returns, picture is taken. Call + mDataCb(CAMERA_MSG_RAW_IMAGE, mRawHeap->mBuffers[0], mCallbackCookie); + } + else iLog("Raw-picture callback was canceled--skipping."); + + if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { + // mJpegSize = mRawWidth * mRawHeight * 3 / 2; + mJpegSize = 1718592; + if (LINK_jpeg_encoder_init()) { + if(native_jpeg_encode()) { + iLog("receiveRawPicture: X (success)"); + return; + } + LOGE("jpeg encoding failed"); + } + else LOGE("receiveRawPicture X: jpeg_encoder_init failed."); + } + else iLog("JPEG callback is NULL, not encoding image."); + deinitRaw(); + iLog("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(); + + iLog("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) +{ + iLog("receiveJpegPicture: E image (%d uint8_ts out of %d)", + mJpegSize, mJpegHeap->mBufferSize); + + LOGD("mJpegHeap->mFrameOffset %d", mJpegHeap->mFrameOffset ) ; + + int index = 0, rc; + + if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { + // 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 buffer = new + MemoryBase(mJpegHeap->mHeap, + index * mJpegHeap->mBufferSize + + mJpegHeap->mFrameOffset, + mJpegSize); + + mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, buffer, mCallbackCookie); + buffer = NULL; + } + else iLog("JPEG callback was cancelled--not delivering image."); + + LINK_jpeg_encoder_join(); + deinitRaw(); + + iLog("receiveJpegPicture: X callback done."); +} + +bool QualcommCameraHardware::previewEnabled() +{ + Mutex::Autolock l(&mLock); + return (mCameraRunning && (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)); +} + +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[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) +{ + iLog("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, + msm_pmem_t pmem_type, + bool active, + bool register_buffer = true); + +QualcommCameraHardware::PmemPool::PmemPool(const char *pmem_pool, + int camera_control_fd, + msm_pmem_t 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(camera_control_fd) +{ + iLog("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); + + // Make a new mmap'ed heap that can be shared across processes. + + mAlignedSize = clp2(buffer_size * num_buffers); + + sp masterHeap = + new MemoryHeapBase(pmem_pool, mAlignedSize, 0); + sp 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; + } + + iLog("pmem pool %s ioctl(fd = %d, PMEM_GET_SIZE) is %ld", + pmem_pool, + mFd, + mSize.len); + + // Register 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, true, + true); + } + + } + else LOGE("pmem pool %s error: could not create master heap!", + pmem_pool); + + completeInitialization(); +} + +QualcommCameraHardware::PmemPool::~PmemPool() +{ + iLog("%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, true, + false /* unregister */); + } + + iLog("destroying PmemPool %s: ", mName); + iLog("%s: %s X", __FUNCTION__, mName); +} + +QualcommCameraHardware::MemPool::~MemPool() +{ + iLog("destroying MemPool %s", mName); + if (mFrameSize > 0) + delete [] mBuffers; + mHeap.clear(); + iLog("destroying MemPool %s completed", mName); +} + +QualcommCameraHardware::PreviewPmemPool::PreviewPmemPool( + int control_fd, + int buffer_size, + int num_buffers, + int frame_size, + int frame_offset, + const char *name) : + QualcommCameraHardware::PmemPool("/dev/pmem_adsp", control_fd, MSM_PMEM_OUTPUT2, + buffer_size, + num_buffers, + frame_size, + frame_offset, + name) +{ + LOGV("QualcommCameraHardware::PreviewPmemPool::PreviewPmemPool"); + if (initialized()) { + //NOTE : SOME PREVIEWPMEMPOOL SPECIFIC CODE MAY BE ADDED + } +} + +QualcommCameraHardware::PreviewPmemPool::~PreviewPmemPool() +{ + iLog("destroying PreviewPmemPool"); + if (initialized()) { + LOGV("releasing PreviewPmemPool memory %p from module %d", + base, QDSP_MODULE_VFETASK); + } +} + +static bool register_buf(int camfd, + int size, + int pmempreviewfd, + uint32_t offset, + uint8_t *buf, + msm_pmem_t pmem_type, + bool active, + bool register_buffer) +{ + struct msm_pmem_info_t pmemBuf; + + pmemBuf.type = pmem_type; + pmemBuf.fd = pmempreviewfd; + pmemBuf.vaddr = buf; + pmemBuf.y_off = 0; + pmemBuf.active = active; + + if (pmem_type == MSM_PMEM_RAW_MAINIMG) + pmemBuf.cbcr_off = 0; + else + pmemBuf.cbcr_off = ((size * 2 / 3) + 1) & ~1; + + 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& 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_t *frame) +{ + iLog("receive_camframe_callback E"); + sp obj = QualcommCameraHardware::getInstance(); + if (obj != 0) { + iLog("receive_camframe_callback Starting PreviewFrame"); + obj->receivePreviewFrame(frame); + } + iLog("receive_camframe_callback X"); +} + +static void receive_jpeg_fragment_callback(uint8_t *buff_ptr, uint32_t buff_size) +{ + iLog("receive_jpeg_fragment_callback E"); + sp obj = QualcommCameraHardware::getInstance(); + if (obj != 0) { + obj->receiveJpegPictureFragment(buff_ptr, buff_size); + } + iLog("receive_jpeg_fragment_callback X"); +} + +static void receive_jpeg_callback(jpeg_event_t status) +{ + iLog("receive_jpeg_callback E (completion status %d)", status); + if (status == JPEG_EVENT_DONE) { + sp obj = QualcommCameraHardware::getInstance(); + if (obj != 0) { + obj->receiveJpegPicture(); + } + } + iLog("receive_jpeg_callback X"); +} + +status_t QualcommCameraHardware::sendCommand(int32_t command, int32_t arg1, + int32_t arg2) +{ + iLog("sendCommand: EX"); + return BAD_VALUE; +} + +}; // namespace android diff --git a/libcamera/QualcommCameraHardware.h b/libcamera/QualcommCameraHardware.h new file mode 100644 index 0000000..cfd64ab --- /dev/null +++ b/libcamera/QualcommCameraHardware.h @@ -0,0 +1,366 @@ +/* +** 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. +*/ + +#ifndef ANDROID_HARDWARE_QUALCOMM_CAMERA_HARDWARE_H +#define ANDROID_HARDWARE_QUALCOMM_CAMERA_HARDWARE_H + +#include +#include +#include +#include +#include + +extern "C" { +#include +#include "msm_camera.h" +} + +#define MSM_CAMERA_CONTROL "/dev/msm_camera/msm_camera0" +#define JPEG_EVENT_DONE 0 /* guess */ + +#define CAM_CTRL_SUCCESS 1 + +#define CAMERA_SET_PARM_DIMENSION 1 +#define CAMERA_SET_PARM_WB 14 +#define CAMERA_SET_PARM_EFFECT 15 +#define CAMERA_SET_PARM_ANTIBANDING 21 +#define CAMERA_STOP_PREVIEW 38 +#define CAMERA_START_PREVIEW 39 +#define CAMERA_EXIT 43 + +#define CAMERA_SET_PARM_AUTO_FOCUS 13 +#define CAMERA_START_SNAPSHOT 40 +#define CAMERA_STOP_SNAPSHOT 41 /* guess, but likely based on previos ording */ + +#define AF_MODE_AUTO 2 +#define CAMERA_AUTO_FOCUS_CANCEL 1 //204 + +typedef enum +{ + CAMERA_WB_MIN_MINUS_1, + CAMERA_WB_AUTO = 1, /* This list must match aeecamera.h */ + CAMERA_WB_CUSTOM, + CAMERA_WB_INCANDESCENT, + CAMERA_WB_FLUORESCENT, + CAMERA_WB_DAYLIGHT, + CAMERA_WB_CLOUDY_DAYLIGHT, + CAMERA_WB_TWILIGHT, + CAMERA_WB_SHADE, + CAMERA_WB_MAX_PLUS_1 +} camera_wb_type; + +typedef enum +{ + CAMERA_RSP_CB_SUCCESS, /* Function is accepted */ + CAMERA_EXIT_CB_DONE, /* Function is executed */ + CAMERA_EXIT_CB_FAILED, /* Execution failed or rejected */ + CAMERA_EXIT_CB_DSP_IDLE, /* DSP is in idle state */ + CAMERA_EXIT_CB_DSP_ABORT, /* Abort due to DSP failure */ + CAMERA_EXIT_CB_ABORT, /* Function aborted */ + CAMERA_EXIT_CB_ERROR, /* Failed due to resource */ + CAMERA_EVT_CB_FRAME, /* Preview or video frame ready */ + CAMERA_EVT_CB_PICTURE, /* Picture frame ready for multi-shot */ + CAMERA_STATUS_CB, /* Status updated */ + CAMERA_EXIT_CB_FILE_SIZE_EXCEEDED, /* Specified file size not achieved, + encoded file written & returned anyway */ + CAMERA_EXIT_CB_BUFFER, /* A buffer is returned */ + CAMERA_EVT_CB_SNAPSHOT_DONE,/* Snapshot updated */ + CAMERA_CB_MAX +} camera_cb_type; + +typedef enum +{ + CAMERA_ANTIBANDING_OFF, + CAMERA_ANTIBANDING_60HZ, + CAMERA_ANTIBANDING_50HZ, + CAMERA_ANTIBANDING_AUTO, + CAMERA_MAX_ANTIBANDING, +} camera_antibanding_type; + +//typedef struct +//{ +// uint32_t timestamp; /* seconds since 1/6/1980 */ +// double latitude; /* degrees, WGS ellipsoid */ +// double longitude; /* degrees */ +// int16_t altitude; /* meters */ +//} camera_position_type; + +typedef struct +{ + unsigned int in1_w; + unsigned int in1_h; + unsigned int out1_w; + unsigned int out1_h; + unsigned int in2_w; + unsigned int in2_h; + unsigned int out2_w; + unsigned int out2_h; + uint8_t update_flag; +} common_crop_t; + +typedef struct +{ + unsigned short picture_width; + unsigned short picture_height; + unsigned short display_width; + unsigned short display_height; + unsigned short filler; + unsigned short filler2; + unsigned short ui_thumbnail_height; + unsigned short ui_thumbnail_width; + unsigned short filler3; + unsigned short filler4; + unsigned short filler5; + unsigned short filler6; + unsigned short filler7; + unsigned short filler8; +} cam_ctrl_dimension_t; + +typedef uint8_t cam_ctrl_type; +typedef uint8_t jpeg_event_t; +typedef unsigned int isp3a_af_mode_t; + +struct str_map { + const char *const desc; + int val; +}; + +namespace android { + +class QualcommCameraHardware : public CameraHardwareInterface { +public: + + virtual sp getPreviewHeap() const; + virtual sp getRawHeap() const; + virtual void setCallbacks(notify_callback notify_cb, + data_callback data_cb, + data_callback_timestamp data_cb_timestamp, + void* user); + + virtual void enableMsgType(int32_t msgType); + virtual void disableMsgType(int32_t msgType); + virtual bool msgTypeEnabled(int32_t msgType); + + virtual status_t dump(int fd, const Vector& args) const; + virtual status_t startPreview(); + virtual void stopPreview(); + virtual bool previewEnabled(); + virtual status_t startRecording(); + virtual void stopRecording(); + virtual bool recordingEnabled(); + virtual void releaseRecordingFrame(const sp& mem); + virtual status_t autoFocus(); + virtual status_t takePicture(); + virtual status_t cancelPicture(); + virtual status_t setParameters(const CameraParameters& params); + virtual CameraParameters getParameters() const; + virtual status_t sendCommand(int32_t command, int32_t arg1, int32_t arg2); + virtual void release(); + virtual status_t cancelAutoFocus(); + + static sp createInstance(); + static sp getInstance(); + + void receivePreviewFrame(struct msm_frame_t *frame); + void receiveJpegPicture(void); + void jpeg_set_location(); + void receiveJpegPictureFragment(uint8_t *buf, uint32_t size); + void notifyShutter(); + +private: + QualcommCameraHardware(); + virtual ~QualcommCameraHardware(); + status_t startPreviewInternal(); + void stopPreviewInternal(); + friend void *auto_focus_thread(void *user); + void runAutoFocus(); + bool native_set_dimension (int camfd); + bool native_jpeg_encode (void); + bool native_set_parm(cam_ctrl_type type, uint16_t length, void *value); + bool native_set_dimension(cam_ctrl_dimension_t *value); + int getParm(const char *parm_str, const str_map *parm_map); + + static wp singleton; + + /* These constants reflect the number of buffers that libmmcamera requires + for preview and raw, and need to be updated when libmmcamera + changes. + */ + static const int kPreviewBufferCount = 4; + static const int kRawBufferCount = 1; + static const int kJpegBufferCount = 1; + static const int kRawFrameHeaderSize = 0; + + //TODO: put the picture dimensions in the CameraParameters object; + CameraParameters mParameters; + int mPreviewHeight; + int mPreviewWidth; + int mRawHeight; + int mRawWidth; + unsigned int frame_size; + bool mCameraRunning; + bool mPreviewInitialized; + bool mRawInitialized; + + // This class represents a heap which maintains several contiguous + // buffers. The heap may be backed by pmem (when pmem_pool contains + // the name of a /dev/pmem* file), or by ashmem (when pmem_pool == NULL). + + struct MemPool : public RefBase { + MemPool(int buffer_size, int num_buffers, + int frame_size, + int frame_offset, + const char *name); + + virtual ~MemPool() = 0; + + void completeInitialization(); + bool initialized() const { + return mHeap != NULL && mHeap->base() != MAP_FAILED; + } + + virtual status_t dump(int fd, const Vector& args) const; + + int mBufferSize; + int mNumBuffers; + int mFrameSize; + int mFrameOffset; + sp mHeap; + sp *mBuffers; + + const char *mName; + }; + + struct AshmemPool : public MemPool { + AshmemPool(int buffer_size, int num_buffers, + int frame_size, + int frame_offset, + const char *name); + }; + + struct PmemPool : public MemPool { + PmemPool(const char *pmem_pool, + int control_camera_fd, + msm_pmem_t pmem_type, + int buffer_size, int num_buffers, + int frame_size, int frame_offset, + const char *name); + virtual ~PmemPool(); + int mFd; + msm_pmem_t mPmemType; + int mCameraControlFd; + uint32_t mAlignedSize; + struct pmem_region mSize; + }; + + struct PreviewPmemPool : public PmemPool { + virtual ~PreviewPmemPool(); + PreviewPmemPool(int control_fd, + int buffer_size, + int num_buffers, + int frame_size, + int frame_offset, + const char *name); + }; + + sp mPreviewHeap; + sp mThumbnailHeap; + sp mRawHeap; + sp mJpegHeap; + + void startCamera(); + bool initPreview(); + void deinitPreview(); + bool initRaw(bool initJpegHeap); + void deinitRaw(); + + friend void *jpeg_encoder_thread( void *user ); + void runJpegEncodeThread(void *data); + + bool mFrameThreadRunning; + Mutex mFrameThreadWaitLock; + Condition mFrameThreadWait; + friend void *frame_thread(void *user); + void runFrameThread(void *data); + + bool mShutterPending; + Mutex mShutterLock; + + bool mSnapshotThreadRunning; + Mutex mSnapshotThreadWaitLock; + Condition mSnapshotThreadWait; + friend void *snapshot_thread(void *user); + void runSnapshotThread(void *data); + + void initDefaultParameters(); + + void setAntibanding(); + void setEffect(); + void setWhiteBalance(); + + Mutex mLock; + bool mReleasedRecordingFrame; + + void receiveRawPicture(void); + + + Mutex mRecordLock; + Mutex mRecordFrameLock; + Condition mRecordWait; + Condition mStateWait; + + /* mJpegSize keeps track of the size of the accumulated JPEG. We clear it + when we are about to take a picture, so at any time it contains either + zero, or the size of the last JPEG picture taken. + */ + uint32_t mJpegSize; + + notify_callback mNotifyCb; + data_callback mDataCb; + data_callback_timestamp mDataCbTimestamp; + void *mCallbackCookie; + + int32_t mMsgEnabled; + + unsigned int mPreviewFrameSize; + int mRawSize; + int mJpegMaxSize; + +#if DLOPEN_LIBMMCAMERA + void *libmmcamera; + void *libmmcamera_target; +#endif + + int mCameraControlFd; + cam_ctrl_dimension_t mDimension; + bool mAutoFocusThreadRunning; + Mutex mAutoFocusThreadLock; + int mAutoFocusFd; + + pthread_t mCamConfigThread; + pthread_t mFrameThread; + pthread_t mSnapshotThread; + + common_crop_t mCrop; + + struct msm_frame_t frames[kPreviewBufferCount]; + bool mInPreviewCallback; + bool mCameraRecording; +}; + +}; // namespace android + +#endif diff --git a/libcamera/exifwriter.c b/libcamera/exifwriter.c new file mode 100644 index 0000000..73016a5 --- /dev/null +++ b/libcamera/exifwriter.c @@ -0,0 +1,282 @@ +#include "exifwriter.h" + +#include "jhead.h" +#define LOG_TAG "ExifWriter" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define TAG_ORIENTATION 0x0112 +#define TAG_MAKE 0x010F +#define TAG_MODEL 0x0110 + + +float *float2degminsec( float deg ) +{ + float *res = malloc( sizeof(float)*3 ) ; + res[0] = floorf( deg ) ; + float min = ( deg - res[0] ) * 60. ; + res[1] = floorf( min ) ; + res[2] = ( min - res[1] ) * 60. ; + return res ; +} + + +// +// original source from +// http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions +// +char * float2rationnal( float src ) +{ + long m[2][2] ; + float x, startx ; + long maxden = 1000 ; + long ai ; + + startx = x = src ; + + /* initialize matrix */ + m[0][0] = m[1][1] = 1; + m[0][1] = m[1][0] = 0; + + /* loop finding terms until denom gets too big */ + while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) { + long t; + t = m[0][0] * ai + m[0][1]; + m[0][1] = m[0][0]; + m[0][0] = t; + t = m[1][0] * ai + m[1][1]; + m[1][1] = m[1][0]; + m[1][0] = t; + if(x==(float)ai) break; // AF: division by zero + x = 1/(x - (float) ai); + if(x>(float)0x7FFFFFFF) break; // AF: representation failure + } + + + /* now remaining x is between 0 and 1/ai */ + /* approx as either 0 or 1/m where m is max that will fit in maxden */ + /* first try zero */ + LOGV("%ld/%ld, error = %e\n", m[0][0], m[1][0], + startx - ((float) m[0][0] / (float) m[1][0])); + + /* now try other possibility */ + ai = (maxden - m[1][1]) / m[1][0]; + m[0][0] = m[0][0] * ai + m[0][1]; + m[1][0] = m[1][0] * ai + m[1][1]; + LOGV("%ld/%ld, error = %e\n", m[0][0], m[1][0], + startx - ((float) m[0][0] / (float) m[1][0])); + + char *res = (char *)malloc( 256 * sizeof(char) ) ; + + snprintf( res, 256, "%ld/%ld", m[0][0], m[1][0] ) ; + return res ; +} + +char * coord2degminsec( float src ) +{ + char *res = (char *)malloc( 256 * sizeof(char) ) ; + float *dms = float2degminsec( fabs(src) ) ; + strcpy( res, float2rationnal(dms[0]) ) ; + strcat( res , "," ) ; + strcat( res , float2rationnal(dms[1]) ) ; + strcat( res , "," ) ; + strcat( res , float2rationnal(dms[2]) ) ; + free( dms ) ; + return res ; +} + + static void dump_to_file(const char *fname, + uint8_t *buf, uint32_t size) + { + int nw, cnt = 0; + uint32_t written = 0; + + LOGD("opening file [%s]\n", fname); + int fd = open(fname, O_RDWR | O_CREAT); + if (fd < 0) { + LOGE("failed to create file [%s]: %s", fname, strerror(errno)); + return; + } + + LOGD("writing %d bytes to file [%s]\n", size, fname); + while (written < size) { + nw = write(fd, + buf + written, + size - written); + if (nw < 0) { + LOGE("failed to write to file [%s]: %s", + fname, strerror(errno)); + break; + } + written += nw; + cnt++; + } + LOGD("done writing %d bytes to file [%s] in %d passes\n", + size, fname, cnt); + close(fd); + } + +void writeExif( void *origData, void *destData , int origSize , uint32_t *resultSize, int orientation,camera_position_type *pt ) { + const char *filename = "/data/temp.jpg" ; + + dump_to_file( filename, (uint8_t *)origData, origSize ) ; + chmod( filename, S_IRWXU ) ; + ResetJpgfile() ; + + + memset(&ImageInfo, 0, sizeof(ImageInfo)); + ImageInfo.FlashUsed = -1; + ImageInfo.MeteringMode = -1; + ImageInfo.Whitebalance = -1; + + int gpsTag = 0 ; + if( pt != NULL ) { + gpsTag = 6 ; + } + + + ExifElement_t *t = (ExifElement_t *)malloc( sizeof(ExifElement_t)*(3+gpsTag) ) ; + + ExifElement_t *it = t ; + // Store file date/time. + (*it).Tag = TAG_ORIENTATION ; + (*it).Format = FMT_USHORT ; + (*it).DataLength = 1 ; + unsigned short v ; + if( orientation == 90 ) { + (*it).Value = "6" ; + } else if( orientation == 180 ) { + (*it).Value = "3" ; + } else { + (*it).Value = "1" ; + } + (*it).GpsTag = FALSE ; + + it++; + + (*it).Tag = TAG_MAKE ; + (*it).Format = FMT_STRING ; + (*it).Value = "Samsung" ; + (*it).DataLength = 8 ; + (*it).GpsTag = FALSE ; + + it++ ; + + (*it).Tag = TAG_MODEL ; + (*it).Format = FMT_STRING ; + (*it).Value = "Galaxy with GAOSP" ; + (*it).DataLength = 18 ; + (*it).GpsTag = FALSE ; + + + if( pt != NULL ) { + LOGD("pt->latitude == %f", pt->latitude ) ; + LOGD("pt->longitude == %f", pt->longitude ) ; + LOGD("pt->altitude == %d", pt->altitude ) ; + + it++ ; + (*it).Tag = 0x01 ; + (*it).Format = FMT_STRING ; + if( pt->latitude > 0 ) { + (*it).Value = "N" ; + } else { + (*it).Value = "S" ; + } + (*it).DataLength = 2 ; + (*it).GpsTag = TRUE ; + + it++ ; + char *mylat = coord2degminsec( pt->latitude ) ; + + (*it).Tag = 0x02 ; + (*it).Format = FMT_URATIONAL ; + (*it).Value = mylat ; + (*it).DataLength = 3 ; + (*it).GpsTag = TRUE ; + free( mylat ) ; + + it++ ; + (*it).Tag = 0x03 ; + (*it).Format = FMT_STRING ; + if( (*pt).longitude > 0 ) { + (*it).Value = "E" ; + } else { + (*it).Value = "W" ; + } + (*it).DataLength = 2 ; + (*it).GpsTag = TRUE ; + + it++ ; + char *mylong = coord2degminsec( (*pt).longitude ) ; + + (*it).Tag = 0x04 ; + (*it).Format = FMT_URATIONAL ; + (*it).Value = mylong ; + (*it).DataLength = 3 ; + (*it).GpsTag = TRUE ; + + free( mylong ) ; + + it++ ; + (*it).Tag = 0x05 ; + (*it).Format = FMT_USHORT ; + if( (*pt).altitude > 0 ) { + (*it).Value = "0" ; + } else { + (*it).Value = "1" ; + } + (*it).DataLength = 1 ; + (*it).GpsTag = TRUE ; + + it++ ; + char *myalt = float2rationnal( fabs( (*pt).altitude ) ) ; + + (*it).Tag = 0x06 ; + (*it).Format = FMT_SRATIONAL ; + (*it).Value = myalt ; + (*it).DataLength = 1 ; + (*it).GpsTag = TRUE ; + + free( myalt ) ; + + } + + { + struct stat st; + if (stat(filename, &st) >= 0) { + ImageInfo.FileDateTime = st.st_mtime; + ImageInfo.FileSize = st.st_size; + } + } + strncpy(ImageInfo.FileName, filename, PATH_MAX); + + ReadMode_t ReadMode; + ReadMode = READ_METADATA; + ReadMode |= READ_IMAGE; + int res = ReadJpegFile(filename, (ReadMode_t)ReadMode ); + + create_EXIF( t, 3, gpsTag); + + WriteJpegFile(filename); + chmod( filename, S_IRWXU ) ; + DiscardData(); + + FILE *src ; + src = fopen( filename, "r") ; + + fseek( src, 0L, SEEK_END ) ; + (*resultSize) = ftell(src) ; + fseek( src, 0L, SEEK_SET ) ; + + int read = fread( destData, 1, (*resultSize), src ) ; + + unlink( filename ); +} diff --git a/libcamera/exifwriter.h b/libcamera/exifwriter.h new file mode 100644 index 0000000..1a49431 --- /dev/null +++ b/libcamera/exifwriter.h @@ -0,0 +1,18 @@ +#ifndef ANDROID_HARDWARE_EXIFWRITER_H +#define ANDROID_HARDWARE_EXIFWRITER_H + +#include + +typedef struct +{ + uint32_t timestamp; /* seconds since 1/6/1980 */ + double latitude; /* degrees, WGS ellipsoid */ + double longitude; /* degrees */ + int16_t altitude; /* meters */ +} camera_position_type; + + +void writeExif( void *origData, void *destData , int origSize , uint32_t *resultSize, int orientation, camera_position_type *pt) ; + +#endif + diff --git a/libcamera/msm_camera.h b/libcamera/msm_camera.h new file mode 100644 index 0000000..4f37888 --- /dev/null +++ b/libcamera/msm_camera.h @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#ifndef __LINUX_MSM_CAMERA_H +#define __LINUX_MSM_CAMERA_H + +#ifdef __KERNEL__ +#include +#include +#include +#else +#include +#include +#include +#endif +#include + +#ifdef __KERNEL__ +#define CDBG_INFO(fmt, args...) printk(KERN_INFO "msm_camera: " fmt, ##args) +#define CDBG_ERR(fmt, args...) printk(KERN_INFO "msm_camera error: " fmt, ##args) +#define CDBG_WARING(fmt, args...) printk(KERN_INFO "msm_camera waring: " fmt, ##args) +#endif + +#define MSM_CAM_IOCTL_MAGIC 'm' + +#define MSM_CAM_IOCTL_GET_SENSOR_INFO \ + _IOR(MSM_CAM_IOCTL_MAGIC, 1, struct msm_camsensor_info_t *) + +#define MSM_CAM_IOCTL_REGISTER_PMEM \ + _IOW(MSM_CAM_IOCTL_MAGIC, 2, struct msm_pmem_info_t *) + +#define MSM_CAM_IOCTL_UNREGISTER_PMEM \ + _IOW(MSM_CAM_IOCTL_MAGIC, 3, unsigned) + +#define MSM_CAM_IOCTL_CTRL_COMMAND \ + _IOW(MSM_CAM_IOCTL_MAGIC, 4, struct msm_ctrl_cmd_t *) + +#define MSM_CAM_IOCTL_CONFIG_VFE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 5, struct msm_camera_vfe_cfg_cmd_t *) + +#define MSM_CAM_IOCTL_GET_STATS \ + _IOR(MSM_CAM_IOCTL_MAGIC, 6, struct msm_camera_stats_event_ctrl_t *) + +#define MSM_CAM_IOCTL_GETFRAME \ + _IOR(MSM_CAM_IOCTL_MAGIC, 7, struct msm_camera_get_frame_t *) + +#define MSM_CAM_IOCTL_ENABLE_VFE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 8, struct camera_enable_cmd_t *) + +#define MSM_CAM_IOCTL_CTRL_CMD_DONE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 9, struct camera_cmd_t *) + +#define MSM_CAM_IOCTL_CONFIG_CMD \ + _IOW(MSM_CAM_IOCTL_MAGIC, 10, struct camera_cmd_t *) + +#define MSM_CAM_IOCTL_DISABLE_VFE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 11, struct camera_enable_cmd_t *) + +#define MSM_CAM_IOCTL_PAD_REG_RESET2 \ + _IOW(MSM_CAM_IOCTL_MAGIC, 12, struct camera_enable_cmd_t *) + +#define MSM_CAM_IOCTL_VFE_APPS_RESET \ + _IOW(MSM_CAM_IOCTL_MAGIC, 13, struct camera_enable_cmd_t *) + +#define MSM_CAM_IOCTL_RELEASE_FRAMEE_BUFFER \ + _IOW(MSM_CAM_IOCTL_MAGIC, 14, struct camera_enable_cmd_t *) + +#define MSM_CAM_IOCTL_RELEASE_STATS_BUFFER \ + _IOW(MSM_CAM_IOCTL_MAGIC, 15, struct msm_stats_buf_t *) + +#define MSM_CAM_IOCTL_AXI_CONFIG \ + _IOW(MSM_CAM_IOCTL_MAGIC, 16, struct msm_camera_vfe_cfg_cmd_t *) + +#define MSM_CAM_IOCTL_GET_PICTURE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 17, struct msm_camera_ctrl_cmd_t *) + +#define MSM_CAM_IOCTL_SET_CROP \ + _IOW(MSM_CAM_IOCTL_MAGIC, 18, struct crop_info_t *) + +#define MSM_CAM_IOCTL_PICT_PP \ + _IOW(MSM_CAM_IOCTL_MAGIC, 19, uint8_t *) + +#define MSM_CAM_IOCTL_PICT_PP_DONE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 20, struct msm_snapshot_pp_status_t *) + +#define MSM_CAM_IOCTL_SENSOR_IO_CFG \ + _IOW(MSM_CAM_IOCTL_MAGIC, 21, struct sensor_cfg_data_t *) + +#define MSM_CAM_IOCTL_FLASH_LED_CFG \ + _IOW(MSM_CAM_IOCTL_MAGIC, 22, enum msm_camera_led_state_t *) + +#define MAX_SENSOR_NUM 3 +#define MAX_SENSOR_NAME 32 + +/***************************************************** + * enum + *****************************************************/ +enum msm_camera_update_t { + MSM_CAM_CTRL_CMD_DONE, + MSM_CAM_SENSOR_VFE_CMD, +}; + +/***************************************************** + * structure + *****************************************************/ + +/* define five type of structures for userspace <==> kernel + * space communication: + * command 1 - 2 are from userspace ==> kernel + * command 3 - 4 are from kernel ==> userspace + * + * 1. control command: control command(from control thread), + * control status (from config thread); + */ +struct msm_ctrl_cmd_t { + int timeout_ms; + uint16_t type; + uint16_t length; + void *value; + uint16_t status; +}; + +struct msm_vfe_evt_msg_t { + unsigned short type; /* 1 == event (RPC), 0 == message (adsp) */ + unsigned short msg_id; + unsigned int len; /* size in, number of bytes out */ + unsigned char *data; +}; + +enum msm_camera_resp_t { + MSM_CAM_RESP_CTRL, + MSM_CAM_RESP_STAT_EVT_MSG, + MSM_CAM_RESP_V4L2, + + MSM_CAM_RESP_MAX +}; + +/* this one is used to send ctrl/status up to config thread */ +struct msm_stats_event_ctrl { + /* 0 - ctrl_cmd from control thread, + * 1 - stats/event kernel, + * 2 - V4L control or read request */ + enum msm_camera_resp_t resptype; + int timeout_ms; + struct msm_ctrl_cmd_t ctrl_cmd; + /* struct vfe_event_t stats_event; */ + struct msm_vfe_evt_msg_t stats_event; +}; + +/* 2. config command: config command(from config thread); */ +struct msm_camera_cfg_cmd_t { + /* what to config: + * 1 - sensor config, 2 - vfe config */ + uint16_t cfg_type; + + /* sensor config type */ + uint16_t cmd_type; + uint16_t queue; + uint16_t length; + void *value; +}; + +enum cfg_cmd_type_t { + CMD_GENERAL, + CMD_AXI_CFG_OUT1, + CMD_AXI_CFG_SNAP_O1_AND_O2, + CMD_AXI_CFG_OUT2, + CMD_PICT_T_AXI_CFG, + CMD_PICT_M_AXI_CFG, + CMD_RAW_PICT_AXI_CFG, + CMD_STATS_AXI_CFG, + CMD_STATS_AF_AXI_CFG, + CMD_FRAME_BUF_RELEASE, + CMD_PREV_BUF_CFG, + CMD_SNAP_BUF_RELEASE, + CMD_SNAP_BUF_CFG, + CMD_STATS_DISABLE, + CMD_STATS_ENABLE, + CMD_STATS_AF_ENABLE, + CMD_STATS_BUF_RELEASE, + CMD_STATS_AF_BUF_RELEASE, + UPDATE_STATS_INVALID +}; + +/* vfe config command: config command(from config thread)*/ +struct msm_vfe_cfg_cmd_t { + enum cfg_cmd_type_t cmd_type; + uint16_t length; + void *value; +}; + +struct camera_enable_cmd_t { + char *name; + uint16_t length; +}; + +enum msm_pmem_t { + MSM_PMEM_OUTPUT1, + MSM_PMEM_OUTPUT2, + MSM_PMEM_OUTPUT1_OUTPUT2, + MSM_PMEM_THUMBNAIL, + MSM_PMEM_MAINIMG, + MSM_PMEM_RAW_MAINIMG, + MSM_PMEM_AEC_AWB, + MSM_PMEM_AF, + + MSM_PMEM_MAX +}; + +enum msm_camera_out_frame_t { + FRAME_PREVIEW_OUTPUT1, + FRAME_PREVIEW_OUTPUT2, + FRAME_SNAPSHOT, + FRAME_THUMBAIL, + FRAME_RAW_SNAPSHOT, + FRAME_MAX +}; + +struct msm_pmem_info_t { + enum msm_pmem_t type; + int fd; + void *vaddr; + uint32_t y_off; + uint32_t cbcr_off; + uint8_t active; +}; + +struct outputCfg_t { + uint32_t height; + uint32_t width; + + uint32_t window_height_firstline; + uint32_t window_height_lastline; +}; + +enum vfeoutput_mode_t { + OUTPUT_1, + OUTPUT_2, + OUTPUT_1_AND_2, + CAMIF_TO_AXI_VIA_OUTPUT_2, + OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2, + OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1, + LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 +}; + +enum msm_frame_path { + MSM_FRAME_PREV_1, + MSM_FRAME_PREV_2, + MSM_FRAME_ENC, +}; + +struct msm_frame_t { + enum msm_frame_path path; + unsigned long buffer; + uint32_t y_off; + uint32_t cbcr_off; + int fd; + + void *cropinfo; + int croplen; +}; + +enum stat_type { + STAT_AEAW, + STAT_AF, + STAT_MAX, +}; + +struct msm_stats_buf_t { + enum stat_type type; + unsigned long buffer; + int fd; +}; + +enum msm_v4l2_ctrl_t { + MSM_V4L2_VID_CAP_TYPE, + MSM_V4L2_STREAM_ON, + MSM_V4L2_STREAM_OFF, + MSM_V4L2_SNAPSHOT, + MSM_V4L2_QUERY_CTRL, + MSM_V4L2_GET_CTRL, + MSM_V4L2_SET_CTRL, + MSM_V4L2_QUERY, + + MSM_V4L2_MAX +}; + +struct crop_info_t { + void *info; + int len; +}; + +struct msm_postproc_t { + int ftnum; + struct msm_frame_t fthumnail; + int fmnum; + struct msm_frame_t fmain; +}; + +struct msm_snapshot_pp_status_t { + void *status; +}; + +enum sensor_cfg_t { + CFG_SET_MODE, + CFG_SET_EFFECT, + CFG_START, + CFG_PWR_UP, + CFG_PWR_DOWN, + CFG_WRITE_EXPOSURE_GAIN, + CFG_SET_DEFAULT_FOCUS, + CFG_MOVE_FOCUS, + CFG_REGISTER_TO_REAL_GAIN, + CFG_REAL_TO_REGISTER_GAIN, + CFG_SET_FPS, + CFG_SET_PICT_FPS, + CFG_SET_BRIGHTNESS, + CFG_SET_CONTRAST, + CFG_SET_ZOOM, + CFG_SET_EXPOSURE_MODE, + CFG_SET_WB, + CFG_SET_ANTIBANDING, + CFG_SET_EXP_GAIN, + CFG_SET_PICT_EXP_GAIN, + CFG_SET_LENS_SHADING, + + CFG_GET_PICT_FPS, + CFG_GET_PREV_L_PF, + CFG_GET_PREV_P_PL, + CFG_GET_PICT_L_PF, + CFG_GET_PICT_P_PL, + + CFG_GET_PICT_MAX_EXP_LC, + + CFG_MAX +}; + +enum sensor_move_focus_t { + MOVE_NEAR, + MOVE_FAR +}; + +enum sensor_mode_t { + SENSOR_PREVIEW_MODE, + SENSOR_SNAPSHOT_MODE, + SENSOR_RAW_SNAPSHOT_MODE +}; + +enum sensor_resolution_t { + SENSOR_QTR_SIZE, + SENSOR_FULL_SIZE, + SENSOR_INVALID_SIZE, +}; + +enum camera_effect_t { + CAMERA_EFFECT_MIN_MINUS_1, + CAMERA_EFFECT_OFF = 1, /* This list must match aeecamera.h */ + CAMERA_EFFECT_MONO, + CAMERA_EFFECT_NEGATIVE, + CAMERA_EFFECT_SOLARIZE, + CAMERA_EFFECT_PASTEL, + CAMERA_EFFECT_MOSAIC, + CAMERA_EFFECT_RESIZE, + CAMERA_EFFECT_SEPIA, + CAMERA_EFFECT_POSTERIZE, + CAMERA_EFFECT_WHITEBOARD, + CAMERA_EFFECT_BLACKBOARD, + CAMERA_EFFECT_AQUA, + CAMERA_EFFECT_MAX_PLUS_1 +}; + +struct sensor_pict_fps { + uint16_t prevfps; + uint16_t pictfps; +}; + +struct exp_gain_cfg { + uint16_t gain; + uint32_t line; +}; + +struct focus_cfg { + int32_t steps; + enum sensor_move_focus_t dir; +}; + +struct fps_cfg { + uint16_t f_mult; + uint16_t fps_div; + uint32_t pict_fps_div; +}; + +enum msm_camera_led_state_t { + MSM_LED_OFF, + MSM_LED_LOW, + MSM_LED_HIGH +}; + +struct sensor_cfg_data_t { + enum sensor_cfg_t cfgtype; + enum sensor_mode_t mode; + enum sensor_resolution_t rs; + + union { + int8_t effect; + uint8_t lens_shading; + uint16_t prevl_pf; + uint16_t prevp_pl; + uint16_t pictl_pf; + uint16_t pictp_pl; + uint32_t pict_max_exp_lc; + uint16_t p_fps; + struct sensor_pict_fps gfps; + struct exp_gain_cfg exp_gain; + struct focus_cfg focus; + struct fps_cfg fps; + } cfg; +}; + +enum sensor_get_info_t { + GET_NAME, + GET_PREVIEW_LINE_PER_FRAME, + GET_PREVIEW_PIXELS_PER_LINE, + GET_SNAPSHOT_LINE_PER_FRAME, + GET_SNAPSHOT_PIXELS_PER_LINE, + GET_SNAPSHOT_FPS, + GET_SNAPSHOT_MAX_EP_LINE_CNT, +}; + +struct msm_camsensor_info_t { + char name[MAX_SENSOR_NAME]; + int8_t flash_enabled; +}; +#endif /* __LINUX_MSM_CAMERA_H */ diff --git a/tattoo.mk b/tattoo.mk index ee92409..de1b1af 100644 --- a/tattoo.mk +++ b/tattoo.mk @@ -40,6 +40,7 @@ PRODUCT_PACKAGES += \ libmm-omxcore \ wlan_loader \ tiwlan.ini \ + libcamera \ librpc \ dhcpcd.conf @@ -119,7 +120,7 @@ PRODUCT_PROPERTY_OVERRIDES += \ # Enable JIT by default PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.execution-mode=int:jit + dalvik.vm.execution-mode=int:fast # VM heap size PRODUCT_PROPERTY_OVERRIDES += \