/* ** Copyright 2008, The Android Open-Source Project ** ** 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. */ // TODO // -- replace Condition::wait with Condition::waitRelative // -- use read/write locks #define LOG_NDEBUG 0 #define LOG_TAG "QualcommCameraHardware" #include <utils/Log.h> #include <utils/threads.h> #include <binder/MemoryHeapPmem.h> #include <utils/String16.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/time.h> #include <time.h> #include <fcntl.h> #include <unistd.h> #if HAVE_ANDROID_OS #include <linux/android_pmem.h> #endif #include <camera_ifc.h> #if DLOPEN_LIBQCAMERA #include <dlfcn.h> #endif #define PRINT_TIME 0 extern "C" { static inline void print_time() { #if PRINT_TIME struct timeval time; gettimeofday(&time, NULL); ALOGV("time: %lld us.", time.tv_sec * 1000000LL + time.tv_usec); #endif } typedef struct { int width; int height; } preview_size_type; // These sizes have to be a multiple of 16 in each dimension static preview_size_type preview_sizes[] = { { 480, 320 }, // HVGA { 432, 320 }, // 1.35-to-1, for photos. (Rounded up from 1.3333 to 1) { 352, 288 }, // CIF { 320, 240 }, // QVGA { 240, 160 }, // SQVGA { 176, 144 }, // QCIF }; #define PREVIEW_SIZE_COUNT (sizeof(preview_sizes)/sizeof(preview_size_type)) // default preview size is QVGA #define DEFAULT_PREVIEW_SETTING 0 #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) /* some functions we need from libqcamera */ extern void rex_start(); extern void rex_shutdown(); /* callbacks */ #if DLOPEN_LIBQCAMERA == 0 extern void (*rex_signal_ready)(); extern uint8_t* (*cam_mmap_preview)(uint32_t size, uint32_t *phy_addr, uint32_t index); extern uint8_t* (*cam_mmap_snapshot)(uint32_t size, uint32_t *phy_addr, uint32_t index); extern int (*cam_munmap_preview)(uint32_t *phy_addr, uint32_t size, uint32_t index); extern int (*cam_munmap_snapshot)(uint32_t *phy_addr, uint32_t size, uint32_t index); extern clear_module_pmem(qdsp_module_type module); extern void camera_assoc_pmem(qdsp_module_type module, int pmem_fd, void *addr, uint32_t length, int external); extern int camera_release_pmem(qdsp_module_type module, void *addr, uint32_t size, uint32_t force); #define LINK_camera_assoc_pmem camera_assoc_pmem #define LINK_clear_module_pmem clear_module_pmem #define LINK_camera_release_pmem camera_release_pmem #define LINK_camera_encode_picture camera_encode_picture #define LINK_camera_init camera_init #define LINK_camera_af_init camera_af_init #define LINK_camera_release_frame camera_release_frame #define LINK_camera_set_dimensions camera_set_dimensions #define LINK_camera_set_encode_properties camera_set_encode_properties #define LINK_camera_set_parm camera_set_parm #define LINK_camera_set_parm_2 camera_set_parm_2 #define LINK_camera_set_position camera_set_position #define LINK_camera_set_thumbnail_properties camera_set_thumbnail_properties #define LINK_camera_start camera_start #define LINK_camera_start_preview camera_start_preview #define LINK_camera_start_focus camera_start_focus #define LINK_camera_stop_focus camera_stop_focus #define LINK_camera_stop camera_stop #define LINK_camera_stop_preview camera_stop_preview #define LINK_camera_take_picture camera_take_picture #define LINK_rex_shutdown rex_shutdown #define LINK_rex_start rex_start #define LINK_rex_signal_ready rex_signal_ready #define LINK_cam_mmap_preview cam_mmap_preview #define LINK_cam_munmap_preview cam_munmap_preview #define LINK_cam_mmap_snapshot cam_mmap_snapshot #define LINK_cam_munmap_snapshot cam_munmap_snapshot #else /* Function pointers to assign to */ void (**LINK_rex_signal_ready)(); uint8_t* (**LINK_cam_mmap_preview)( uint32_t size, uint32_t *phy_addr, uint32_t index); int (**LINK_cam_munmap_preview)( uint32_t *phy_addr, uint32_t size, uint32_t index); uint8_t* (**LINK_cam_mmap_snapshot)( uint32_t size, uint32_t *phy_addr, uint32_t index); int (**LINK_cam_munmap_snapshot)( uint32_t *phy_addr, uint32_t size, uint32_t index); /* Function pointers to resolve */ void (*LINK_camera_assoc_pmem)(qdsp_module_type module, int pmem_fd, void *addr, uint32_t length, int external); void (*LINK_clear_module_pmem)(qdsp_module_type module); int (*LINK_camera_release_pmem)(qdsp_module_type module, void *addr, uint32_t size, uint32_t force); camera_ret_code_type (*LINK_camera_encode_picture) ( camera_frame_type *frame, camera_handle_type *handle, camera_cb_f_type callback, void *client_data); void (*LINK_camera_init)(void); void (*LINK_camera_af_init)(void); camera_ret_code_type (*LINK_camera_release_frame)(void); camera_ret_code_type (*LINK_camera_set_dimensions) ( uint16_t picture_width, uint16_t picture_height, uint16_t display_width, #ifdef FEATURE_CAMERA_V7 uint16_t display_height, #endif camera_cb_f_type callback, void *client_data); camera_ret_code_type (*LINK_camera_set_encode_properties)( camera_encode_properties_type *encode_properties); camera_ret_code_type (*LINK_camera_set_parm) ( camera_parm_type id, int32_t parm, camera_cb_f_type callback, void *client_data); camera_ret_code_type (*LINK_camera_set_parm_2) ( camera_parm_type id, int32_t parm1, int32_t parm2, camera_cb_f_type callback, void *client_data); camera_ret_code_type (*LINK_camera_set_position) ( camera_position_type *position, camera_cb_f_type callback, void *client_data); camera_ret_code_type (*LINK_camera_set_thumbnail_properties) ( uint32_t width, uint32_t height, uint32_t quality); camera_ret_code_type (*LINK_camera_start) ( camera_cb_f_type callback, void *client_data #ifdef FEATURE_NATIVELINUX ,int display_height, int display_width #endif /* FEATURE_NATIVELINUX */ ); camera_ret_code_type (*LINK_camera_start_preview) ( camera_cb_f_type callback, void *client_data); camera_ret_code_type (*LINK_camera_start_focus) ( camera_focus_e_type focus, camera_cb_f_type callback, void *client_data); camera_ret_code_type (*LINK_camera_stop_focus) (void); camera_ret_code_type (*LINK_camera_stop) ( camera_cb_f_type callback, void *client_data); camera_ret_code_type (*LINK_camera_stop_preview) (void); camera_ret_code_type (*LINK_camera_take_picture) ( camera_cb_f_type callback, void *client_data #if !defined FEATURE_CAMERA_ENCODE_PROPERTIES && defined FEATURE_CAMERA_V7 ,camera_raw_type camera_raw_mode #endif /* nFEATURE_CAMERA_ENCODE_PROPERTIES && FEATURE_CAMERA_V7 */ ); int (*LINK_rex_start)(void); int (*LINK_rex_shutdown)(void); #endif } #include "QualcommCameraHardware.h" namespace android { static Mutex singleton_lock; static Mutex rex_init_lock; static Condition rex_init_wait; static uint8_t* malloc_preview(uint32_t, uint32_t *, uint32_t); static uint8_t* malloc_raw(uint32_t, uint32_t *, uint32_t); static int free_preview(uint32_t *, uint32_t , uint32_t); static int free_raw(uint32_t *, uint32_t , uint32_t); static int reassoc(qdsp_module_type module); static void cb_rex_signal_ready(void); QualcommCameraHardware::QualcommCameraHardware() : mParameters(), mPreviewHeight(-1), mPreviewWidth(-1), mRawHeight(-1), mRawWidth(-1), mCameraState(QCS_INIT), mShutterCallback(0), mRawPictureCallback(0), mJpegPictureCallback(0), mPictureCallbackCookie(0), mAutoFocusCallback(0), mAutoFocusCallbackCookie(0), mPreviewCallback(0), mPreviewCallbackCookie(0), mRecordingCallback(0), mRecordingCallbackCookie(0), mPreviewFrameSize(0), mRawSize(0), mPreviewCount(0) { ALOGV("constructor EX"); } void QualcommCameraHardware::initDefaultParameters() { CameraParameters p; preview_size_type* ps = &preview_sizes[DEFAULT_PREVIEW_SETTING]; p.setPreviewSize(ps->width, ps->height); p.setPreviewFrameRate(15); p.setPreviewFormat("yuv420sp"); p.setPictureFormat("jpeg"); // we do not look at this currently p.setPictureSize(2048, 1536); p.set("jpeg-quality", "100"); // maximum quality // These values must be multiples of 16, so we can't do 427x320, which is the exact size on // screen we want to display at. 480x360 doesn't work either since it's a multiple of 8. p.set("jpeg-thumbnail-width", "512"); p.set("jpeg-thumbnail-height", "384"); p.set("jpeg-thumbnail-quality", "90"); p.set("nightshot-mode", "0"); // off p.set("luma-adaptation", "0"); // FIXME: turning it on causes a crash p.set("antibanding", "auto"); // flicker detection and elimination p.set("whitebalance", "auto"); p.set("rotation", "0"); #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 // List supported picture size values p.set("picture-size-values", "2048x1536,1600x1200,1024x768"); // List supported antibanding values p.set("antibanding-values", "off,50hz,60hz,auto"); // List supported effects: p.set("effect-values", "off,mono,negative,solarize,sepia,posterize,whiteboard,"\ "blackboard,aqua"); // List supported exposure-offset: p.set("exposure-offset-values", "0,1,2,3,4,5,6,7,8,9,10"); // List of whitebalance values p.set("whitebalance-values", "auto,incandescent,fluorescent,daylight,cloudy"); // List of ISO values p.set("iso-values", "auto,high"); if (setParameters(p) != NO_ERROR) { ALOGE("Failed to set default parameters?!"); } } #define ROUND_TO_PAGE(x) (((x)+0xfff)&~0xfff) // Called with mStateLock held! void QualcommCameraHardware::startCameraIfNecessary() { if (mCameraState == QCS_INIT) { #if DLOPEN_LIBQCAMERA == 1 ALOGV("loading libqcamera"); libqcamera = ::dlopen("liboemcamera.so", RTLD_NOW); if (!libqcamera) { ALOGE("FATAL ERROR: could not dlopen liboemcamera.so: %s", dlerror()); return; } *(void **)&LINK_camera_assoc_pmem = ::dlsym(libqcamera, "camera_assoc_pmem"); *(void **)&LINK_clear_module_pmem = ::dlsym(libqcamera, "clear_module_pmem"); *(void **)&LINK_camera_release_pmem = ::dlsym(libqcamera, "camera_release_pmem"); *(void **)&LINK_camera_encode_picture = ::dlsym(libqcamera, "camera_encode_picture"); *(void **)&LINK_camera_init = ::dlsym(libqcamera, "camera_init"); *(void **)&LINK_camera_af_init = ::dlsym(libqcamera, "camera_af_init"); *(void **)&LINK_camera_release_frame = ::dlsym(libqcamera, "camera_release_frame"); *(void **)&LINK_camera_set_dimensions = ::dlsym(libqcamera, "camera_set_dimensions"); *(void **)&LINK_camera_set_encode_properties = ::dlsym(libqcamera, "camera_set_encode_properties"); *(void **)&LINK_camera_set_parm = ::dlsym(libqcamera, "camera_set_parm"); *(void **)&LINK_camera_set_parm_2 = ::dlsym(libqcamera, "camera_set_parm_2"); *(void **)&LINK_camera_set_position = ::dlsym(libqcamera, "camera_set_position"); *(void **)&LINK_camera_set_thumbnail_properties = ::dlsym(libqcamera, "camera_set_thumbnail_properties"); *(void **)&LINK_camera_start = ::dlsym(libqcamera, "camera_start"); *(void **)&LINK_camera_start_preview = ::dlsym(libqcamera, "camera_start_preview"); *(void **)&LINK_camera_start_focus = ::dlsym(libqcamera, "camera_start_focus"); *(void **)&LINK_camera_stop_focus = ::dlsym(libqcamera, "camera_stop_focus"); *(void **)&LINK_camera_stop = ::dlsym(libqcamera, "camera_stop"); *(void **)&LINK_camera_stop_preview = ::dlsym(libqcamera, "camera_stop_preview"); *(void **)&LINK_camera_take_picture = ::dlsym(libqcamera, "camera_take_picture"); *(void **)&LINK_rex_shutdown = ::dlsym(libqcamera, "rex_shutdown"); *(void **)&LINK_rex_start = ::dlsym(libqcamera, "rex_start"); *(void **)&LINK_rex_signal_ready = ::dlsym(libqcamera, "rex_signal_ready"); *(void **)&LINK_cam_mmap_preview = ::dlsym(libqcamera, "cam_mmap_preview"); *(void **)&LINK_cam_munmap_preview = ::dlsym(libqcamera, "cam_munmap_preview"); *(void **)&LINK_cam_mmap_snapshot = ::dlsym(libqcamera, "cam_mmap_snapshot"); *(void **)&LINK_cam_munmap_snapshot = ::dlsym(libqcamera, "cam_munmap_snapshot"); *LINK_rex_signal_ready = cb_rex_signal_ready; *LINK_cam_mmap_preview = malloc_preview; *LINK_cam_munmap_preview = free_preview; *LINK_cam_mmap_snapshot = malloc_raw; *LINK_cam_munmap_snapshot = free_raw; #else LINK_rex_signal_ready = cb_rex_signal_ready; LINK_cam_mmap_preview = malloc_preview; LINK_cam_munmap_preview = free_preview; LINK_cam_mmap_snapshot = malloc_raw; LINK_cam_munmap_snapshot = free_raw; #endif // DLOPEN_LIBQCAMERA == 1 rex_init_lock.lock(); LINK_rex_start(); ALOGV("waiting for REX to initialize."); rex_init_wait.wait(rex_init_lock); ALOGV("REX is ready."); rex_init_lock.unlock(); LINK_camera_init(); ALOGV("starting REX emulation"); // NOTE: camera_start() takes (height, width), not (width, height). LINK_camera_start(camera_cb, this, mPreviewHeight, mPreviewWidth); while(mCameraState != QCS_IDLE && mCameraState != QCS_ERROR) { ALOGV("init camera: waiting for QCS_IDLE"); mStateWait.wait(mStateLock); ALOGV("init camera: woke up"); } ALOGV("init camera: initializing parameters"); } else ALOGV("camera hardware has been started already"); } status_t QualcommCameraHardware::dump(int fd, const Vector<String16>& args) const { const size_t SIZE = 256; char buffer[SIZE]; String8 result; // Dump internal primitives. snprintf(buffer, 255, "QualcommCameraHardware::dump: state (%d)\n", mCameraState); result.append(buffer); snprintf(buffer, 255, "preview width(%d) x height (%d)\n", mPreviewWidth, mPreviewHeight); result.append(buffer); snprintf(buffer, 255, "raw width(%d) x height (%d)\n", mRawWidth, mRawHeight); result.append(buffer); snprintf(buffer, 255, "preview frame size(%d), raw size (%d), jpeg size (%d) and jpeg max size (%d)\n", mPreviewFrameSize, mRawSize, mJpegSize, mJpegMaxSize); result.append(buffer); write(fd, result.string(), result.size()); // Dump internal objects. if (mPreviewHeap != 0) { mPreviewHeap->dump(fd, args); } if (mRawHeap != 0) { mRawHeap->dump(fd, args); } if (mJpegHeap != 0) { mJpegHeap->dump(fd, args); } mParameters.dump(fd, args); return NO_ERROR; } bool QualcommCameraHardware::initPreview() { // LINK_clear_module_pmem(QDSP_MODULE_VFETASK); startCameraIfNecessary(); // Tell libqcamera what the preview and raw dimensions are. We // call this method even if the preview dimensions have not changed, // because the picture ones may have. // // NOTE: if this errors out, mCameraState != QCS_IDLE, which will be // checked by the caller of this method. setCameraDimensions(); ALOGV("initPreview: preview size=%dx%d", mPreviewWidth, mPreviewHeight); mPreviewFrameSize = mPreviewWidth * mPreviewHeight * 3 / 2; // reality mPreviewHeap = new PreviewPmemPool(kRawFrameHeaderSize + mPreviewWidth * mPreviewHeight * 2, // worst kPreviewBufferCount, mPreviewFrameSize, kRawFrameHeaderSize, "preview"); if (!mPreviewHeap->initialized()) { mPreviewHeap = NULL; return false; } // LINK_camera_af_init(); return true; } void QualcommCameraHardware::deinitPreview() { mPreviewHeap = NULL; } // Called with mStateLock held! bool QualcommCameraHardware::initRaw(bool initJpegHeap) { ALOGV("initRaw E"); startCameraIfNecessary(); // Tell libqcamera what the preview and raw dimensions are. We // call this method even if the preview dimensions have not changed, // because the picture ones may have. // // NOTE: if this errors out, mCameraState != QCS_IDLE, which will be // checked by the caller of this method. setCameraDimensions(); ALOGV("initRaw: picture size=%dx%d", mRawWidth, mRawHeight); // Note that we enforce yuv420 in setParameters(). mRawSize = mRawWidth * mRawHeight * 3 / 2; /* reality */ mJpegMaxSize = mRawWidth * mRawHeight * 2; ALOGV("initRaw: clearing old mJpegHeap."); mJpegHeap = NULL; ALOGV("initRaw: initializing mRawHeap."); mRawHeap = new RawPmemPool("/dev/pmem_camera", kRawFrameHeaderSize + mJpegMaxSize, /* worst */ kRawBufferCount, mRawSize, kRawFrameHeaderSize, "snapshot camera"); if (!mRawHeap->initialized()) { ALOGE("initRaw X failed: error initializing mRawHeap"); mRawHeap = NULL; return false; } if (initJpegHeap) { ALOGV("initRaw: initializing mJpegHeap."); mJpegHeap = new AshmemPool(mJpegMaxSize, kJpegBufferCount, 0, // we do not know how big the picture wil be 0, "jpeg"); if (!mJpegHeap->initialized()) { ALOGE("initRaw X failed: error initializing mJpegHeap."); mJpegHeap = NULL; mRawHeap = NULL; return false; } } ALOGV("initRaw X success"); return true; } void QualcommCameraHardware::release() { ALOGV("release E"); Mutex::Autolock l(&mLock); // Either preview was ongoing, or we are in the middle or taking a // picture. It's the caller's responsibility to make sure the camera // is in the idle or init state before destroying this object. if (mCameraState != QCS_IDLE && mCameraState != QCS_INIT) { ALOGE("Serious error: the camera state is %s, " "not QCS_IDLE or QCS_INIT!", getCameraStateStr(mCameraState)); } mStateLock.lock(); if (mCameraState != QCS_INIT) { // When libqcamera detects an error, it calls camera_cb from the // call to LINK_camera_stop, which would cause a deadlock if we // held the mStateLock. For this reason, we have an intermediate // state QCS_INTERNAL_STOPPING, which we use to check to see if the // camera_cb was called inline. mCameraState = QCS_INTERNAL_STOPPING; mStateLock.unlock(); ALOGV("stopping camera."); LINK_camera_stop(stop_camera_cb, this); mStateLock.lock(); if (mCameraState == QCS_INTERNAL_STOPPING) { while (mCameraState != QCS_INIT && mCameraState != QCS_ERROR) { ALOGV("stopping camera: waiting for QCS_INIT"); mStateWait.wait(mStateLock); } } ALOGV("Shutting REX down."); LINK_rex_shutdown(); ALOGV("REX has shut down."); #if DLOPEN_LIBQCAMERA if (libqcamera) { unsigned ref = ::dlclose(libqcamera); ALOGV("dlclose(libqcamera) refcount %d", ref); } #endif mCameraState = QCS_INIT; } mStateLock.unlock(); ALOGV("release X"); } QualcommCameraHardware::~QualcommCameraHardware() { ALOGV("~QualcommCameraHardware E"); Mutex::Autolock singletonLock(&singleton_lock); singleton.clear(); ALOGV("~QualcommCameraHardware X"); } sp<IMemoryHeap> QualcommCameraHardware::getPreviewHeap() const { ALOGV("getPreviewHeap"); return mPreviewHeap != NULL ? mPreviewHeap->mHeap : NULL; } sp<IMemoryHeap> QualcommCameraHardware::getRawHeap() const { return mRawHeap != NULL ? mRawHeap->mHeap : NULL; } bool QualcommCameraHardware::setCallbacks( preview_callback pcb, void *puser, recording_callback rcb, void *ruser) { Mutex::Autolock cbLock(&mCallbackLock); mPreviewCallback = pcb; mPreviewCallbackCookie = puser; mRecordingCallback = rcb; mRecordingCallbackCookie = ruser; return mPreviewCallback != NULL || mRecordingCallback != NULL; } status_t QualcommCameraHardware::startPreviewInternal( preview_callback pcb, void *puser, recording_callback rcb, void *ruser) { ALOGV("startPreview E"); if (mCameraState == QCS_PREVIEW_IN_PROGRESS) { ALOGE("startPreview is already in progress, doing nothing."); // We might want to change the callback functions while preview is // streaming, for example to enable or disable recording. setCallbacks(pcb, puser, rcb, ruser); return NO_ERROR; } // We check for these two states explicitly because it is possible // for startPreview() to be called in response to a raw or JPEG // callback, but before we've updated the state from QCS_WAITING_RAW // or QCS_WAITING_JPEG to QCS_IDLE. This is because in camera_cb(), // we update the state *after* we've made the callback. See that // function for an explanation. if (mCameraState == QCS_WAITING_RAW || mCameraState == QCS_WAITING_JPEG) { while (mCameraState != QCS_IDLE && mCameraState != QCS_ERROR) { ALOGV("waiting for QCS_IDLE"); mStateWait.wait(mStateLock); } } if (mCameraState != QCS_IDLE) { ALOGE("startPreview X Camera state is %s, expecting QCS_IDLE!", getCameraStateStr(mCameraState)); return INVALID_OPERATION; } if (!initPreview()) { ALOGE("startPreview X initPreview failed. Not starting preview."); return UNKNOWN_ERROR; } setCallbacks(pcb, puser, rcb, ruser); // hack to prevent first preview frame from being black mPreviewCount = 0; mCameraState = QCS_INTERNAL_PREVIEW_REQUESTED; camera_ret_code_type qret = LINK_camera_start_preview(camera_cb, this); if (qret == CAMERA_SUCCESS) { while(mCameraState != QCS_PREVIEW_IN_PROGRESS && mCameraState != QCS_ERROR) { ALOGV("waiting for QCS_PREVIEW_IN_PROGRESS"); mStateWait.wait(mStateLock); } } else { ALOGE("startPreview failed: sensor error."); mCameraState = QCS_ERROR; } ALOGV("startPreview X"); return mCameraState == QCS_PREVIEW_IN_PROGRESS ? NO_ERROR : UNKNOWN_ERROR; } void QualcommCameraHardware::stopPreviewInternal() { ALOGV("stopPreviewInternal E"); if (mCameraState != QCS_PREVIEW_IN_PROGRESS) { ALOGE("Preview not in progress!"); return; } if (mAutoFocusCallback != NULL) { // WARNING: clear mAutoFocusCallback BEFORE you call // camera_stop_focus. The CAMERA_EXIT_CB_ABORT is (erroneously) // delivered inline camera_stop_focus(), and we cannot acquire // mStateLock, because that would cause a deadlock. In any case, // CAMERA_EXIT_CB_ABORT is delivered only when we call // camera_stop_focus. mAutoFocusCallback = NULL; LINK_camera_stop_focus(); } setCallbacks(NULL, NULL, NULL, NULL); mCameraState = QCS_INTERNAL_PREVIEW_STOPPING; LINK_camera_stop_preview(); while (mCameraState != QCS_IDLE && mCameraState != QCS_ERROR) { ALOGV("waiting for QCS_IDLE"); mStateWait.wait(mStateLock); } ALOGV("stopPreviewInternal: Freeing preview heap."); mPreviewHeap = NULL; mPreviewCallback = NULL; ALOGV("stopPreviewInternal: X Preview has stopped."); } status_t QualcommCameraHardware::startPreview( preview_callback pcb, void *puser) { Mutex::Autolock l(&mLock); Mutex::Autolock stateLock(&mStateLock); return startPreviewInternal(pcb, puser, mRecordingCallback, mRecordingCallbackCookie); } void QualcommCameraHardware::stopPreview() { ALOGV("stopPreview: E"); Mutex::Autolock l(&mLock); if (!setCallbacks(NULL, NULL, mRecordingCallback, mRecordingCallbackCookie)) { Mutex::Autolock statelock(&mStateLock); stopPreviewInternal(); } ALOGV("stopPreview: X"); } bool QualcommCameraHardware::previewEnabled() { Mutex::Autolock l(&mLock); return mCameraState == QCS_PREVIEW_IN_PROGRESS; } status_t QualcommCameraHardware::startRecording( recording_callback rcb, void *ruser) { Mutex::Autolock l(&mLock); Mutex::Autolock stateLock(&mStateLock); return startPreviewInternal(mPreviewCallback, mPreviewCallbackCookie, rcb, ruser); } void QualcommCameraHardware::stopRecording() { ALOGV("stopRecording: E"); Mutex::Autolock l(&mLock); if (!setCallbacks(mPreviewCallback, mPreviewCallbackCookie, NULL, NULL)) { Mutex::Autolock statelock(&mStateLock); stopPreviewInternal(); } ALOGV("stopRecording: X"); } bool QualcommCameraHardware::recordingEnabled() { Mutex::Autolock l(&mLock); Mutex::Autolock stateLock(&mStateLock); return mCameraState == QCS_PREVIEW_IN_PROGRESS && mRecordingCallback != NULL; } void QualcommCameraHardware::releaseRecordingFrame( const sp<IMemory>& mem __attribute__((unused))) { Mutex::Autolock l(&mLock); LINK_camera_release_frame(); } status_t QualcommCameraHardware::autoFocus(autofocus_callback af_cb, void *user) { ALOGV("Starting auto focus."); Mutex::Autolock l(&mLock); Mutex::Autolock lock(&mStateLock); if (mCameraState != QCS_PREVIEW_IN_PROGRESS) { ALOGE("Invalid camera state %s: expecting QCS_PREVIEW_IN_PROGRESS," " cannot start autofocus!", getCameraStateStr(mCameraState)); return INVALID_OPERATION; } if (mAutoFocusCallback != NULL) { ALOGV("Auto focus is already in progress"); return mAutoFocusCallback == af_cb ? NO_ERROR : INVALID_OPERATION; } mAutoFocusCallback = af_cb; mAutoFocusCallbackCookie = user; LINK_camera_start_focus(CAMERA_AUTO_FOCUS, camera_cb, this); return NO_ERROR; } status_t QualcommCameraHardware::takePicture(shutter_callback shutter_cb, raw_callback raw_cb, jpeg_callback jpeg_cb, void* user) { ALOGV("takePicture: E raw_cb = %p, jpeg_cb = %p", raw_cb, jpeg_cb); print_time(); Mutex::Autolock l(&mLock); Mutex::Autolock stateLock(&mStateLock); qualcomm_camera_state last_state = mCameraState; if (mCameraState == QCS_PREVIEW_IN_PROGRESS) { stopPreviewInternal(); } // We check for these two states explicitly because it is possible // for takePicture() to be called in response to a raw or JPEG // callback, but before we've updated the state from QCS_WAITING_RAW // or QCS_WAITING_JPEG to QCS_IDLE. This is because in camera_cb(), // we update the state *after* we've made the callback. See that // function for an explanation why. if (mCameraState == QCS_WAITING_RAW || mCameraState == QCS_WAITING_JPEG) { while (mCameraState != QCS_IDLE && mCameraState != QCS_ERROR) { ALOGV("waiting for QCS_IDLE"); mStateWait.wait(mStateLock); } } if (mCameraState != QCS_IDLE) { ALOGE("takePicture: %sunexpected state %d, expecting QCS_IDLE", (last_state == QCS_PREVIEW_IN_PROGRESS ? "(stop preview) " : ""), mCameraState); // If we had to stop preview in order to take a picture, and // we failed to transition to a QCS_IDLE state, that's because // of an internal error. return last_state == QCS_PREVIEW_IN_PROGRESS ? UNKNOWN_ERROR : INVALID_OPERATION; } if (!initRaw(jpeg_cb != NULL)) { ALOGE("initRaw failed. Not taking picture."); return UNKNOWN_ERROR; } if (mCameraState != QCS_IDLE) { ALOGE("takePicture: (init raw) " "unexpected state %d, expecting QCS_IDLE", mCameraState); // If we had to stop preview in order to take a picture, and // we failed to transition to a QCS_IDLE state, that's because // of an internal error. return last_state == QCS_PREVIEW_IN_PROGRESS ? UNKNOWN_ERROR : INVALID_OPERATION; } { Mutex::Autolock cbLock(&mCallbackLock); mShutterCallback = shutter_cb; mRawPictureCallback = raw_cb; mJpegPictureCallback = jpeg_cb; mPictureCallbackCookie = user; } mCameraState = QCS_INTERNAL_RAW_REQUESTED; LINK_camera_take_picture(camera_cb, this); // It's possible for the YUV callback as well as the JPEG callbacks // to be invoked before we even make it here, so we check for all // possible result states from takePicture. while (mCameraState != QCS_WAITING_RAW && mCameraState != QCS_WAITING_JPEG && mCameraState != QCS_IDLE && mCameraState != QCS_ERROR) { ALOGV("takePicture: waiting for QCS_WAITING_RAW or QCS_WAITING_JPEG"); mStateWait.wait(mStateLock); ALOGV("takePicture: woke up, state is %s", getCameraStateStr(mCameraState)); } ALOGV("takePicture: X"); print_time(); return mCameraState != QCS_ERROR ? NO_ERROR : UNKNOWN_ERROR; } status_t QualcommCameraHardware::cancelPicture( bool cancel_shutter, bool cancel_raw, bool cancel_jpeg) { ALOGV("cancelPicture: E cancel_shutter = %d, cancel_raw = %d, cancel_jpeg = %d", cancel_shutter, cancel_raw, cancel_jpeg); Mutex::Autolock l(&mLock); Mutex::Autolock stateLock(&mStateLock); switch (mCameraState) { case QCS_INTERNAL_RAW_REQUESTED: case QCS_WAITING_RAW: case QCS_WAITING_JPEG: ALOGV("camera state is %s, stopping picture.", getCameraStateStr(mCameraState)); { Mutex::Autolock cbLock(&mCallbackLock); if (cancel_shutter) mShutterCallback = NULL; if (cancel_raw) mRawPictureCallback = NULL; if (cancel_jpeg) mJpegPictureCallback = NULL; } while (mCameraState != QCS_IDLE && mCameraState != QCS_ERROR) { ALOGV("cancelPicture: waiting for QCS_IDLE"); mStateWait.wait(mStateLock); } break; default: ALOGV("not taking a picture (state %s)", getCameraStateStr(mCameraState)); } ALOGV("cancelPicture: X"); return NO_ERROR; } status_t QualcommCameraHardware::setParameters( const CameraParameters& params) { ALOGV("setParameters: E params = %p", ¶ms); Mutex::Autolock l(&mLock); Mutex::Autolock lock(&mStateLock); // FIXME: verify params // yuv422sp is here only for legacy reason. Unfortunately, we release // the code with yuv422sp as the default and enforced setting. The // correct setting is yuv420sp. if ((strcmp(params.getPreviewFormat(), "yuv420sp") != 0) && (strcmp(params.getPreviewFormat(), "yuv422sp") != 0)) { ALOGE("Only yuv420sp preview is supported"); return INVALID_OPERATION; } // FIXME: will this make a deep copy/do the right thing? String8 i // should handle it mParameters = params; // libqcamera only supports certain size/aspect ratios // find closest match that doesn't exceed app's request int width, height; params.getPreviewSize(&width, &height); ALOGV("requested size %d x %d", width, height); preview_size_type* ps = preview_sizes; size_t i; for (i = 0; i < PREVIEW_SIZE_COUNT; ++i, ++ps) { if (width >= ps->width && height >= ps->height) break; } // app requested smaller size than supported, use smallest size if (i == PREVIEW_SIZE_COUNT) ps--; ALOGV("actual size %d x %d", ps->width, ps->height); mParameters.setPreviewSize(ps->width, ps->height); mParameters.getPreviewSize(&mPreviewWidth, &mPreviewHeight); mParameters.getPictureSize(&mRawWidth, &mRawHeight); mPreviewWidth = (mPreviewWidth + 1) & ~1; mPreviewHeight = (mPreviewHeight + 1) & ~1; mRawHeight = (mRawHeight + 1) & ~1; mRawWidth = (mRawWidth + 1) & ~1; initCameraParameters(); ALOGV("setParameters: X mCameraState=%d", mCameraState); return mCameraState == QCS_IDLE ? NO_ERROR : UNKNOWN_ERROR; } CameraParameters QualcommCameraHardware::getParameters() const { ALOGV("getParameters: EX"); return mParameters; } static CameraInfo sCameraInfo[] = { { CAMERA_FACING_BACK, 90, /* orientation */ } }; extern "C" int HAL_getNumberOfCameras() { return sizeof(sCameraInfo) / sizeof(sCameraInfo[0]); } extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo) { memcpy(cameraInfo, &sCameraInfo[cameraId], sizeof(CameraInfo)); } extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId) { ALOGV("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() { ALOGV("createInstance: E"); singleton_lock.lock(); if (singleton != 0) { sp<CameraHardwareInterface> hardware = singleton.promote(); if (hardware != 0) { ALOGV("createInstance: X return existing hardware=%p", &(*hardware)); singleton_lock.unlock(); return hardware; } } { struct stat st; int rc = stat("/dev/oncrpc", &st); if (rc < 0) { ALOGV("createInstance: X failed to create hardware: %s", strerror(errno)); singleton_lock.unlock(); return NULL; } } QualcommCameraHardware *cam = new QualcommCameraHardware(); sp<QualcommCameraHardware> hardware(cam); singleton = hardware; singleton_lock.unlock(); // initDefaultParameters() will cause the camera_cb() to be called. // Since the latter tries to promote the singleton object to make sure // it still exists, we need to call this function after we have set the // singleton. cam->initDefaultParameters(); ALOGV("createInstance: X created hardware=%p", &(*hardware)); return hardware; } // For internal use only, hence the strong pointer to the derived type. sp<QualcommCameraHardware> QualcommCameraHardware::getInstance() { Mutex::Autolock singletonLock(&singleton_lock); sp<CameraHardwareInterface> hardware = singleton.promote(); return (hardware != 0) ? sp<QualcommCameraHardware>(static_cast<QualcommCameraHardware*> (hardware.get())) : NULL; } void* QualcommCameraHardware::get_preview_mem(uint32_t size, uint32_t *phy_addr, uint32_t index) { if (mPreviewHeap != NULL && mPreviewHeap->mHeap != NULL) { uint8_t *base = (uint8_t *)mPreviewHeap->mHeap->base(); if (base && size <= mPreviewHeap->mSize.len) { // For preview, associate the memory with the VFE task in the // DSP. This way, when the DSP gets a command that has a // physical address, it knows which pmem region to patch it // against. uint32_t vaddr = (uint32_t)(base + size*index); ALOGV("get_preview_mem: base %p MALLOC size %d index %d --> %p", base, size, index, (void *)vaddr); *phy_addr = vaddr; return (void *)vaddr; } } ALOGV("get_preview_mem: X NULL"); return NULL; } void QualcommCameraHardware::free_preview_mem(uint32_t *phy_addr, uint32_t size, uint32_t index) { ALOGV("free_preview_mem: EX NOP"); return; } void* QualcommCameraHardware::get_raw_mem(uint32_t size, uint32_t *phy_addr, uint32_t index) { if (mRawHeap != NULL && mRawHeap->mHeap != NULL) { uint8_t *base = (uint8_t *)mRawHeap->mHeap->base(); if (base && size <= mRawHeap->mSize.len) { // For raw snapshot, associate the memory with the VFE and LPM // tasks in the DSP. This way, when the DSP gets a command // that has a physical address, it knows which pmem region to // patch it against. uint32_t vaddr = (uint32_t)(base + size*index); ALOGV("get_raw_mem: base %p MALLOC size %d index %d --> %p", base, size, index, (void *)vaddr); *phy_addr = vaddr; return (void *)vaddr; } } ALOGV("get_raw_mem: X NULL"); return NULL; } void QualcommCameraHardware::free_raw_mem(uint32_t *phy_addr, uint32_t size, uint32_t index) { ALOGV("free_raw_mem: EX NOP"); return; } void QualcommCameraHardware::receivePreviewFrame(camera_frame_type *frame) { Mutex::Autolock cbLock(&mCallbackLock); // Ignore the first frame--there is a bug in the VFE pipeline and that // frame may be bad. if (++mPreviewCount == 1) { LINK_camera_release_frame(); return; } // Find the offset within the heap of the current buffer. ssize_t offset = (uint32_t)frame->buf_Virt_Addr; offset -= (uint32_t)mPreviewHeap->mHeap->base(); ssize_t frame_size = kRawFrameHeaderSize + frame->dx * frame->dy * 2; if (offset + frame_size <= (ssize_t)mPreviewHeap->mHeap->virtualSize()) { #if 0 // frame->buffer includes the header, frame->buf_Virt_Addr skips it ALOGV("PREVIEW FRAME CALLBACK " "base %p addr %p offset %ld " "framesz %dx%d=%ld (expect %d) rotation %d " "(index %ld) size %d header_size 0x%x", mPreviewHeap->mHeap->base(), frame->buf_Virt_Addr, offset, frame->dx, frame->dy, frame_size, mPreviewFrameSize, frame->rotation, offset / frame_size, mPreviewFrameSize, frame->header_size); #endif offset /= frame_size; if (mPreviewCallback != NULL) mPreviewCallback(mPreviewHeap->mBuffers[offset], mPreviewCallbackCookie); if (mRecordingCallback != NULL) mRecordingCallback(mPreviewHeap->mBuffers[offset], mRecordingCallbackCookie); else { // When we are doing preview but not recording, we need to // release every preview frame immediately so that the next // preview frame is delivered. However, when we are recording // (whether or not we are also streaming the preview frames to // the screen), we have the user explicitly release a preview // frame via method releaseRecordingFrame(). In this way we // allow a video encoder which is potentially slower than the // preview stream to skip frames. Note that we call // LINK_camera_release_frame() in this method because we first // need to check to see if mPreviewCallback != NULL, which // requires holding mCallbackLock. LINK_camera_release_frame(); } } else ALOGE("Preview frame virtual address %p is out of range!", frame->buf_Virt_Addr); } void QualcommCameraHardware::notifyShutter() { ALOGV("notifyShutter: E"); print_time(); Mutex::Autolock lock(&mStateLock); if (mShutterCallback) mShutterCallback(mPictureCallbackCookie); print_time(); ALOGV("notifyShutter: X"); } // Pass the pre-LPM raw picture to raw picture callback. // This method is called by a libqcamera thread, different from the one on // which startPreview() or takePicture() are called. void QualcommCameraHardware::receiveRawPicture(camera_frame_type *frame) { ALOGV("receiveRawPicture: E"); print_time(); Mutex::Autolock cbLock(&mCallbackLock); if (mRawPictureCallback != NULL) { // FIXME: WHY IS buf_Virt_Addr ZERO?? frame->buf_Virt_Addr = (uint32_t*)frame->buffer; // Find the offset within the heap of the current buffer. ssize_t offset = (uint32_t)frame->buf_Virt_Addr; offset -= (uint32_t)mRawHeap->mHeap->base(); ssize_t frame_size = kRawFrameHeaderSize + frame->captured_dx * frame->captured_dy * 2; if (offset + frame_size <= (ssize_t)mRawHeap->mHeap->virtualSize()) { #if 0 // frame->buffer includes the header, frame->buf_Virt_Addr // skips it. ALOGV("receiveRawPicture: RAW CALLBACK (CB %p) " "base %p addr %p buffer %p offset %ld " "framesz %dx%d=%ld (expect %d) rotation %d " "(index %ld) size %d header_size 0x%x", mRawPictureCallback, mRawHeap->mHeap->base(), frame->buf_Virt_Addr, frame->buffer, offset, frame->captured_dx, frame->captured_dy, frame_size, mRawSize, frame->rotation, offset / frame_size, mRawSize, frame->header_size); #endif offset /= frame_size; mRawPictureCallback(mRawHeap->mBuffers[offset], mPictureCallbackCookie); } else ALOGE("receiveRawPicture: virtual address %p is out of range!", frame->buf_Virt_Addr); } else ALOGV("Raw-picture callback was canceled--skipping."); print_time(); ALOGV("receiveRawPicture: X"); } // Encode the post-LPM raw picture. // This method is called by a libqcamera thread, different from the one on // which startPreview() or takePicture() are called. void QualcommCameraHardware::receivePostLpmRawPicture(camera_frame_type *frame) { ALOGV("receivePostLpmRawPicture: E"); print_time(); qualcomm_camera_state new_state = QCS_ERROR; Mutex::Autolock cbLock(&mCallbackLock); if (mJpegPictureCallback != NULL) { bool encode_location = true; #define PARSE_LOCATION(what,type,fmt,desc) do { \ pt.what = 0; \ const char *what##_str = mParameters.get("gps-"#what); \ ALOGV("receiveRawPicture: 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 { \ ALOGE("GPS " #what " %s could not" \ " be parsed as a " #desc, \ what##_str); \ encode_location = false; \ } \ } \ else { \ ALOGW("receiveRawPicture: 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) { ALOGV("receiveRawPicture: setting image location ALT %d LAT %lf LON %lf", pt.altitude, pt.latitude, pt.longitude); if (LINK_camera_set_position(&pt, NULL, NULL) != CAMERA_SUCCESS) { ALOGE("receiveRawPicture: camera_set_position: error"); /* return; */ // not a big deal } } else ALOGV("receiveRawPicture: not setting image location"); mJpegSize = 0; camera_handle.device = CAMERA_DEVICE_MEM; camera_handle.mem.encBuf_num = MAX_JPEG_ENCODE_BUF_NUM; for (int cnt = 0; cnt < MAX_JPEG_ENCODE_BUF_NUM; cnt++) { camera_handle.mem.encBuf[cnt].buffer = (uint8_t *) malloc(MAX_JPEG_ENCODE_BUF_LEN); camera_handle.mem.encBuf[cnt].buf_len = MAX_JPEG_ENCODE_BUF_LEN; camera_handle.mem.encBuf[cnt].used_len = 0; } /* for */ LINK_camera_encode_picture(frame, &camera_handle, camera_cb, this); } else { ALOGV("JPEG callback was cancelled--not encoding image."); // We need to keep the raw heap around until the JPEG is fully // encoded, because the JPEG encode uses the raw image contained in // that heap. mRawHeap = NULL; } print_time(); ALOGV("receivePostLpmRawPicture: X"); } void QualcommCameraHardware::receiveJpegPictureFragment( JPEGENC_CBrtnType *encInfo) { camera_encode_mem_type *enc = (camera_encode_mem_type *)encInfo->outPtr; int index = enc - camera_handle.mem.encBuf; uint8_t *base = (uint8_t *)mJpegHeap->mHeap->base(); uint32_t size = encInfo->size; uint32_t remaining = mJpegHeap->mHeap->virtualSize(); remaining -= mJpegSize; ALOGV("receiveJpegPictureFragment: (index %d status %d size %d)", index, encInfo->status, size); if (size > remaining) { ALOGE("receiveJpegPictureFragment: size %d exceeds what " "remains in JPEG heap (%d), truncating", size, remaining); size = remaining; } camera_handle.mem.encBuf[index].used_len = 0; memcpy(base + mJpegSize, enc->buffer, size); mJpegSize += size; } // This method is called by a libqcamera thread, different from the one on // which startPreview() or takePicture() are called. void QualcommCameraHardware::receiveJpegPicture(void) { ALOGV("receiveJpegPicture: E image (%d bytes out of %d)", mJpegSize, mJpegHeap->mBufferSize); print_time(); Mutex::Autolock cbLock(&mCallbackLock); int index = 0; 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 ALOGV("JPEG callback was cancelled--not delivering image."); // NOTE: the JPEG encoder uses the raw image contained in mRawHeap, so we need // to keep the heap around until the encoding is complete. mJpegHeap = NULL; mRawHeap = NULL; for (int cnt = 0; cnt < MAX_JPEG_ENCODE_BUF_NUM; cnt++) { if (camera_handle.mem.encBuf[cnt].buffer != NULL) { free(camera_handle.mem.encBuf[cnt].buffer); memset(camera_handle.mem.encBuf + cnt, 0, sizeof(camera_encode_mem_type)); } } /* for */ print_time(); ALOGV("receiveJpegPicture: X callback done."); } struct str_map { const char *const desc; int val; }; static const struct str_map wb_map[] = { { "auto", CAMERA_WB_AUTO }, { "custom", CAMERA_WB_CUSTOM }, { "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 const struct str_map effect_map[] = { { "off", CAMERA_EFFECT_OFF }, { "mono", CAMERA_EFFECT_MONO }, { "negative", CAMERA_EFFECT_NEGATIVE }, { "solarize", CAMERA_EFFECT_SOLARIZE }, { "pastel", CAMERA_EFFECT_PASTEL }, { "mosaic", CAMERA_EFFECT_MOSAIC }, { "resize", CAMERA_EFFECT_RESIZE }, { "sepia", CAMERA_EFFECT_SEPIA }, { "posterize", CAMERA_EFFECT_POSTERIZE }, { "whiteboard", CAMERA_EFFECT_WHITEBOARD }, { "blackboard", CAMERA_EFFECT_BLACKBOARD }, { "aqua", CAMERA_EFFECT_AQUA }, { NULL, 0 } }; static const struct str_map brightness_map[] = { { "0", CAMERA_BRIGHTNESS_0 }, { "1", CAMERA_BRIGHTNESS_1 }, { "2", CAMERA_BRIGHTNESS_2 }, { "3", CAMERA_BRIGHTNESS_3 }, { "4", CAMERA_BRIGHTNESS_4 }, { "5", CAMERA_BRIGHTNESS_5 }, { "6", CAMERA_BRIGHTNESS_6 }, { "7", CAMERA_BRIGHTNESS_7 }, { "8", CAMERA_BRIGHTNESS_8 }, { "9", CAMERA_BRIGHTNESS_9 }, { "10", CAMERA_BRIGHTNESS_10 }, { NULL, 0 } }; static const struct str_map antibanding_map[] = { { "off", CAMERA_ANTIBANDING_OFF }, { "50hz", CAMERA_ANTIBANDING_50HZ }, { "60hz", CAMERA_ANTIBANDING_60HZ }, { "auto", CAMERA_ANTIBANDING_AUTO }, { NULL, 0 } }; static const struct str_map iso_map[] = { { "auto", CAMERA_ISO_AUTO }, { "high", CAMERA_ISO_HIGH }, { NULL, 0 } }; static int lookup(const struct str_map *const arr, const char *name, int def) { if (name) { const struct str_map * trav = arr; while (trav->desc) { if (!strcmp(trav->desc, name)) return trav->val; trav++; } } return def; } void QualcommCameraHardware::initCameraParameters() { ALOGV("initCameraParameters: E"); // Because libqcamera is broken, for the camera_set_parm() calls // QualcommCameraHardware camera_cb() is called synchronously, // so we cannot wait on a state change. Also, we have to unlock // the mStateLock, because camera_cb() acquires it. startCameraIfNecessary(); #define SET_PARM(x,y) do { \ ALOGV("initCameraParameters: set parm: %s, %d", #x, y); \ LINK_camera_set_parm (x, y, NULL, NULL); \ } while(0) /* Preview Mode: snapshot or movie */ SET_PARM(CAMERA_PARM_PREVIEW_MODE, CAMERA_PREVIEW_MODE_SNAPSHOT); /* Default Rotation - none */ int rotation = mParameters.getInt("rotation"); // Rotation may be negative, but may not be -1, because it has to be a // multiple of 90. That's why we can still interpret -1 as an error, if (rotation == -1) { ALOGV("rotation not specified or is invalid, defaulting to 0"); rotation = 0; } else if (rotation % 90) { ALOGE("rotation %d is not a multiple of 90 degrees! Defaulting to zero.", rotation); rotation = 0; } else { // normalize to [0 - 270] degrees rotation %= 360; if (rotation < 0) rotation += 360; } SET_PARM(CAMERA_PARM_ENCODE_ROTATION, rotation); SET_PARM(CAMERA_PARM_WB, lookup(wb_map, mParameters.get("whitebalance"), CAMERA_WB_AUTO)); SET_PARM(CAMERA_PARM_EFFECT, lookup(effect_map, mParameters.get("effect"), CAMERA_EFFECT_OFF)); SET_PARM(CAMERA_PARM_BRIGHTNESS, lookup(brightness_map, mParameters.get("exposure-offset"), CAMERA_BRIGHTNESS_DEFAULT)); SET_PARM(CAMERA_PARM_ISO, lookup(iso_map, mParameters.get("iso"), CAMERA_ISO_AUTO)); SET_PARM(CAMERA_PARM_ANTIBANDING, lookup(antibanding_map, mParameters.get("antibanding"), CAMERA_ANTIBANDING_AUTO)); int ns_mode = mParameters.getInt("nightshot-mode"); if (ns_mode < 0) ns_mode = 0; SET_PARM(CAMERA_PARM_NIGHTSHOT_MODE, ns_mode); int luma_adaptation = mParameters.getInt("luma-adaptation"); if (luma_adaptation < 0) luma_adaptation = 0; SET_PARM(CAMERA_PARM_LUMA_ADAPTATION, luma_adaptation); #undef SET_PARM #if 0 /* Default Auto FPS: 30 (maximum) */ LINK_camera_set_parm_2 (CAMERA_PARM_PREVIEW_FPS, (1<<16|20), // max frame rate 30 (4<<16|20), // min frame rate 5 NULL, NULL); #endif int th_w, th_h, th_q; th_w = mParameters.getInt("jpeg-thumbnail-width"); if (th_w < 0) ALOGW("property jpeg-thumbnail-width not specified"); th_h = mParameters.getInt("jpeg-thumbnail-height"); if (th_h < 0) ALOGW("property jpeg-thumbnail-height not specified"); th_q = mParameters.getInt("jpeg-thumbnail-quality"); if (th_q < 0) ALOGW("property jpeg-thumbnail-quality not specified"); if (th_w > 0 && th_h > 0 && th_q > 0) { ALOGI("setting thumbnail dimensions to %dx%d, quality %d", th_w, th_h, th_q); int ret = LINK_camera_set_thumbnail_properties(th_w, th_h, th_q); if (ret != CAMERA_SUCCESS) { ALOGE("LINK_camera_set_thumbnail_properties returned %d", ret); } } #if defined FEATURE_CAMERA_ENCODE_PROPERTIES /* Set Default JPEG encoding--this does not cause a callback */ encode_properties.quality = mParameters.getInt("jpeg-quality"); if (encode_properties.quality < 0) { ALOGW("JPEG-image quality is not specified " "or is negative, defaulting to %d", encode_properties.quality); encode_properties.quality = 100; } else ALOGV("Setting JPEG-image quality to %d", encode_properties.quality); encode_properties.format = CAMERA_JPEG; encode_properties.file_size = 0x0; LINK_camera_set_encode_properties(&encode_properties); #else #warning 'FEATURE_CAMERA_ENCODE_PROPERTIES should be enabled!' #endif ALOGV("initCameraParameters: X"); } // Called with mStateLock held! void QualcommCameraHardware::setCameraDimensions() { if (mCameraState != QCS_IDLE) { ALOGE("set camera dimensions: expecting state QCS_IDLE, not %s", getCameraStateStr(mCameraState)); return; } LINK_camera_set_dimensions(mRawWidth, mRawHeight, mPreviewWidth, mPreviewHeight, NULL, NULL); } QualcommCameraHardware::qualcomm_camera_state QualcommCameraHardware::change_state(qualcomm_camera_state new_state, bool lock) { if (lock) mStateLock.lock(); if (new_state != mCameraState) { // Due to the fact that we allow only one thread at a time to call // startPreview(), stopPreview(), or takePicture(), we know that // only one thread at a time may be blocked waiting for a state // transition on mStateWait. That's why we signal(), not // broadcast(). ALOGV("state transition %s --> %s", getCameraStateStr(mCameraState), getCameraStateStr(new_state)); mCameraState = new_state; mStateWait.signal(); } if (lock) mStateLock.unlock(); return new_state; } #define CAMERA_STATE(n) case n: if(n != CAMERA_FUNC_START_PREVIEW || cb != CAMERA_EVT_CB_FRAME) ALOGV("STATE %s // STATUS %d", #n, cb); #define TRANSITION(e,s) do { \ obj->change_state(obj->mCameraState == e ? s : QCS_ERROR); \ } while(0) #define TRANSITION_LOCKED(e,s) do { \ obj->change_state((obj->mCameraState == e ? s : QCS_ERROR), false); \ } while(0) #define TRANSITION_ALWAYS(s) obj->change_state(s) // This callback is called from the destructor. void QualcommCameraHardware::stop_camera_cb(camera_cb_type cb, const void *client_data, camera_func_type func, int32_t parm4) { QualcommCameraHardware *obj = (QualcommCameraHardware *)client_data; switch(func) { CAMERA_STATE(CAMERA_FUNC_STOP) TRANSITION(QCS_INTERNAL_STOPPING, QCS_INIT); break; default: break; } } void QualcommCameraHardware::camera_cb(camera_cb_type cb, const void *client_data, camera_func_type func, int32_t parm4) { QualcommCameraHardware *obj = (QualcommCameraHardware *)client_data; // Promote the singleton to make sure that we do not get destroyed // while this callback is executing. if (UNLIKELY(getInstance() == NULL)) { ALOGE("camera object has been destroyed--returning immediately"); return; } if (cb == CAMERA_EXIT_CB_ABORT || /* Function aborted */ cb == CAMERA_EXIT_CB_DSP_ABORT || /* Abort due to DSP failure */ cb == CAMERA_EXIT_CB_ERROR || /* Failed due to resource */ cb == CAMERA_EXIT_CB_FAILED) /* Execution failed or rejected */ { // Autofocus failures occur relatively often and are not fatal, so // we do not transition to QCS_ERROR for them. if (func != CAMERA_FUNC_START_FOCUS) { ALOGE("QualcommCameraHardware::camera_cb: @CAMERA_EXIT_CB_FAILURE(%d) in state %s.", parm4, obj->getCameraStateStr(obj->mCameraState)); TRANSITION_ALWAYS(QCS_ERROR); } } switch(func) { // This is the commonest case. CAMERA_STATE(CAMERA_FUNC_START_PREVIEW) switch(cb) { case CAMERA_RSP_CB_SUCCESS: TRANSITION(QCS_INTERNAL_PREVIEW_REQUESTED, QCS_PREVIEW_IN_PROGRESS); break; case CAMERA_EVT_CB_FRAME: switch (obj->mCameraState) { case QCS_PREVIEW_IN_PROGRESS: if (parm4) obj->receivePreviewFrame((camera_frame_type *)parm4); break; case QCS_INTERNAL_PREVIEW_STOPPING: ALOGE("camera cb: discarding preview frame " "while stopping preview"); break; default: // transition to QCS_ERROR ALOGE("camera cb: invalid state %s for preview!", obj->getCameraStateStr(obj->mCameraState)); break; } /* -- this function is called now inside of receivePreviewFrame. LINK_camera_release_frame(); */ break; default: // transition to QCS_ERROR ALOGE("unexpected cb %d for CAMERA_FUNC_START_PREVIEW.", cb); } break; CAMERA_STATE(CAMERA_FUNC_START) TRANSITION(QCS_INIT, QCS_IDLE); break; /* -- this case handled in stop_camera_cb() now. CAMERA_STATE(CAMERA_FUNC_STOP) TRANSITION(QCS_INTERNAL_STOPPING, QCS_INIT); break; */ CAMERA_STATE(CAMERA_FUNC_STOP_PREVIEW) TRANSITION(QCS_INTERNAL_PREVIEW_STOPPING, QCS_IDLE); break; CAMERA_STATE(CAMERA_FUNC_TAKE_PICTURE) if (cb == CAMERA_RSP_CB_SUCCESS) { TRANSITION(QCS_INTERNAL_RAW_REQUESTED, QCS_WAITING_RAW); } else if (cb == CAMERA_EVT_CB_SNAPSHOT_DONE) { obj->notifyShutter(); // Received pre-LPM raw picture. Notify callback now. obj->receiveRawPicture((camera_frame_type *)parm4); } else if (cb == CAMERA_EXIT_CB_DONE) { // It's important that we call receiveRawPicture() before // we transition the state because another thread may be // waiting in cancelPicture(), and then delete this object. // If the order were reversed, we might call // receiveRawPicture on a dead object. ALOGV("Receiving post LPM raw picture."); obj->receivePostLpmRawPicture((camera_frame_type *)parm4); { Mutex::Autolock lock(&obj->mStateLock); TRANSITION_LOCKED(QCS_WAITING_RAW, obj->mJpegPictureCallback != NULL ? QCS_WAITING_JPEG : QCS_IDLE); } } else { // transition to QCS_ERROR if (obj->mCameraState == QCS_ERROR) { ALOGE("camera cb: invalid state %s for taking a picture!", obj->getCameraStateStr(obj->mCameraState)); obj->mRawPictureCallback(NULL, obj->mPictureCallbackCookie); obj->mJpegPictureCallback(NULL, obj->mPictureCallbackCookie); TRANSITION_ALWAYS(QCS_IDLE); } } break; CAMERA_STATE(CAMERA_FUNC_ENCODE_PICTURE) switch (cb) { case CAMERA_RSP_CB_SUCCESS: // We already transitioned the camera state to // QCS_WAITING_JPEG when we called // camera_encode_picture(). break; case CAMERA_EXIT_CB_BUFFER: if (obj->mCameraState == QCS_WAITING_JPEG) { obj->receiveJpegPictureFragment( (JPEGENC_CBrtnType *)parm4); } else ALOGE("camera cb: invalid state %s for receiving " "JPEG fragment!", obj->getCameraStateStr(obj->mCameraState)); break; case CAMERA_EXIT_CB_DONE: if (obj->mCameraState == QCS_WAITING_JPEG) { // Receive the last fragment of the image. obj->receiveJpegPictureFragment( (JPEGENC_CBrtnType *)parm4); // The size of the complete JPEG image is in // mJpegSize. // It's important that we call receiveJpegPicture() // before we transition the state because another // thread may be waiting in cancelPicture(), and then // delete this object. If the order were reversed, we // might call receiveRawPicture on a dead object. obj->receiveJpegPicture(); TRANSITION(QCS_WAITING_JPEG, QCS_IDLE); } // transition to QCS_ERROR else ALOGE("camera cb: invalid state %s for " "receiving JPEG!", obj->getCameraStateStr(obj->mCameraState)); break; default: // transition to QCS_ERROR ALOGE("camera cb: unknown cb %d for JPEG!", cb); } break; CAMERA_STATE(CAMERA_FUNC_START_FOCUS) { // NO TRANSITION HERE. We acquire mStateLock here because it is // possible for ::autoFocus to be called after the call to // mAutoFocusCallback() but before we set mAutoFocusCallback // to NULL. if (obj->mAutoFocusCallback) { switch (cb) { case CAMERA_RSP_CB_SUCCESS: ALOGV("camera cb: autofocus has started."); break; case CAMERA_EXIT_CB_DONE: { ALOGV("camera cb: autofocus succeeded."); Mutex::Autolock lock(&obj->mStateLock); if (obj->mAutoFocusCallback) { obj->mAutoFocusCallback(true, obj->mAutoFocusCallbackCookie); obj->mAutoFocusCallback = NULL; } } break; case CAMERA_EXIT_CB_ABORT: ALOGE("camera cb: autofocus aborted"); break; case CAMERA_EXIT_CB_FAILED: { ALOGE("camera cb: autofocus failed"); Mutex::Autolock lock(&obj->mStateLock); if (obj->mAutoFocusCallback) { obj->mAutoFocusCallback(false, obj->mAutoFocusCallbackCookie); obj->mAutoFocusCallback = NULL; } } break; default: ALOGE("camera cb: unknown cb %d for " "CAMERA_FUNC_START_FOCUS!", cb); } } } break; default: // transition to QCS_ERROR ALOGE("Unknown camera-callback status %d", cb); } } #undef TRANSITION #undef TRANSITION_LOCKED #undef TRANSITION_ALWAYS #undef CAMERA_STATE static 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; } 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) { ALOGV("constructing MemPool %s backed by ashmem: " "%d frames @ %d bytes, 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(); } QualcommCameraHardware::PmemPool::PmemPool(const char *pmem_pool, 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) { ALOGV("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<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)) { ALOGE("pmem pool %s ioctl(PMEM_GET_SIZE) error %s (%d)", pmem_pool, ::strerror(errno), errno); mHeap.clear(); return; } ALOGV("pmem pool %s ioctl(PMEM_GET_SIZE) is %ld", pmem_pool, mSize.len); completeInitialization(); } else ALOGE("pmem pool %s error: could not create master heap!", pmem_pool); } QualcommCameraHardware::PreviewPmemPool::PreviewPmemPool( int buffer_size, int num_buffers, int frame_size, int frame_offset, const char *name) : QualcommCameraHardware::PmemPool("/dev/pmem_adsp", buffer_size, num_buffers, frame_size, frame_offset, name) { ALOGV("constructing PreviewPmemPool"); if (initialized()) { LINK_camera_assoc_pmem(QDSP_MODULE_VFETASK, mFd, mHeap->base(), mAlignedSize, 0); // external } } QualcommCameraHardware::PreviewPmemPool::~PreviewPmemPool() { ALOGV("destroying PreviewPmemPool"); if(initialized()) { void *base = mHeap->base(); ALOGV("releasing PreviewPmemPool memory %p from module %d", base, QDSP_MODULE_VFETASK); LINK_camera_release_pmem(QDSP_MODULE_VFETASK, base, mAlignedSize, true); } } QualcommCameraHardware::RawPmemPool::RawPmemPool( const char *pmem_pool, int buffer_size, int num_buffers, int frame_size, int frame_offset, const char *name) : QualcommCameraHardware::PmemPool(pmem_pool, buffer_size, num_buffers, frame_size, frame_offset, name) { ALOGV("constructing RawPmemPool"); if (initialized()) { LINK_camera_assoc_pmem(QDSP_MODULE_VFETASK, mFd, mHeap->base(), mAlignedSize, 0); // do not free, main module LINK_camera_assoc_pmem(QDSP_MODULE_LPMTASK, mFd, mHeap->base(), mAlignedSize, 2); // do not free, dependent module LINK_camera_assoc_pmem(QDSP_MODULE_JPEGTASK, mFd, mHeap->base(), mAlignedSize, 2); // do not free, dependent module } } QualcommCameraHardware::RawPmemPool::~RawPmemPool() { ALOGV("destroying RawPmemPool"); if(initialized()) { void *base = mHeap->base(); ALOGV("releasing RawPmemPool memory %p from modules %d, %d, and %d", base, QDSP_MODULE_VFETASK, QDSP_MODULE_LPMTASK, QDSP_MODULE_JPEGTASK); LINK_camera_release_pmem(QDSP_MODULE_VFETASK, base, mAlignedSize, true); LINK_camera_release_pmem(QDSP_MODULE_LPMTASK, base, mAlignedSize, true); LINK_camera_release_pmem(QDSP_MODULE_JPEGTASK, base, mAlignedSize, true); } } QualcommCameraHardware::MemPool::~MemPool() { ALOGV("destroying MemPool %s", mName); if (mFrameSize > 0) delete [] mBuffers; mHeap.clear(); ALOGV("destroying MemPool %s completed", mName); } 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 uint8_t* malloc_preview(uint32_t size, uint32_t *phy_addr, uint32_t index) { sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance(); if (obj != 0) { return (uint8_t *)obj->get_preview_mem(size, phy_addr, index); } return NULL; } static int free_preview(uint32_t *phy_addr, uint32_t size, uint32_t index) { sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance(); if (obj != 0) { obj->free_preview_mem(phy_addr, size, index); } return 0; } static uint8_t* malloc_raw(uint32_t size, uint32_t *phy_addr, uint32_t index) { sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance(); if (obj != 0) { return (uint8_t *)obj->get_raw_mem(size, phy_addr, index); } return NULL; } static int free_raw(uint32_t *phy_addr, uint32_t size, uint32_t index) { sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance(); if (obj != 0) { obj->free_raw_mem(phy_addr, size, index); } return 0; } static void cb_rex_signal_ready(void) { ALOGV("Received REX-ready signal."); rex_init_lock.lock(); rex_init_wait.broadcast(); rex_init_lock.unlock(); } const char* const QualcommCameraHardware::getCameraStateStr( QualcommCameraHardware::qualcomm_camera_state s) { static const char* states[] = { #define STATE_STR(x) #x STATE_STR(QCS_INIT), STATE_STR(QCS_IDLE), STATE_STR(QCS_ERROR), STATE_STR(QCS_PREVIEW_IN_PROGRESS), STATE_STR(QCS_WAITING_RAW), STATE_STR(QCS_WAITING_JPEG), STATE_STR(QCS_INTERNAL_PREVIEW_STOPPING), STATE_STR(QCS_INTERNAL_PREVIEW_REQUESTED), STATE_STR(QCS_INTERNAL_RAW_REQUESTED), STATE_STR(QCS_INTERNAL_STOPPING), #undef STATE_STR }; return states[s]; } }; // namespace android