| /* |
| ** 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", ¶ms); |
| |
| 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 |