diff options
Diffstat (limited to 'camera')
-rw-r--r-- | camera/Android.mk | 20 | ||||
-rw-r--r-- | camera/Camera.cpp | 142 | ||||
-rw-r--r-- | camera/CameraBase.cpp | 218 | ||||
-rw-r--r-- | camera/CameraMetadata.cpp | 411 | ||||
-rw-r--r-- | camera/CameraParameters.cpp | 5 | ||||
-rw-r--r-- | camera/ICamera.cpp | 30 | ||||
-rw-r--r-- | camera/ICameraService.cpp | 76 | ||||
-rw-r--r-- | camera/ICameraServiceListener.cpp | 86 | ||||
-rw-r--r-- | camera/IProCameraCallbacks.cpp | 128 | ||||
-rw-r--r-- | camera/IProCameraUser.cpp | 426 | ||||
-rw-r--r-- | camera/ProCamera.cpp | 431 | ||||
-rw-r--r-- | camera/tests/Android.mk | 38 | ||||
-rw-r--r-- | camera/tests/ProCameraTests.cpp | 1281 | ||||
-rw-r--r-- | camera/tests/main.cpp | 27 |
14 files changed, 3163 insertions, 156 deletions
diff --git a/camera/Android.mk b/camera/Android.mk index 7286f92..fa518ff 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -1,22 +1,36 @@ -LOCAL_PATH:= $(call my-dir) +CAMERA_CLIENT_LOCAL_PATH:= $(call my-dir) +include $(call all-subdir-makefiles) include $(CLEAR_VARS) +LOCAL_PATH := $(CAMERA_CLIENT_LOCAL_PATH) + LOCAL_SRC_FILES:= \ Camera.cpp \ + CameraMetadata.cpp \ CameraParameters.cpp \ ICamera.cpp \ ICameraClient.cpp \ ICameraService.cpp \ + ICameraServiceListener.cpp \ ICameraRecordingProxy.cpp \ - ICameraRecordingProxyListener.cpp + ICameraRecordingProxyListener.cpp \ + IProCameraUser.cpp \ + IProCameraCallbacks.cpp \ + ProCamera.cpp \ + CameraBase.cpp \ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ + liblog \ libbinder \ libhardware \ libui \ - libgui + libgui \ + libcamera_metadata \ + +LOCAL_C_INCLUDES += \ + system/media/camera/include \ LOCAL_MODULE:= libcamera_client diff --git a/camera/Camera.cpp b/camera/Camera.cpp index d43cb0b..1b136de 100644 --- a/camera/Camera.cpp +++ b/camera/Camera.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "Camera" #include <utils/Log.h> #include <utils/threads.h> +#include <utils/String16.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/IMemory.h> @@ -26,46 +27,16 @@ #include <camera/Camera.h> #include <camera/ICameraRecordingProxyListener.h> #include <camera/ICameraService.h> +#include <camera/ICamera.h> -#include <gui/ISurfaceTexture.h> +#include <gui/IGraphicBufferProducer.h> #include <gui/Surface.h> namespace android { -// client singleton for camera service binder interface -Mutex Camera::mLock; -sp<ICameraService> Camera::mCameraService; -sp<Camera::DeathNotifier> Camera::mDeathNotifier; - -// establish binder interface to camera service -const sp<ICameraService>& Camera::getCameraService() -{ - Mutex::Autolock _l(mLock); - if (mCameraService.get() == 0) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - do { - binder = sm->getService(String16("media.camera")); - if (binder != 0) - break; - ALOGW("CameraService not published, waiting..."); - usleep(500000); // 0.5 s - } while(true); - if (mDeathNotifier == NULL) { - mDeathNotifier = new DeathNotifier(); - } - binder->linkToDeath(mDeathNotifier); - mCameraService = interface_cast<ICameraService>(binder); - } - ALOGE_IF(mCameraService==0, "no CameraService!?"); - return mCameraService; -} - -// --------------------------------------------------------------------------- - -Camera::Camera() +Camera::Camera(int cameraId) + : CameraBase(cameraId) { - init(); } // construct a camera client from an existing camera remote @@ -77,7 +48,7 @@ sp<Camera> Camera::create(const sp<ICamera>& camera) return 0; } - sp<Camera> c = new Camera(); + sp<Camera> c = new Camera(-1); if (camera->connect(c) == NO_ERROR) { c->mStatus = NO_ERROR; c->mCamera = camera; @@ -87,11 +58,6 @@ sp<Camera> Camera::create(const sp<ICamera>& camera) return 0; } -void Camera::init() -{ - mStatus = UNKNOWN_ERROR; -} - Camera::~Camera() { // We don't need to call disconnect() here because if the CameraService @@ -102,45 +68,10 @@ Camera::~Camera() // deadlock if we call any method of ICamera here. } -int32_t Camera::getNumberOfCameras() -{ - const sp<ICameraService>& cs = getCameraService(); - if (cs == 0) return 0; - return cs->getNumberOfCameras(); -} - -status_t Camera::getCameraInfo(int cameraId, - struct CameraInfo* cameraInfo) { - const sp<ICameraService>& cs = getCameraService(); - if (cs == 0) return UNKNOWN_ERROR; - return cs->getCameraInfo(cameraId, cameraInfo); -} - -sp<Camera> Camera::connect(int cameraId) -{ - ALOGV("connect"); - sp<Camera> c = new Camera(); - const sp<ICameraService>& cs = getCameraService(); - if (cs != 0) { - c->mCamera = cs->connect(c, cameraId); - } - if (c->mCamera != 0) { - c->mCamera->asBinder()->linkToDeath(c); - c->mStatus = NO_ERROR; - } else { - c.clear(); - } - return c; -} - -void Camera::disconnect() +sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName, + int clientUid) { - ALOGV("disconnect"); - if (mCamera != 0) { - mCamera->disconnect(); - mCamera->asBinder()->unlinkToDeath(this); - mCamera = 0; - } + return CameraBaseT::connect(cameraId, clientPackageName, clientUid); } status_t Camera::reconnect() @@ -151,11 +82,6 @@ status_t Camera::reconnect() return c->connect(this); } -sp<ICamera> Camera::remote() -{ - return mCamera; -} - status_t Camera::lock() { sp <ICamera> c = mCamera; @@ -170,32 +96,14 @@ status_t Camera::unlock() return c->unlock(); } -// pass the buffered Surface to the camera service -status_t Camera::setPreviewDisplay(const sp<Surface>& surface) -{ - ALOGV("setPreviewDisplay(%p)", surface.get()); - sp <ICamera> c = mCamera; - if (c == 0) return NO_INIT; - if (surface != 0) { - return c->setPreviewDisplay(surface); - } else { - ALOGD("app passed NULL surface"); - return c->setPreviewDisplay(0); - } -} - -// pass the buffered ISurfaceTexture to the camera service -status_t Camera::setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture) +// pass the buffered IGraphicBufferProducer to the camera service +status_t Camera::setPreviewTexture(const sp<IGraphicBufferProducer>& bufferProducer) { - ALOGV("setPreviewTexture(%p)", surfaceTexture.get()); + ALOGV("setPreviewTexture(%p)", bufferProducer.get()); sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; - if (surfaceTexture != 0) { - return c->setPreviewTexture(surfaceTexture); - } else { - ALOGD("app passed NULL surface"); - return c->setPreviewTexture(0); - } + ALOGD_IF(bufferProducer == 0, "app passed NULL surface"); + return c->setPreviewTexture(bufferProducer); } // start preview mode @@ -350,14 +258,7 @@ void Camera::setPreviewCallbackFlags(int flag) // callback from camera service void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { - sp<CameraListener> listener; - { - Mutex::Autolock _l(mLock); - listener = mListener; - } - if (listener != NULL) { - listener->notify(msgType, ext1, ext2); - } + return CameraBaseT::notifyCallback(msgType, ext1, ext2); } // callback from camera service when frame or image is ready @@ -395,6 +296,7 @@ void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp< Mutex::Autolock _l(mLock); listener = mListener; } + if (listener != NULL) { listener->postDataTimestamp(timestamp, msgType, dataPtr); } else { @@ -403,18 +305,6 @@ void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp< } } -void Camera::binderDied(const wp<IBinder>& who) { - ALOGW("ICamera died"); - notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, 0); -} - -void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) { - ALOGV("binderDied"); - Mutex::Autolock _l(Camera::mLock); - Camera::mCameraService.clear(); - ALOGW("Camera server died!"); -} - sp<ICameraRecordingProxy> Camera::getRecordingProxy() { ALOGV("getProxy"); return new RecordingProxy(this); diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp new file mode 100644 index 0000000..c25c5fd --- /dev/null +++ b/camera/CameraBase.cpp @@ -0,0 +1,218 @@ +/* +** +** Copyright (C) 2013, 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "CameraBase" +#include <utils/Log.h> +#include <utils/threads.h> +#include <utils/Mutex.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> + +#include <camera/CameraBase.h> +#include <camera/ICameraService.h> + +// needed to instantiate +#include <camera/ProCamera.h> +#include <camera/Camera.h> + +#include <system/camera_metadata.h> + +namespace android { + +namespace { + sp<ICameraService> gCameraService; + const int kCameraServicePollDelay = 500000; // 0.5s + const char* kCameraServiceName = "media.camera"; + + Mutex gLock; + + class DeathNotifier : public IBinder::DeathRecipient + { + public: + DeathNotifier() { + } + + virtual void binderDied(const wp<IBinder>& who) { + ALOGV("binderDied"); + Mutex::Autolock _l(gLock); + gCameraService.clear(); + ALOGW("Camera service died!"); + } + }; + + sp<DeathNotifier> gDeathNotifier; +}; // namespace anonymous + +/////////////////////////////////////////////////////////// +// CameraBase definition +/////////////////////////////////////////////////////////// + +// establish binder interface to camera service +template <typename TCam, typename TCamTraits> +const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService() +{ + Mutex::Autolock _l(gLock); + if (gCameraService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16(kCameraServiceName)); + if (binder != 0) { + break; + } + ALOGW("CameraService not published, waiting..."); + usleep(kCameraServicePollDelay); + } while(true); + if (gDeathNotifier == NULL) { + gDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(gDeathNotifier); + gCameraService = interface_cast<ICameraService>(binder); + } + ALOGE_IF(gCameraService == 0, "no CameraService!?"); + return gCameraService; +} + +template <typename TCam, typename TCamTraits> +sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId, + const String16& clientPackageName, + int clientUid) +{ + ALOGV("%s: connect", __FUNCTION__); + sp<TCam> c = new TCam(cameraId); + sp<TCamCallbacks> cl = c; + const sp<ICameraService>& cs = getCameraService(); + if (cs != 0) { + c->mCamera = cs->connect(cl, cameraId, clientPackageName, clientUid); + } + if (c->mCamera != 0) { + c->mCamera->asBinder()->linkToDeath(c); + c->mStatus = NO_ERROR; + } else { + c.clear(); + } + return c; +} + +template <typename TCam, typename TCamTraits> +void CameraBase<TCam, TCamTraits>::disconnect() +{ + ALOGV("%s: disconnect", __FUNCTION__); + if (mCamera != 0) { + mCamera->disconnect(); + mCamera->asBinder()->unlinkToDeath(this); + mCamera = 0; + } + ALOGV("%s: disconnect (done)", __FUNCTION__); +} + +template <typename TCam, typename TCamTraits> +CameraBase<TCam, TCamTraits>::CameraBase(int cameraId) : + mStatus(UNKNOWN_ERROR), + mCameraId(cameraId) +{ +} + +template <typename TCam, typename TCamTraits> +CameraBase<TCam, TCamTraits>::~CameraBase() +{ +} + +template <typename TCam, typename TCamTraits> +sp<typename TCamTraits::TCamUser> CameraBase<TCam, TCamTraits>::remote() +{ + return mCamera; +} + +template <typename TCam, typename TCamTraits> +status_t CameraBase<TCam, TCamTraits>::getStatus() +{ + return mStatus; +} + +template <typename TCam, typename TCamTraits> +void CameraBase<TCam, TCamTraits>::binderDied(const wp<IBinder>& who) { + ALOGW("mediaserver's remote binder Camera object died"); + notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, /*ext2*/0); +} + +template <typename TCam, typename TCamTraits> +void CameraBase<TCam, TCamTraits>::setListener(const sp<TCamListener>& listener) +{ + Mutex::Autolock _l(mLock); + mListener = listener; +} + +// callback from camera service +template <typename TCam, typename TCamTraits> +void CameraBase<TCam, TCamTraits>::notifyCallback(int32_t msgType, + int32_t ext1, + int32_t ext2) +{ + sp<TCamListener> listener; + { + Mutex::Autolock _l(mLock); + listener = mListener; + } + if (listener != NULL) { + listener->notify(msgType, ext1, ext2); + } +} + +template <typename TCam, typename TCamTraits> +int CameraBase<TCam, TCamTraits>::getNumberOfCameras() { + const sp<ICameraService> cs = getCameraService(); + + if (!cs.get()) { + // as required by the public Java APIs + return 0; + } + return cs->getNumberOfCameras(); +} + +// this can be in BaseCamera but it should be an instance method +template <typename TCam, typename TCamTraits> +status_t CameraBase<TCam, TCamTraits>::getCameraInfo(int cameraId, + struct CameraInfo* cameraInfo) { + const sp<ICameraService>& cs = getCameraService(); + if (cs == 0) return UNKNOWN_ERROR; + return cs->getCameraInfo(cameraId, cameraInfo); +} + +template <typename TCam, typename TCamTraits> +status_t CameraBase<TCam, TCamTraits>::addServiceListener( + const sp<ICameraServiceListener>& listener) { + const sp<ICameraService>& cs = getCameraService(); + if (cs == 0) return UNKNOWN_ERROR; + return cs->addListener(listener); +} + +template <typename TCam, typename TCamTraits> +status_t CameraBase<TCam, TCamTraits>::removeServiceListener( + const sp<ICameraServiceListener>& listener) { + const sp<ICameraService>& cs = getCameraService(); + if (cs == 0) return UNKNOWN_ERROR; + return cs->removeListener(listener); +} + +template class CameraBase<ProCamera>; +template class CameraBase<Camera>; + +} // namespace android diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp new file mode 100644 index 0000000..a8f9eff --- /dev/null +++ b/camera/CameraMetadata.cpp @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2012 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. + */ + +// #define LOG_NDEBUG 0 + +#define LOG_TAG "Camera2-Metadata" +#include <utils/Log.h> +#include <utils/Errors.h> + +#include <camera/CameraMetadata.h> + +namespace android { + +CameraMetadata::CameraMetadata() : + mBuffer(NULL), mLocked(false) { +} + +CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) : + mLocked(false) +{ + mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity); +} + +CameraMetadata::CameraMetadata(const CameraMetadata &other) : + mLocked(false) { + mBuffer = clone_camera_metadata(other.mBuffer); +} + +CameraMetadata::CameraMetadata(camera_metadata_t *buffer) : + mBuffer(NULL), mLocked(false) { + acquire(buffer); +} + +CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) { + return operator=(other.mBuffer); +} + +CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) { + if (mLocked) { + ALOGE("%s: Assignment to a locked CameraMetadata!", __FUNCTION__); + return *this; + } + + if (CC_LIKELY(buffer != mBuffer)) { + camera_metadata_t *newBuffer = clone_camera_metadata(buffer); + clear(); + mBuffer = newBuffer; + } + return *this; +} + +CameraMetadata::~CameraMetadata() { + mLocked = false; + clear(); +} + +const camera_metadata_t* CameraMetadata::getAndLock() { + mLocked = true; + return mBuffer; +} + +status_t CameraMetadata::unlock(const camera_metadata_t *buffer) { + if (!mLocked) { + ALOGE("%s: Can't unlock a non-locked CameraMetadata!", __FUNCTION__); + return INVALID_OPERATION; + } + if (buffer != mBuffer) { + ALOGE("%s: Can't unlock CameraMetadata with wrong pointer!", + __FUNCTION__); + return BAD_VALUE; + } + mLocked = false; + return OK; +} + +camera_metadata_t* CameraMetadata::release() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return NULL; + } + camera_metadata_t *released = mBuffer; + mBuffer = NULL; + return released; +} + +void CameraMetadata::clear() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } + if (mBuffer) { + free_camera_metadata(mBuffer); + mBuffer = NULL; + } +} + +void CameraMetadata::acquire(camera_metadata_t *buffer) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } + clear(); + mBuffer = buffer; + + ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) != OK, + "%s: Failed to validate metadata structure %p", + __FUNCTION__, buffer); +} + +void CameraMetadata::acquire(CameraMetadata &other) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } + acquire(other.release()); +} + +status_t CameraMetadata::append(const CameraMetadata &other) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + return append_camera_metadata(mBuffer, other.mBuffer); +} + +size_t CameraMetadata::entryCount() const { + return (mBuffer == NULL) ? 0 : + get_camera_metadata_entry_count(mBuffer); +} + +bool CameraMetadata::isEmpty() const { + return entryCount() == 0; +} + +status_t CameraMetadata::sort() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + return sort_camera_metadata(mBuffer); +} + +status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) { + int tagType = get_camera_metadata_tag_type(tag); + if ( CC_UNLIKELY(tagType == -1)) { + ALOGE("Update metadata entry: Unknown tag %d", tag); + return INVALID_OPERATION; + } + if ( CC_UNLIKELY(tagType != expectedType) ) { + ALOGE("Mismatched tag type when updating entry %s (%d) of type %s; " + "got type %s data instead ", + get_camera_metadata_tag_name(tag), tag, + camera_metadata_type_names[tagType], + camera_metadata_type_names[expectedType]); + return INVALID_OPERATION; + } + return OK; +} + +status_t CameraMetadata::update(uint32_t tag, + const int32_t *data, size_t data_count) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + if ( (res = checkType(tag, TYPE_INT32)) != OK) { + return res; + } + return updateImpl(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const uint8_t *data, size_t data_count) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + if ( (res = checkType(tag, TYPE_BYTE)) != OK) { + return res; + } + return updateImpl(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const float *data, size_t data_count) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + if ( (res = checkType(tag, TYPE_FLOAT)) != OK) { + return res; + } + return updateImpl(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const int64_t *data, size_t data_count) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + if ( (res = checkType(tag, TYPE_INT64)) != OK) { + return res; + } + return updateImpl(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const double *data, size_t data_count) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + if ( (res = checkType(tag, TYPE_DOUBLE)) != OK) { + return res; + } + return updateImpl(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const camera_metadata_rational_t *data, size_t data_count) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + if ( (res = checkType(tag, TYPE_RATIONAL)) != OK) { + return res; + } + return updateImpl(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const String8 &string) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + if ( (res = checkType(tag, TYPE_BYTE)) != OK) { + return res; + } + return updateImpl(tag, (const void*)string.string(), string.size()); +} + +status_t CameraMetadata::updateImpl(uint32_t tag, const void *data, + size_t data_count) { + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + int type = get_camera_metadata_tag_type(tag); + if (type == -1) { + ALOGE("%s: Tag %d not found", __FUNCTION__, tag); + return BAD_VALUE; + } + size_t data_size = calculate_camera_metadata_entry_data_size(type, + data_count); + + res = resizeIfNeeded(1, data_size); + + if (res == OK) { + camera_metadata_entry_t entry; + res = find_camera_metadata_entry(mBuffer, tag, &entry); + if (res == NAME_NOT_FOUND) { + res = add_camera_metadata_entry(mBuffer, + tag, data, data_count); + } else if (res == OK) { + res = update_camera_metadata_entry(mBuffer, + entry.index, data, data_count, NULL); + } + } + + if (res != OK) { + ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)", + __FUNCTION__, get_camera_metadata_section_name(tag), + get_camera_metadata_tag_name(tag), tag, strerror(-res), res); + } + + IF_ALOGV() { + ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) != + OK, + + "%s: Failed to validate metadata structure after update %p", + __FUNCTION__, mBuffer); + } + + return res; +} + +bool CameraMetadata::exists(uint32_t tag) const { + camera_metadata_ro_entry entry; + return find_camera_metadata_ro_entry(mBuffer, tag, &entry) == 0; +} + +camera_metadata_entry_t CameraMetadata::find(uint32_t tag) { + status_t res; + camera_metadata_entry entry; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + entry.count = 0; + return entry; + } + res = find_camera_metadata_entry(mBuffer, tag, &entry); + if (CC_UNLIKELY( res != OK )) { + entry.count = 0; + entry.data.u8 = NULL; + } + return entry; +} + +camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const { + status_t res; + camera_metadata_ro_entry entry; + res = find_camera_metadata_ro_entry(mBuffer, tag, &entry); + if (CC_UNLIKELY( res != OK )) { + entry.count = 0; + entry.data.u8 = NULL; + } + return entry; +} + +status_t CameraMetadata::erase(uint32_t tag) { + camera_metadata_entry_t entry; + status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } + res = find_camera_metadata_entry(mBuffer, tag, &entry); + if (res == NAME_NOT_FOUND) { + return OK; + } else if (res != OK) { + ALOGE("%s: Error looking for entry %s.%s (%x): %s %d", + __FUNCTION__, + get_camera_metadata_section_name(tag), + get_camera_metadata_tag_name(tag), tag, strerror(-res), res); + return res; + } + res = delete_camera_metadata_entry(mBuffer, entry.index); + if (res != OK) { + ALOGE("%s: Error deleting entry %s.%s (%x): %s %d", + __FUNCTION__, + get_camera_metadata_section_name(tag), + get_camera_metadata_tag_name(tag), tag, strerror(-res), res); + } + return res; +} + +void CameraMetadata::dump(int fd, int verbosity, int indentation) const { + dump_indented_camera_metadata(mBuffer, fd, verbosity, indentation); +} + +status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) { + if (mBuffer == NULL) { + mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2); + if (mBuffer == NULL) { + ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__); + return NO_MEMORY; + } + } else { + size_t currentEntryCount = get_camera_metadata_entry_count(mBuffer); + size_t currentEntryCap = get_camera_metadata_entry_capacity(mBuffer); + size_t newEntryCount = currentEntryCount + + extraEntries; + newEntryCount = (newEntryCount > currentEntryCap) ? + newEntryCount * 2 : currentEntryCap; + + size_t currentDataCount = get_camera_metadata_data_count(mBuffer); + size_t currentDataCap = get_camera_metadata_data_capacity(mBuffer); + size_t newDataCount = currentDataCount + + extraData; + newDataCount = (newDataCount > currentDataCap) ? + newDataCount * 2 : currentDataCap; + + if (newEntryCount > currentEntryCap || + newDataCount > currentDataCap) { + camera_metadata_t *oldBuffer = mBuffer; + mBuffer = allocate_camera_metadata(newEntryCount, + newDataCount); + if (mBuffer == NULL) { + ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__); + return NO_MEMORY; + } + append_camera_metadata(mBuffer, oldBuffer); + free_camera_metadata(oldBuffer); + } + } + return OK; +} + +}; // namespace android diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp index 661f49c..c51f265 100644 --- a/camera/CameraParameters.cpp +++ b/camera/CameraParameters.cpp @@ -90,6 +90,7 @@ const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint"; const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported"; const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization"; const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported"; +const char CameraParameters::KEY_LIGHTFX[] = "light-fx"; const char CameraParameters::TRUE[] = "true"; const char CameraParameters::FALSE[] = "false"; @@ -167,6 +168,10 @@ const char CameraParameters::FOCUS_MODE_EDOF[] = "edof"; const char CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO[] = "continuous-video"; const char CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE[] = "continuous-picture"; +// Values for light fx settings +const char CameraParameters::LIGHTFX_LOWLIGHT[] = "low-light"; +const char CameraParameters::LIGHTFX_HDR[] = "high-dynamic-range"; + CameraParameters::CameraParameters() : mMap() { diff --git a/camera/ICamera.cpp b/camera/ICamera.cpp index 8d8408c..8900867 100644 --- a/camera/ICamera.cpp +++ b/camera/ICamera.cpp @@ -22,14 +22,13 @@ #include <sys/types.h> #include <binder/Parcel.h> #include <camera/ICamera.h> -#include <gui/ISurfaceTexture.h> +#include <gui/IGraphicBufferProducer.h> #include <gui/Surface.h> namespace android { enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, - SET_PREVIEW_DISPLAY, SET_PREVIEW_TEXTURE, SET_PREVIEW_CALLBACK_FLAG, START_PREVIEW, @@ -68,24 +67,13 @@ public: remote()->transact(DISCONNECT, data, &reply); } - // pass the buffered Surface to the camera service - status_t setPreviewDisplay(const sp<Surface>& surface) - { - ALOGV("setPreviewDisplay"); - Parcel data, reply; - data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - Surface::writeToParcel(surface, &data); - remote()->transact(SET_PREVIEW_DISPLAY, data, &reply); - return reply.readInt32(); - } - - // pass the buffered SurfaceTexture to the camera service - status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture) + // pass the buffered IGraphicBufferProducer to the camera service + status_t setPreviewTexture(const sp<IGraphicBufferProducer>& bufferProducer) { ALOGV("setPreviewTexture"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - sp<IBinder> b(surfaceTexture->asBinder()); + sp<IBinder> b(bufferProducer->asBinder()); data.writeStrongBinder(b); remote()->transact(SET_PREVIEW_TEXTURE, data, &reply); return reply.readInt32(); @@ -282,17 +270,11 @@ status_t BnCamera::onTransact( disconnect(); return NO_ERROR; } break; - case SET_PREVIEW_DISPLAY: { - ALOGV("SET_PREVIEW_DISPLAY"); - CHECK_INTERFACE(ICamera, data, reply); - sp<Surface> surface = Surface::readFromParcel(data); - reply->writeInt32(setPreviewDisplay(surface)); - return NO_ERROR; - } break; case SET_PREVIEW_TEXTURE: { ALOGV("SET_PREVIEW_TEXTURE"); CHECK_INTERFACE(ICamera, data, reply); - sp<ISurfaceTexture> st = interface_cast<ISurfaceTexture>(data.readStrongBinder()); + sp<IGraphicBufferProducer> st = + interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); reply->writeInt32(setPreviewTexture(st)); return NO_ERROR; } break; diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index f2d367e..134f7f0 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -23,6 +23,11 @@ #include <binder/IServiceManager.h> #include <camera/ICameraService.h> +#include <camera/ICameraServiceListener.h> +#include <camera/IProCameraUser.h> +#include <camera/IProCameraCallbacks.h> +#include <camera/ICamera.h> +#include <camera/ICameraClient.h> namespace android { @@ -56,15 +61,50 @@ public: } // connect to camera service - virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId) + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId, + const String16 &clientPackageName, int clientUid) { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); data.writeStrongBinder(cameraClient->asBinder()); data.writeInt32(cameraId); + data.writeString16(clientPackageName); + data.writeInt32(clientUid); remote()->transact(BnCameraService::CONNECT, data, &reply); return interface_cast<ICamera>(reply.readStrongBinder()); } + + // connect to camera service (pro client) + virtual sp<IProCameraUser> connect(const sp<IProCameraCallbacks>& cameraCb, int cameraId, + const String16 &clientPackageName, int clientUid) + { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + data.writeStrongBinder(cameraCb->asBinder()); + data.writeInt32(cameraId); + data.writeString16(clientPackageName); + data.writeInt32(clientUid); + remote()->transact(BnCameraService::CONNECT_PRO, data, &reply); + return interface_cast<IProCameraUser>(reply.readStrongBinder()); + } + + virtual status_t addListener(const sp<ICameraServiceListener>& listener) + { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + remote()->transact(BnCameraService::ADD_LISTENER, data, &reply); + return reply.readInt32(); + } + + virtual status_t removeListener(const sp<ICameraServiceListener>& listener) + { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + remote()->transact(BnCameraService::REMOVE_LISTENER, data, &reply); + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService"); @@ -92,11 +132,41 @@ status_t BnCameraService::onTransact( } break; case CONNECT: { CHECK_INTERFACE(ICameraService, data, reply); - sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder()); - sp<ICamera> camera = connect(cameraClient, data.readInt32()); + sp<ICameraClient> cameraClient = + interface_cast<ICameraClient>(data.readStrongBinder()); + int32_t cameraId = data.readInt32(); + const String16 clientName = data.readString16(); + int32_t clientUid = data.readInt32(); + sp<ICamera> camera = connect(cameraClient, cameraId, + clientName, clientUid); + reply->writeStrongBinder(camera->asBinder()); + return NO_ERROR; + } break; + case CONNECT_PRO: { + CHECK_INTERFACE(ICameraService, data, reply); + sp<IProCameraCallbacks> cameraClient = interface_cast<IProCameraCallbacks>(data.readStrongBinder()); + int32_t cameraId = data.readInt32(); + const String16 clientName = data.readString16(); + int32_t clientUid = data.readInt32(); + sp<IProCameraUser> camera = connect(cameraClient, cameraId, + clientName, clientUid); reply->writeStrongBinder(camera->asBinder()); return NO_ERROR; } break; + case ADD_LISTENER: { + CHECK_INTERFACE(ICameraService, data, reply); + sp<ICameraServiceListener> listener = + interface_cast<ICameraServiceListener>(data.readStrongBinder()); + reply->writeInt32(addListener(listener)); + return NO_ERROR; + } break; + case REMOVE_LISTENER: { + CHECK_INTERFACE(ICameraService, data, reply); + sp<ICameraServiceListener> listener = + interface_cast<ICameraServiceListener>(data.readStrongBinder()); + reply->writeInt32(removeListener(listener)); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp new file mode 100644 index 0000000..640ee35 --- /dev/null +++ b/camera/ICameraServiceListener.cpp @@ -0,0 +1,86 @@ +/* +** +** Copyright 2013, 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. +*/ + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <camera/ICameraServiceListener.h> + +namespace android { + +namespace { + enum { + STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION, + }; +}; // namespace anonymous + +class BpCameraServiceListener: public BpInterface<ICameraServiceListener> +{ + +public: + BpCameraServiceListener(const sp<IBinder>& impl) + : BpInterface<ICameraServiceListener>(impl) + { + } + + virtual void onStatusChanged(Status status, int32_t cameraId) + { + Parcel data, reply; + data.writeInterfaceToken( + ICameraServiceListener::getInterfaceDescriptor()); + + data.writeInt32(static_cast<int32_t>(status)); + data.writeInt32(cameraId); + + remote()->transact(STATUS_CHANGED, + data, + &reply, + IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(CameraServiceListener, + "android.hardware.ICameraServiceListener"); + +// ---------------------------------------------------------------------- + +status_t BnCameraServiceListener::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case STATUS_CHANGED: { + CHECK_INTERFACE(ICameraServiceListener, data, reply); + + Status status = static_cast<Status>(data.readInt32()); + int32_t cameraId = data.readInt32(); + + onStatusChanged(status, cameraId); + + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/camera/IProCameraCallbacks.cpp b/camera/IProCameraCallbacks.cpp new file mode 100644 index 0000000..b9cd14d --- /dev/null +++ b/camera/IProCameraCallbacks.cpp @@ -0,0 +1,128 @@ +/* +** +** Copyright 2013, 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "IProCameraCallbacks" +#include <utils/Log.h> +#include <stdint.h> +#include <sys/types.h> + +#include <binder/Parcel.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> +#include <utils/Mutex.h> + +#include <camera/IProCameraCallbacks.h> + +#include <system/camera_metadata.h> + +namespace android { + +enum { + NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, + LOCK_STATUS_CHANGED, + RESULT_RECEIVED, +}; + +void readMetadata(const Parcel& data, camera_metadata_t** out); +void writeMetadata(Parcel& data, camera_metadata_t* metadata); + +class BpProCameraCallbacks: public BpInterface<IProCameraCallbacks> +{ +public: + BpProCameraCallbacks(const sp<IBinder>& impl) + : BpInterface<IProCameraCallbacks>(impl) + { + } + + // generic callback from camera service to app + void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) + { + ALOGV("notifyCallback"); + Parcel data, reply; + data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor()); + data.writeInt32(msgType); + data.writeInt32(ext1); + data.writeInt32(ext2); + remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + void onLockStatusChanged(LockStatus newLockStatus) { + ALOGV("onLockStatusChanged"); + Parcel data, reply; + data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor()); + data.writeInt32(newLockStatus); + remote()->transact(LOCK_STATUS_CHANGED, data, &reply, + IBinder::FLAG_ONEWAY); + } + + void onResultReceived(int32_t frameId, camera_metadata* result) { + ALOGV("onResultReceived"); + Parcel data, reply; + data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor()); + data.writeInt32(frameId); + writeMetadata(data, result); + remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(ProCameraCallbacks, + "android.hardware.IProCameraCallbacks"); + +// ---------------------------------------------------------------------- + +status_t BnProCameraCallbacks::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + ALOGV("onTransact - code = %d", code); + switch(code) { + case NOTIFY_CALLBACK: { + ALOGV("NOTIFY_CALLBACK"); + CHECK_INTERFACE(IProCameraCallbacks, data, reply); + int32_t msgType = data.readInt32(); + int32_t ext1 = data.readInt32(); + int32_t ext2 = data.readInt32(); + notifyCallback(msgType, ext1, ext2); + return NO_ERROR; + } break; + case LOCK_STATUS_CHANGED: { + ALOGV("LOCK_STATUS_CHANGED"); + CHECK_INTERFACE(IProCameraCallbacks, data, reply); + LockStatus newLockStatus + = static_cast<LockStatus>(data.readInt32()); + onLockStatusChanged(newLockStatus); + return NO_ERROR; + } break; + case RESULT_RECEIVED: { + ALOGV("RESULT_RECEIVED"); + CHECK_INTERFACE(IProCameraCallbacks, data, reply); + int32_t frameId = data.readInt32(); + camera_metadata_t *result = NULL; + readMetadata(data, &result); + onResultReceived(frameId, result); + return NO_ERROR; + break; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/camera/IProCameraUser.cpp b/camera/IProCameraUser.cpp new file mode 100644 index 0000000..4c4dec3 --- /dev/null +++ b/camera/IProCameraUser.cpp @@ -0,0 +1,426 @@ +/* +** +** Copyright 2013, 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "IProCameraUser" +#include <utils/Log.h> +#include <stdint.h> +#include <sys/types.h> +#include <binder/Parcel.h> +#include <camera/IProCameraUser.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> +#include <system/camera_metadata.h> + +namespace android { + +typedef Parcel::WritableBlob WritableBlob; +typedef Parcel::ReadableBlob ReadableBlob; + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + CONNECT, + EXCLUSIVE_TRY_LOCK, + EXCLUSIVE_LOCK, + EXCLUSIVE_UNLOCK, + HAS_EXCLUSIVE_LOCK, + SUBMIT_REQUEST, + CANCEL_REQUEST, + DELETE_STREAM, + CREATE_STREAM, + CREATE_DEFAULT_REQUEST, + GET_CAMERA_INFO, +}; + +/** + * Caller becomes the owner of the new metadata + * 'const Parcel' doesnt prevent us from calling the read functions. + * which is interesting since it changes the internal state + * + * NULL can be returned when no metadata was sent, OR if there was an issue + * unpacking the serialized data (i.e. bad parcel or invalid structure). + */ +void readMetadata(const Parcel& data, camera_metadata_t** out) { + + status_t err = OK; + + camera_metadata_t* metadata = NULL; + + if (out) { + *out = NULL; + } + + // arg0 = metadataSize (int32) + int32_t metadataSizeTmp = -1; + if ((err = data.readInt32(&metadataSizeTmp)) != OK) { + ALOGE("%s: Failed to read metadata size (error %d %s)", + __FUNCTION__, err, strerror(-err)); + return; + } + const size_t metadataSize = static_cast<size_t>(metadataSizeTmp); + + if (metadataSize == 0) { + return; + } + + // NOTE: this doesn't make sense to me. shouldnt the blob + // know how big it is? why do we have to specify the size + // to Parcel::readBlob ? + + ReadableBlob blob; + // arg1 = metadata (blob) + do { + if ((err = data.readBlob(metadataSize, &blob)) != OK) { + ALOGE("%s: Failed to read metadata blob (sized %d). Possible " + " serialization bug. Error %d %s", + __FUNCTION__, metadataSize, err, strerror(-err)); + break; + } + const camera_metadata_t* tmp = + reinterpret_cast<const camera_metadata_t*>(blob.data()); + + metadata = allocate_copy_camera_metadata_checked(tmp, metadataSize); + } while(0); + blob.release(); + + if (out) { + *out = metadata; + } else if (metadata != NULL) { + free_camera_metadata(metadata); + } +} + +/** + * Caller retains ownership of metadata + * - Write 2 (int32 + blob) args in the current position + */ +void writeMetadata(Parcel& data, camera_metadata_t* metadata) { + // arg0 = metadataSize (int32) + + if (metadata == NULL) { + data.writeInt32(0); + return; + } + + const size_t metadataSize = get_camera_metadata_compact_size(metadata); + data.writeInt32(static_cast<int32_t>(metadataSize)); + + // arg1 = metadata (blob) + WritableBlob blob; + { + data.writeBlob(metadataSize, &blob); + copy_camera_metadata(blob.data(), metadataSize, metadata); + + IF_ALOGV() { + if (validate_camera_metadata_structure( + (const camera_metadata_t*)blob.data(), + &metadataSize) != OK) { + ALOGV("%s: Failed to validate metadata %p after writing blob", + __FUNCTION__, blob.data()); + } else { + ALOGV("%s: Metadata written to blob. Validation success", + __FUNCTION__); + } + } + + // Not too big of a problem since receiving side does hard validation + if (validate_camera_metadata_structure(metadata, &metadataSize) != OK) { + ALOGW("%s: Failed to validate metadata %p before writing blob", + __FUNCTION__, metadata); + } + + } + blob.release(); +} + +class BpProCameraUser: public BpInterface<IProCameraUser> +{ +public: + BpProCameraUser(const sp<IBinder>& impl) + : BpInterface<IProCameraUser>(impl) + { + } + + // disconnect from camera service + void disconnect() + { + ALOGV("disconnect"); + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + virtual status_t connect(const sp<IProCameraCallbacks>& cameraClient) + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + data.writeStrongBinder(cameraClient->asBinder()); + remote()->transact(CONNECT, data, &reply); + return reply.readInt32(); + } + + /* Shared ProCameraUser */ + + virtual status_t exclusiveTryLock() + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + remote()->transact(EXCLUSIVE_TRY_LOCK, data, &reply); + return reply.readInt32(); + } + virtual status_t exclusiveLock() + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + remote()->transact(EXCLUSIVE_LOCK, data, &reply); + return reply.readInt32(); + } + + virtual status_t exclusiveUnlock() + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + remote()->transact(EXCLUSIVE_UNLOCK, data, &reply); + return reply.readInt32(); + } + + virtual bool hasExclusiveLock() + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + remote()->transact(HAS_EXCLUSIVE_LOCK, data, &reply); + return !!reply.readInt32(); + } + + virtual int submitRequest(camera_metadata_t* metadata, bool streaming) + { + + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + + // arg0+arg1 + writeMetadata(data, metadata); + + // arg2 = streaming (bool) + data.writeInt32(streaming); + + remote()->transact(SUBMIT_REQUEST, data, &reply); + return reply.readInt32(); + } + + virtual status_t cancelRequest(int requestId) + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + data.writeInt32(requestId); + + remote()->transact(CANCEL_REQUEST, data, &reply); + return reply.readInt32(); + } + + virtual status_t deleteStream(int streamId) + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + data.writeInt32(streamId); + + remote()->transact(DELETE_STREAM, data, &reply); + return reply.readInt32(); + } + + virtual status_t createStream(int width, int height, int format, + const sp<IGraphicBufferProducer>& bufferProducer, + /*out*/ + int* streamId) + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + data.writeInt32(width); + data.writeInt32(height); + data.writeInt32(format); + + sp<IBinder> b(bufferProducer->asBinder()); + data.writeStrongBinder(b); + + remote()->transact(CREATE_STREAM, data, &reply); + + int sId = reply.readInt32(); + if (streamId) { + *streamId = sId; + } + return reply.readInt32(); + } + + // Create a request object from a template. + virtual status_t createDefaultRequest(int templateId, + /*out*/ + camera_metadata** request) + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + data.writeInt32(templateId); + remote()->transact(CREATE_DEFAULT_REQUEST, data, &reply); + readMetadata(reply, /*out*/request); + return reply.readInt32(); + } + + + virtual status_t getCameraInfo(int cameraId, camera_metadata** info) + { + Parcel data, reply; + data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); + data.writeInt32(cameraId); + remote()->transact(GET_CAMERA_INFO, data, &reply); + readMetadata(reply, /*out*/info); + return reply.readInt32(); + } + + +private: + + +}; + +IMPLEMENT_META_INTERFACE(ProCameraUser, "android.hardware.IProCameraUser"); + +// ---------------------------------------------------------------------- + +status_t BnProCameraUser::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DISCONNECT: { + ALOGV("DISCONNECT"); + CHECK_INTERFACE(IProCameraUser, data, reply); + disconnect(); + return NO_ERROR; + } break; + case CONNECT: { + CHECK_INTERFACE(IProCameraUser, data, reply); + sp<IProCameraCallbacks> cameraClient = + interface_cast<IProCameraCallbacks>(data.readStrongBinder()); + reply->writeInt32(connect(cameraClient)); + return NO_ERROR; + } break; + + /* Shared ProCameraUser */ + case EXCLUSIVE_TRY_LOCK: { + CHECK_INTERFACE(IProCameraUser, data, reply); + reply->writeInt32(exclusiveTryLock()); + return NO_ERROR; + } break; + case EXCLUSIVE_LOCK: { + CHECK_INTERFACE(IProCameraUser, data, reply); + reply->writeInt32(exclusiveLock()); + return NO_ERROR; + } break; + case EXCLUSIVE_UNLOCK: { + CHECK_INTERFACE(IProCameraUser, data, reply); + reply->writeInt32(exclusiveUnlock()); + return NO_ERROR; + } break; + case HAS_EXCLUSIVE_LOCK: { + CHECK_INTERFACE(IProCameraUser, data, reply); + reply->writeInt32(hasExclusiveLock()); + return NO_ERROR; + } break; + case SUBMIT_REQUEST: { + CHECK_INTERFACE(IProCameraUser, data, reply); + camera_metadata_t* metadata; + readMetadata(data, /*out*/&metadata); + + // arg2 = streaming (bool) + bool streaming = data.readInt32(); + + // return code: requestId (int32) + reply->writeInt32(submitRequest(metadata, streaming)); + + return NO_ERROR; + } break; + case CANCEL_REQUEST: { + CHECK_INTERFACE(IProCameraUser, data, reply); + int requestId = data.readInt32(); + reply->writeInt32(cancelRequest(requestId)); + return NO_ERROR; + } break; + case DELETE_STREAM: { + CHECK_INTERFACE(IProCameraUser, data, reply); + int streamId = data.readInt32(); + reply->writeInt32(deleteStream(streamId)); + return NO_ERROR; + } break; + case CREATE_STREAM: { + CHECK_INTERFACE(IProCameraUser, data, reply); + int width, height, format; + + width = data.readInt32(); + height = data.readInt32(); + format = data.readInt32(); + + sp<IGraphicBufferProducer> bp = + interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); + + int streamId = -1; + status_t ret; + ret = createStream(width, height, format, bp, &streamId); + + reply->writeInt32(streamId); + reply->writeInt32(ret); + + return NO_ERROR; + } break; + + case CREATE_DEFAULT_REQUEST: { + CHECK_INTERFACE(IProCameraUser, data, reply); + + int templateId = data.readInt32(); + + camera_metadata_t* request = NULL; + status_t ret; + ret = createDefaultRequest(templateId, &request); + + writeMetadata(*reply, request); + reply->writeInt32(ret); + + free_camera_metadata(request); + + return NO_ERROR; + } break; + case GET_CAMERA_INFO: { + CHECK_INTERFACE(IProCameraUser, data, reply); + + int cameraId = data.readInt32(); + + camera_metadata_t* info = NULL; + status_t ret; + ret = getCameraInfo(cameraId, &info); + + writeMetadata(*reply, info); + reply->writeInt32(ret); + + free_camera_metadata(info); + + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp new file mode 100644 index 0000000..fec5461 --- /dev/null +++ b/camera/ProCamera.cpp @@ -0,0 +1,431 @@ +/* +** +** Copyright (C) 2013, 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ProCamera" +#include <utils/Log.h> +#include <utils/threads.h> +#include <utils/Mutex.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> + +#include <camera/ProCamera.h> +#include <camera/ICameraService.h> +#include <camera/IProCameraUser.h> +#include <camera/IProCameraCallbacks.h> + +#include <gui/IGraphicBufferProducer.h> + +#include <system/camera_metadata.h> + +namespace android { + +sp<ProCamera> ProCamera::connect(int cameraId) +{ + return CameraBaseT::connect(cameraId, String16(), + ICameraService::USE_CALLING_UID); +} + +ProCamera::ProCamera(int cameraId) + : CameraBase(cameraId) +{ +} + +ProCamera::~ProCamera() +{ + +} + +/* IProCameraUser's implementation */ + +// callback from camera service +void ProCamera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) +{ + return CameraBaseT::notifyCallback(msgType, ext1, ext2); +} + +void ProCamera::onLockStatusChanged( + IProCameraCallbacks::LockStatus newLockStatus) +{ + ALOGV("%s: newLockStatus = %d", __FUNCTION__, newLockStatus); + + sp<ProCameraListener> listener; + { + Mutex::Autolock _l(mLock); + listener = mListener; + } + if (listener != NULL) { + switch (newLockStatus) { + case IProCameraCallbacks::LOCK_ACQUIRED: + listener->onLockAcquired(); + break; + case IProCameraCallbacks::LOCK_RELEASED: + listener->onLockReleased(); + break; + case IProCameraCallbacks::LOCK_STOLEN: + listener->onLockStolen(); + break; + default: + ALOGE("%s: Unknown lock status: %d", + __FUNCTION__, newLockStatus); + } + } +} + +void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) { + ALOGV("%s: frameId = %d, result = %p", __FUNCTION__, frameId, result); + + sp<ProCameraListener> listener; + { + Mutex::Autolock _l(mLock); + listener = mListener; + } + + CameraMetadata tmp(result); + + // Unblock waitForFrame(id) callers + { + Mutex::Autolock al(mWaitMutex); + mMetadataReady = true; + mLatestMetadata = tmp; // make copy + mWaitCondition.broadcast(); + } + + result = tmp.release(); + + if (listener != NULL) { + listener->onResultReceived(frameId, result); + } else { + free_camera_metadata(result); + } + +} + +status_t ProCamera::exclusiveTryLock() +{ + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + return c->exclusiveTryLock(); +} +status_t ProCamera::exclusiveLock() +{ + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + return c->exclusiveLock(); +} +status_t ProCamera::exclusiveUnlock() +{ + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + return c->exclusiveUnlock(); +} +bool ProCamera::hasExclusiveLock() +{ + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + return c->hasExclusiveLock(); +} + +// Note that the callee gets a copy of the metadata. +int ProCamera::submitRequest(const struct camera_metadata* metadata, + bool streaming) +{ + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + return c->submitRequest(const_cast<struct camera_metadata*>(metadata), + streaming); +} + +status_t ProCamera::cancelRequest(int requestId) +{ + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + return c->cancelRequest(requestId); +} + +status_t ProCamera::deleteStream(int streamId) +{ + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + status_t s = c->deleteStream(streamId); + + mStreams.removeItem(streamId); + + return s; +} + +status_t ProCamera::createStream(int width, int height, int format, + const sp<Surface>& surface, + /*out*/ + int* streamId) +{ + *streamId = -1; + + ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height, + format); + + if (surface == 0) { + return BAD_VALUE; + } + + return createStream(width, height, format, + surface->getIGraphicBufferProducer(), + streamId); +} + +status_t ProCamera::createStream(int width, int height, int format, + const sp<IGraphicBufferProducer>& bufferProducer, + /*out*/ + int* streamId) { + *streamId = -1; + + ALOGV("%s: createStreamT %dx%d (fmt=0x%x)", __FUNCTION__, width, height, + format); + + if (bufferProducer == 0) { + return BAD_VALUE; + } + + sp <IProCameraUser> c = mCamera; + status_t stat = c->createStream(width, height, format, bufferProducer, + streamId); + + if (stat == OK) { + StreamInfo s(*streamId); + + mStreams.add(*streamId, s); + } + + return stat; +} + +status_t ProCamera::createStreamCpu(int width, int height, int format, + int heapCount, + /*out*/ + sp<CpuConsumer>* cpuConsumer, + int* streamId) { + return createStreamCpu(width, height, format, heapCount, + /*synchronousMode*/true, + cpuConsumer, streamId); +} + +status_t ProCamera::createStreamCpu(int width, int height, int format, + int heapCount, + bool synchronousMode, + /*out*/ + sp<CpuConsumer>* cpuConsumer, + int* streamId) +{ + ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height, + format); + + *cpuConsumer = NULL; + + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + sp<CpuConsumer> cc = new CpuConsumer(heapCount, synchronousMode); + cc->setName(String8("ProCamera::mCpuConsumer")); + + sp<Surface> stc = new Surface( + cc->getProducerInterface()); + + status_t s = createStream(width, height, format, + stc->getIGraphicBufferProducer(), + streamId); + + if (s != OK) { + ALOGE("%s: Failure to create stream %dx%d (fmt=0x%x)", __FUNCTION__, + width, height, format); + return s; + } + + sp<ProFrameListener> frameAvailableListener = + new ProFrameListener(this, *streamId); + + getStreamInfo(*streamId).cpuStream = true; + getStreamInfo(*streamId).cpuConsumer = cc; + getStreamInfo(*streamId).synchronousMode = synchronousMode; + getStreamInfo(*streamId).stc = stc; + // for lifetime management + getStreamInfo(*streamId).frameAvailableListener = frameAvailableListener; + + cc->setFrameAvailableListener(frameAvailableListener); + + *cpuConsumer = cc; + + return s; +} + +camera_metadata* ProCamera::getCameraInfo(int cameraId) { + ALOGV("%s: cameraId = %d", __FUNCTION__, cameraId); + + sp <IProCameraUser> c = mCamera; + if (c == 0) return NULL; + + camera_metadata* ptr = NULL; + status_t status = c->getCameraInfo(cameraId, &ptr); + + if (status != OK) { + ALOGE("%s: Failed to get camera info, error = %d", __FUNCTION__, status); + } + + return ptr; +} + +status_t ProCamera::createDefaultRequest(int templateId, + camera_metadata** request) const { + ALOGV("%s: templateId = %d", __FUNCTION__, templateId); + + sp <IProCameraUser> c = mCamera; + if (c == 0) return NO_INIT; + + return c->createDefaultRequest(templateId, request); +} + +void ProCamera::onFrameAvailable(int streamId) { + ALOGV("%s: streamId = %d", __FUNCTION__, streamId); + + sp<ProCameraListener> listener = mListener; + StreamInfo& stream = getStreamInfo(streamId); + + if (listener.get() != NULL) { + listener->onFrameAvailable(streamId, stream.cpuConsumer); + } + + // Unblock waitForFrame(id) callers + { + Mutex::Autolock al(mWaitMutex); + getStreamInfo(streamId).frameReady++; + mWaitCondition.broadcast(); + } +} + +int ProCamera::waitForFrameBuffer(int streamId) { + status_t stat = BAD_VALUE; + Mutex::Autolock al(mWaitMutex); + + StreamInfo& si = getStreamInfo(streamId); + + if (si.frameReady > 0) { + int numFrames = si.frameReady; + si.frameReady = 0; + return numFrames; + } else { + while (true) { + stat = mWaitCondition.waitRelative(mWaitMutex, + mWaitTimeout); + if (stat != OK) { + ALOGE("%s: Error while waiting for frame buffer: %d", + __FUNCTION__, stat); + return stat; + } + + if (si.frameReady > 0) { + int numFrames = si.frameReady; + si.frameReady = 0; + return numFrames; + } + // else it was some other stream that got unblocked + } + } + + return stat; +} + +int ProCamera::dropFrameBuffer(int streamId, int count) { + StreamInfo& si = getStreamInfo(streamId); + + if (!si.cpuStream) { + return BAD_VALUE; + } else if (count < 0) { + return BAD_VALUE; + } + + if (!si.synchronousMode) { + ALOGW("%s: No need to drop frames on asynchronous streams," + " as asynchronous mode only keeps 1 latest frame around.", + __FUNCTION__); + return BAD_VALUE; + } + + int numDropped = 0; + for (int i = 0; i < count; ++i) { + CpuConsumer::LockedBuffer buffer; + if (si.cpuConsumer->lockNextBuffer(&buffer) != OK) { + break; + } + + si.cpuConsumer->unlockBuffer(buffer); + numDropped++; + } + + return numDropped; +} + +status_t ProCamera::waitForFrameMetadata() { + status_t stat = BAD_VALUE; + Mutex::Autolock al(mWaitMutex); + + if (mMetadataReady) { + return OK; + } else { + while (true) { + stat = mWaitCondition.waitRelative(mWaitMutex, + mWaitTimeout); + + if (stat != OK) { + ALOGE("%s: Error while waiting for metadata: %d", + __FUNCTION__, stat); + return stat; + } + + if (mMetadataReady) { + mMetadataReady = false; + return OK; + } + // else it was some other stream or metadata + } + } + + return stat; +} + +CameraMetadata ProCamera::consumeFrameMetadata() { + Mutex::Autolock al(mWaitMutex); + + // Destructive: Subsequent calls return empty metadatas + CameraMetadata tmp = mLatestMetadata; + mLatestMetadata.clear(); + + return tmp; +} + +ProCamera::StreamInfo& ProCamera::getStreamInfo(int streamId) { + return mStreams.editValueFor(streamId); +} + +}; // namespace android diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk new file mode 100644 index 0000000..ec13911 --- /dev/null +++ b/camera/tests/Android.mk @@ -0,0 +1,38 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main.cpp \ + ProCameraTests.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libcutils \ + libstlport \ + libcamera_metadata \ + libcamera_client \ + libgui \ + libsync \ + libui \ + libdl \ + libbinder + +LOCAL_STATIC_LIBRARIES := \ + libgtest + +LOCAL_C_INCLUDES += \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + system/media/camera/include \ + frameworks/av/services/camera/libcameraservice \ + frameworks/av/include/camera \ + frameworks/native/include \ + +LOCAL_CFLAGS += -Wall -Wextra + +LOCAL_MODULE:= camera_client_test +LOCAL_MODULE_TAGS := tests + +include $(BUILD_NATIVE_TEST) diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp new file mode 100644 index 0000000..f203949 --- /dev/null +++ b/camera/tests/ProCameraTests.cpp @@ -0,0 +1,1281 @@ +/* + * Copyright (C) 2013 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. + */ + +#include <gtest/gtest.h> +#include <iostream> + +#include <binder/IPCThreadState.h> +#include <utils/Thread.h> + +#include "Camera.h" +#include "ProCamera.h" +#include <utils/Vector.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> + +#include <gui/SurfaceComposerClient.h> +#include <gui/Surface.h> + +#include <system/camera_metadata.h> +#include <hardware/camera2.h> // for CAMERA2_TEMPLATE_PREVIEW only +#include <camera/CameraMetadata.h> + +#include <camera/ICameraServiceListener.h> + +namespace android { +namespace camera2 { +namespace tests { +namespace client { + +#define CAMERA_ID 0 +#define TEST_DEBUGGING 0 + +#define TEST_LISTENER_TIMEOUT 1000000000 // 1 second listener timeout +#define TEST_FORMAT HAL_PIXEL_FORMAT_Y16 //TODO: YUY2 instead + +#define TEST_FORMAT_MAIN HAL_PIXEL_FORMAT_Y8 +#define TEST_FORMAT_DEPTH HAL_PIXEL_FORMAT_Y16 + +// defaults for display "test" +#define TEST_DISPLAY_FORMAT HAL_PIXEL_FORMAT_Y8 +#define TEST_DISPLAY_WIDTH 320 +#define TEST_DISPLAY_HEIGHT 240 + +#define TEST_CPU_FRAME_COUNT 2 +#define TEST_CPU_HEAP_COUNT 5 + +#define TEST_FRAME_PROCESSING_DELAY_US 200000 // 200 ms + +#if TEST_DEBUGGING +#define dout std::cerr +#else +#define dout if (0) std::cerr +#endif + +#define EXPECT_OK(x) EXPECT_EQ(OK, (x)) +#define ASSERT_OK(x) ASSERT_EQ(OK, (x)) + +class ProCameraTest; + +struct ServiceListener : public BnCameraServiceListener { + + ServiceListener() : + mLatestStatus(STATUS_UNKNOWN), + mPrevStatus(STATUS_UNKNOWN) + { + } + + void onStatusChanged(Status status, int32_t cameraId) { + dout << "On status changed: 0x" << std::hex + << (unsigned int) status << " cameraId " << cameraId + << std::endl; + + Mutex::Autolock al(mMutex); + + mLatestStatus = status; + mCondition.broadcast(); + } + + status_t waitForStatusChange(Status& newStatus) { + Mutex::Autolock al(mMutex); + + if (mLatestStatus != mPrevStatus) { + newStatus = mLatestStatus; + mPrevStatus = mLatestStatus; + return OK; + } + + status_t stat = mCondition.waitRelative(mMutex, + TEST_LISTENER_TIMEOUT); + + if (stat == OK) { + newStatus = mLatestStatus; + mPrevStatus = mLatestStatus; + } + + return stat; + } + + Condition mCondition; + Mutex mMutex; + + Status mLatestStatus; + Status mPrevStatus; +}; + +enum ProEvent { + UNKNOWN, + ACQUIRED, + RELEASED, + STOLEN, + FRAME_RECEIVED, + RESULT_RECEIVED, +}; + +inline int ProEvent_Mask(ProEvent e) { + return (1 << static_cast<int>(e)); +} + +typedef Vector<ProEvent> EventList; + +class ProCameraTestThread : public Thread +{ +public: + ProCameraTestThread() { + } + + virtual bool threadLoop() { + mProc = ProcessState::self(); + mProc->startThreadPool(); + + IPCThreadState *ptr = IPCThreadState::self(); + + ptr->joinThreadPool(); + + return false; + } + + sp<ProcessState> mProc; +}; + +class ProCameraTestListener : public ProCameraListener { + +public: + static const int EVENT_MASK_ALL = 0xFFFFFFFF; + + ProCameraTestListener() { + mEventMask = EVENT_MASK_ALL; + mDropFrames = false; + } + + status_t WaitForEvent() { + Mutex::Autolock cal(mConditionMutex); + + { + Mutex::Autolock al(mListenerMutex); + + if (mProEventList.size() > 0) { + return OK; + } + } + + return mListenerCondition.waitRelative(mConditionMutex, + TEST_LISTENER_TIMEOUT); + } + + /* Read events into out. Existing queue is flushed */ + void ReadEvents(EventList& out) { + Mutex::Autolock al(mListenerMutex); + + for (size_t i = 0; i < mProEventList.size(); ++i) { + out.push(mProEventList[i]); + } + + mProEventList.clear(); + } + + /** + * Dequeue 1 event from the event queue. + * Returns UNKNOWN if queue is empty + */ + ProEvent ReadEvent() { + Mutex::Autolock al(mListenerMutex); + + if (mProEventList.size() == 0) { + return UNKNOWN; + } + + ProEvent ev = mProEventList[0]; + mProEventList.removeAt(0); + + return ev; + } + + void SetEventMask(int eventMask) { + Mutex::Autolock al(mListenerMutex); + mEventMask = eventMask; + } + + // Automatically acquire/release frames as they are available + void SetDropFrames(bool dropFrames) { + Mutex::Autolock al(mListenerMutex); + mDropFrames = dropFrames; + } + +private: + void QueueEvent(ProEvent ev) { + bool eventAdded = false; + { + Mutex::Autolock al(mListenerMutex); + + // Drop events not part of mask + if (ProEvent_Mask(ev) & mEventMask) { + mProEventList.push(ev); + eventAdded = true; + } + } + + if (eventAdded) { + mListenerCondition.broadcast(); + } + } + +protected: + + ////////////////////////////////////////////////// + ///////// ProCameraListener ////////////////////// + ////////////////////////////////////////////////// + + + // Lock has been acquired. Write operations now available. + virtual void onLockAcquired() { + QueueEvent(ACQUIRED); + } + // Lock has been released with exclusiveUnlock + virtual void onLockReleased() { + QueueEvent(RELEASED); + } + + // Lock has been stolen by another client. + virtual void onLockStolen() { + QueueEvent(STOLEN); + } + + // Lock free. + virtual void onTriggerNotify(int32_t ext1, int32_t ext2, int32_t ext3) { + + dout << "Trigger notify: " << ext1 << " " << ext2 + << " " << ext3 << std::endl; + } + + virtual void onFrameAvailable(int streamId, + const sp<CpuConsumer>& consumer) { + + QueueEvent(FRAME_RECEIVED); + + Mutex::Autolock al(mListenerMutex); + if (mDropFrames) { + CpuConsumer::LockedBuffer buf; + status_t ret; + + EXPECT_OK(ret); + if (OK == (ret = consumer->lockNextBuffer(&buf))) { + + dout << "Frame received on streamId = " << streamId << + ", dataPtr = " << (void*)buf.data << + ", timestamp = " << buf.timestamp << std::endl; + + EXPECT_OK(consumer->unlockBuffer(buf)); + } + } else { + dout << "Frame received on streamId = " << streamId << std::endl; + } + } + + virtual void onResultReceived(int32_t frameId, + camera_metadata* request) { + dout << "Result received frameId = " << frameId + << ", requestPtr = " << (void*)request << std::endl; + QueueEvent(RESULT_RECEIVED); + free_camera_metadata(request); + } + + virtual void notify(int32_t msg, int32_t ext1, int32_t ext2) { + dout << "Notify received: msg " << std::hex << msg + << ", ext1: " << std::hex << ext1 << ", ext2: " << std::hex << ext2 + << std::endl; + } + + Vector<ProEvent> mProEventList; + Mutex mListenerMutex; + Mutex mConditionMutex; + Condition mListenerCondition; + int mEventMask; + bool mDropFrames; +}; + +class ProCameraTest : public ::testing::Test { + +public: + ProCameraTest() { + char* displaySecsEnv = getenv("TEST_DISPLAY_SECS"); + if (displaySecsEnv != NULL) { + mDisplaySecs = atoi(displaySecsEnv); + if (mDisplaySecs < 0) { + mDisplaySecs = 0; + } + } else { + mDisplaySecs = 0; + } + + char* displayFmtEnv = getenv("TEST_DISPLAY_FORMAT"); + if (displayFmtEnv != NULL) { + mDisplayFmt = FormatFromString(displayFmtEnv); + } else { + mDisplayFmt = TEST_DISPLAY_FORMAT; + } + + char* displayWidthEnv = getenv("TEST_DISPLAY_WIDTH"); + if (displayWidthEnv != NULL) { + mDisplayW = atoi(displayWidthEnv); + if (mDisplayW < 0) { + mDisplayW = 0; + } + } else { + mDisplayW = TEST_DISPLAY_WIDTH; + } + + char* displayHeightEnv = getenv("TEST_DISPLAY_HEIGHT"); + if (displayHeightEnv != NULL) { + mDisplayH = atoi(displayHeightEnv); + if (mDisplayH < 0) { + mDisplayH = 0; + } + } else { + mDisplayH = TEST_DISPLAY_HEIGHT; + } + } + + static void SetUpTestCase() { + // Binder Thread Pool Initialization + mTestThread = new ProCameraTestThread(); + mTestThread->run("ProCameraTestThread"); + } + + virtual void SetUp() { + mCamera = ProCamera::connect(CAMERA_ID); + ASSERT_NE((void*)NULL, mCamera.get()); + + mListener = new ProCameraTestListener(); + mCamera->setListener(mListener); + } + + virtual void TearDown() { + ASSERT_NE((void*)NULL, mCamera.get()); + mCamera->disconnect(); + } + +protected: + sp<ProCamera> mCamera; + sp<ProCameraTestListener> mListener; + + static sp<Thread> mTestThread; + + int mDisplaySecs; + int mDisplayFmt; + int mDisplayW; + int mDisplayH; + + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; + + sp<SurfaceComposerClient> mDepthComposerClient; + sp<SurfaceControl> mDepthSurfaceControl; + + int getSurfaceWidth() { + return 512; + } + int getSurfaceHeight() { + return 512; + } + + void createOnScreenSurface(sp<Surface>& surface) { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface( + String8("ProCameraTest StreamingImage Surface"), + getSurfaceWidth(), getSurfaceHeight(), + PIXEL_FORMAT_RGB_888, 0); + + mSurfaceControl->setPosition(0, 0); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + SurfaceComposerClient::closeGlobalTransaction(); + + sp<ANativeWindow> window = mSurfaceControl->getSurface(); + surface = mSurfaceControl->getSurface(); + + ASSERT_NE((void*)NULL, surface.get()); + } + + void createDepthOnScreenSurface(sp<Surface>& surface) { + mDepthComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mDepthComposerClient->initCheck()); + + mDepthSurfaceControl = mDepthComposerClient->createSurface( + String8("ProCameraTest StreamingImage Surface"), + getSurfaceWidth(), getSurfaceHeight(), + PIXEL_FORMAT_RGB_888, 0); + + mDepthSurfaceControl->setPosition(640, 0); + + ASSERT_TRUE(mDepthSurfaceControl != NULL); + ASSERT_TRUE(mDepthSurfaceControl->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->setLayer(0x7FFFFFFF)); + ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->show()); + SurfaceComposerClient::closeGlobalTransaction(); + + sp<ANativeWindow> window = mDepthSurfaceControl->getSurface(); + surface = mDepthSurfaceControl->getSurface(); + + ASSERT_NE((void*)NULL, surface.get()); + } + + template <typename T> + static bool ExistsItem(T needle, T* array, size_t count) { + if (!array) { + return false; + } + + for (size_t i = 0; i < count; ++i) { + if (array[i] == needle) { + return true; + } + } + return false; + } + + + static int FormatFromString(const char* str) { + std::string s(str); + +#define CMP_STR(x, y) \ + if (s == #x) return HAL_PIXEL_FORMAT_ ## y; +#define CMP_STR_SAME(x) CMP_STR(x, x) + + CMP_STR_SAME( Y16); + CMP_STR_SAME( Y8); + CMP_STR_SAME( YV12); + CMP_STR(NV16, YCbCr_422_SP); + CMP_STR(NV21, YCrCb_420_SP); + CMP_STR(YUY2, YCbCr_422_I); + CMP_STR(RAW, RAW_SENSOR); + CMP_STR(RGBA, RGBA_8888); + + std::cerr << "Unknown format string " << str << std::endl; + return -1; + + } + + /** + * Creating a streaming request for these output streams from a template, + * and submit it + */ + void createSubmitRequestForStreams(uint8_t* streamIds, size_t count, int requestCount=-1) { + + ASSERT_NE((void*)NULL, streamIds); + ASSERT_LT(0u, count); + + camera_metadata_t *requestTmp = NULL; + EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + /*out*/&requestTmp)); + ASSERT_NE((void*)NULL, requestTmp); + CameraMetadata request(requestTmp); + + // set the output streams. default is empty + + uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS); + request.update(tag, streamIds, count); + + requestTmp = request.release(); + + if (requestCount < 0) { + EXPECT_OK(mCamera->submitRequest(requestTmp, /*streaming*/true)); + } else { + for (int i = 0; i < requestCount; ++i) { + EXPECT_OK(mCamera->submitRequest(requestTmp, + /*streaming*/false)); + } + } + request.acquire(requestTmp); + } +}; + +sp<Thread> ProCameraTest::mTestThread; + +TEST_F(ProCameraTest, AvailableFormats) { + if (HasFatalFailure()) { + return; + } + + CameraMetadata staticInfo = mCamera->getCameraInfo(CAMERA_ID); + ASSERT_FALSE(staticInfo.isEmpty()); + + uint32_t tag = static_cast<uint32_t>(ANDROID_SCALER_AVAILABLE_FORMATS); + EXPECT_TRUE(staticInfo.exists(tag)); + camera_metadata_entry_t entry = staticInfo.find(tag); + + EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YV12, + entry.data.i32, entry.count)); + EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YCrCb_420_SP, + entry.data.i32, entry.count)); +} + +// test around exclusiveTryLock (immediate locking) +TEST_F(ProCameraTest, LockingImmediate) { + + if (HasFatalFailure()) { + return; + } + + mListener->SetEventMask(ProEvent_Mask(ACQUIRED) | + ProEvent_Mask(STOLEN) | + ProEvent_Mask(RELEASED)); + + EXPECT_FALSE(mCamera->hasExclusiveLock()); + EXPECT_EQ(OK, mCamera->exclusiveTryLock()); + // at this point we definitely have the lock + + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(ACQUIRED, mListener->ReadEvent()); + + EXPECT_TRUE(mCamera->hasExclusiveLock()); + EXPECT_EQ(OK, mCamera->exclusiveUnlock()); + + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(RELEASED, mListener->ReadEvent()); + + EXPECT_FALSE(mCamera->hasExclusiveLock()); +} + +// test around exclusiveLock (locking at some future point in time) +TEST_F(ProCameraTest, LockingAsynchronous) { + + if (HasFatalFailure()) { + return; + } + + + mListener->SetEventMask(ProEvent_Mask(ACQUIRED) | + ProEvent_Mask(STOLEN) | + ProEvent_Mask(RELEASED)); + + // TODO: Add another procamera that has a lock here. + // then we can be test that the lock wont immediately be acquired + + EXPECT_FALSE(mCamera->hasExclusiveLock()); + EXPECT_EQ(OK, mCamera->exclusiveTryLock()); + // at this point we definitely have the lock + + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(ACQUIRED, mListener->ReadEvent()); + + EXPECT_TRUE(mCamera->hasExclusiveLock()); + EXPECT_EQ(OK, mCamera->exclusiveUnlock()); + + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(RELEASED, mListener->ReadEvent()); + + EXPECT_FALSE(mCamera->hasExclusiveLock()); +} + +// Stream directly to the screen. +TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) { + if (HasFatalFailure()) { + return; + } + + sp<Surface> surface; + if (mDisplaySecs > 0) { + createOnScreenSurface(/*out*/surface); + } + else { + dout << "Skipping, will not render to screen" << std::endl; + return; + } + + int depthStreamId = -1; + + sp<ServiceListener> listener = new ServiceListener(); + EXPECT_OK(ProCamera::addServiceListener(listener)); + + ServiceListener::Status currentStatus; + + // when subscribing a new listener, + // we immediately get a callback to the current status + while (listener->waitForStatusChange(/*out*/currentStatus) != OK); + EXPECT_EQ(ServiceListener::STATUS_PRESENT, currentStatus); + + dout << "Will now stream and resume infinitely..." << std::endl; + while (true) { + + if (currentStatus == ServiceListener::STATUS_PRESENT) { + + ASSERT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt, + surface, + &depthStreamId)); + EXPECT_NE(-1, depthStreamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + + uint8_t streams[] = { depthStreamId }; + ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams( + streams, + /*count*/1)); + } + + ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN; + + // TODO: maybe check for getch every once in a while? + while (listener->waitForStatusChange(/*out*/stat) != OK); + + if (currentStatus != stat) { + if (stat == ServiceListener::STATUS_PRESENT) { + dout << "Reconnecting to camera" << std::endl; + mCamera = ProCamera::connect(CAMERA_ID); + } else if (stat == ServiceListener::STATUS_NOT_AVAILABLE) { + dout << "Disconnecting from camera" << std::endl; + mCamera->disconnect(); + } else if (stat == ServiceListener::STATUS_NOT_PRESENT) { + dout << "Camera unplugged" << std::endl; + mCamera = NULL; + } else { + dout << "Unknown status change " + << std::hex << stat << std::endl; + } + + currentStatus = stat; + } + } + + EXPECT_OK(ProCamera::removeServiceListener(listener)); + EXPECT_OK(mCamera->deleteStream(depthStreamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +// Stream directly to the screen. +TEST_F(ProCameraTest, DISABLED_StreamingImageDual) { + if (HasFatalFailure()) { + return; + } + sp<Surface> surface; + sp<Surface> depthSurface; + if (mDisplaySecs > 0) { + createOnScreenSurface(/*out*/surface); + createDepthOnScreenSurface(/*out*/depthSurface); + } + + int streamId = -1; + EXPECT_OK(mCamera->createStream(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, surface, &streamId)); + EXPECT_NE(-1, streamId); + + int depthStreamId = -1; + EXPECT_OK(mCamera->createStream(/*width*/320, /*height*/240, + TEST_FORMAT_DEPTH, depthSurface, &depthStreamId)); + EXPECT_NE(-1, depthStreamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + /* + */ + /* iterate in a loop submitting requests every frame. + * what kind of requests doesnt really matter, just whatever. + */ + + // it would probably be better to use CameraMetadata from camera service. + camera_metadata_t *request = NULL; + EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + /*out*/&request)); + EXPECT_NE((void*)NULL, request); + + /*FIXME: dont need this later, at which point the above should become an + ASSERT_NE*/ + if(request == NULL) request = allocate_camera_metadata(10, 100); + + // set the output streams to just this stream ID + + // wow what a verbose API. + uint8_t allStreams[] = { streamId, depthStreamId }; + // IMPORTANT. bad things will happen if its not a uint8. + size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]); + camera_metadata_entry_t entry; + uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS); + int find = find_camera_metadata_entry(request, tag, &entry); + if (find == -ENOENT) { + if (add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/streamCount) != OK) { + camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000); + ASSERT_OK(append_camera_metadata(tmp, request)); + free_camera_metadata(request); + request = tmp; + + ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/streamCount)); + } + } else { + ASSERT_OK(update_camera_metadata_entry(request, entry.index, + &allStreams, /*data_count*/streamCount, &entry)); + } + + EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true)); + + dout << "will sleep now for " << mDisplaySecs << std::endl; + sleep(mDisplaySecs); + + free_camera_metadata(request); + + for (int i = 0; i < streamCount; ++i) { + EXPECT_OK(mCamera->deleteStream(allStreams[i])); + } + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +TEST_F(ProCameraTest, CpuConsumerSingle) { + if (HasFatalFailure()) { + return; + } + + mListener->SetEventMask(ProEvent_Mask(ACQUIRED) | + ProEvent_Mask(STOLEN) | + ProEvent_Mask(RELEASED) | + ProEvent_Mask(FRAME_RECEIVED)); + mListener->SetDropFrames(true); + + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240, + TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(ACQUIRED, mListener->ReadEvent()); + /* iterate in a loop submitting requests every frame. + * what kind of requests doesnt really matter, just whatever. + */ + + // it would probably be better to use CameraMetadata from camera service. + camera_metadata_t *request = NULL; + EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + /*out*/&request)); + EXPECT_NE((void*)NULL, request); + + /*FIXME: dont need this later, at which point the above should become an + ASSERT_NE*/ + if(request == NULL) request = allocate_camera_metadata(10, 100); + + // set the output streams to just this stream ID + + uint8_t allStreams[] = { streamId }; + camera_metadata_entry_t entry; + uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS); + int find = find_camera_metadata_entry(request, tag, &entry); + if (find == -ENOENT) { + if (add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/1) != OK) { + camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000); + ASSERT_OK(append_camera_metadata(tmp, request)); + free_camera_metadata(request); + request = tmp; + + ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/1)); + } + } else { + ASSERT_OK(update_camera_metadata_entry(request, entry.index, + &allStreams, /*data_count*/1, &entry)); + } + + EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true)); + + // Consume a couple of frames + for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) { + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent()); + } + + // Done: clean up + free_camera_metadata(request); + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +TEST_F(ProCameraTest, CpuConsumerDual) { + if (HasFatalFailure()) { + return; + } + + mListener->SetEventMask(ProEvent_Mask(FRAME_RECEIVED)); + mListener->SetDropFrames(true); + + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + int depthStreamId = -1; + EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240, + TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &depthStreamId)); + EXPECT_NE(-1, depthStreamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + /* + */ + /* iterate in a loop submitting requests every frame. + * what kind of requests doesnt really matter, just whatever. + */ + + // it would probably be better to use CameraMetadata from camera service. + camera_metadata_t *request = NULL; + EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + /*out*/&request)); + EXPECT_NE((void*)NULL, request); + + if(request == NULL) request = allocate_camera_metadata(10, 100); + + // set the output streams to just this stream ID + + // wow what a verbose API. + uint8_t allStreams[] = { streamId, depthStreamId }; + size_t streamCount = 2; + camera_metadata_entry_t entry; + uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS); + int find = find_camera_metadata_entry(request, tag, &entry); + if (find == -ENOENT) { + if (add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/streamCount) != OK) { + camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000); + ASSERT_OK(append_camera_metadata(tmp, request)); + free_camera_metadata(request); + request = tmp; + + ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/streamCount)); + } + } else { + ASSERT_OK(update_camera_metadata_entry(request, entry.index, + &allStreams, /*data_count*/streamCount, &entry)); + } + + EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true)); + + // Consume a couple of frames + for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) { + // stream id 1 + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent()); + + // stream id 2 + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent()); + + //TODO: events should be a struct with some data like the stream id + } + + // Done: clean up + free_camera_metadata(request); + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +TEST_F(ProCameraTest, ResultReceiver) { + if (HasFatalFailure()) { + return; + } + + mListener->SetEventMask(ProEvent_Mask(RESULT_RECEIVED)); + mListener->SetDropFrames(true); + //FIXME: if this is run right after the previous test we get FRAME_RECEIVED + // need to filter out events at read time + + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + /* + */ + /* iterate in a loop submitting requests every frame. + * what kind of requests doesnt really matter, just whatever. + */ + + camera_metadata_t *request = NULL; + EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + /*out*/&request)); + EXPECT_NE((void*)NULL, request); + + /*FIXME*/ + if(request == NULL) request = allocate_camera_metadata(10, 100); + + // set the output streams to just this stream ID + + uint8_t allStreams[] = { streamId }; + size_t streamCount = 1; + camera_metadata_entry_t entry; + uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS); + int find = find_camera_metadata_entry(request, tag, &entry); + if (find == -ENOENT) { + if (add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/streamCount) != OK) { + camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000); + ASSERT_OK(append_camera_metadata(tmp, request)); + free_camera_metadata(request); + request = tmp; + + ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/streamCount)); + } + } else { + ASSERT_OK(update_camera_metadata_entry(request, entry.index, + &allStreams, /*data_count*/streamCount, &entry)); + } + + EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true)); + + // Consume a couple of results + for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) { + EXPECT_EQ(OK, mListener->WaitForEvent()); + EXPECT_EQ(RESULT_RECEIVED, mListener->ReadEvent()); + } + + // Done: clean up + free_camera_metadata(request); + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +// FIXME: This is racy and sometimes fails on waitForFrameMetadata +TEST_F(ProCameraTest, DISABLED_WaitForResult) { + if (HasFatalFailure()) { + return; + } + + mListener->SetDropFrames(true); + + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + + uint8_t streams[] = { streamId }; + ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1)); + + // Consume a couple of results + for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) { + EXPECT_OK(mCamera->waitForFrameMetadata()); + CameraMetadata meta = mCamera->consumeFrameMetadata(); + EXPECT_FALSE(meta.isEmpty()); + } + + // Done: clean up + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +TEST_F(ProCameraTest, WaitForSingleStreamBuffer) { + if (HasFatalFailure()) { + return; + } + + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + + uint8_t streams[] = { streamId }; + ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1, + /*requests*/TEST_CPU_FRAME_COUNT)); + + // Consume a couple of results + for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) { + EXPECT_EQ(1, mCamera->waitForFrameBuffer(streamId)); + + CpuConsumer::LockedBuffer buf; + EXPECT_OK(consumer->lockNextBuffer(&buf)); + + dout << "Buffer synchronously received on streamId = " << streamId << + ", dataPtr = " << (void*)buf.data << + ", timestamp = " << buf.timestamp << std::endl; + + EXPECT_OK(consumer->unlockBuffer(buf)); + } + + // Done: clean up + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +// FIXME: This is racy and sometimes fails on waitForFrameMetadata +TEST_F(ProCameraTest, DISABLED_WaitForDualStreamBuffer) { + if (HasFatalFailure()) { + return; + } + + const int REQUEST_COUNT = TEST_CPU_FRAME_COUNT * 10; + + // 15 fps + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + // 30 fps + int depthStreamId = -1; + sp<CpuConsumer> depthConsumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240, + TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &depthConsumer, &depthStreamId)); + EXPECT_NE(-1, depthStreamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + + uint8_t streams[] = { streamId, depthStreamId }; + ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/2, + /*requests*/REQUEST_COUNT)); + + int depthFrames = 0; + int greyFrames = 0; + + // Consume two frames simultaneously. Unsynchronized by timestamps. + for (int i = 0; i < REQUEST_COUNT; ++i) { + + // Exhaust event queue so it doesn't keep growing + while (mListener->ReadEvent() != UNKNOWN); + + // Get the metadata + EXPECT_OK(mCamera->waitForFrameMetadata()); + CameraMetadata meta = mCamera->consumeFrameMetadata(); + EXPECT_FALSE(meta.isEmpty()); + + // Get the buffers + + EXPECT_EQ(1, mCamera->waitForFrameBuffer(depthStreamId)); + + /** + * Guaranteed to be able to consume the depth frame, + * since we waited on it. + */ + CpuConsumer::LockedBuffer depthBuffer; + EXPECT_OK(depthConsumer->lockNextBuffer(&depthBuffer)); + + dout << "Depth Buffer synchronously received on streamId = " << + streamId << + ", dataPtr = " << (void*)depthBuffer.data << + ", timestamp = " << depthBuffer.timestamp << std::endl; + + EXPECT_OK(depthConsumer->unlockBuffer(depthBuffer)); + + depthFrames++; + + + /** Consume Greyscale frames if there are any. + * There may not be since it runs at half FPS */ + CpuConsumer::LockedBuffer greyBuffer; + while (consumer->lockNextBuffer(&greyBuffer) == OK) { + + dout << "GRAY Buffer synchronously received on streamId = " << + streamId << + ", dataPtr = " << (void*)greyBuffer.data << + ", timestamp = " << greyBuffer.timestamp << std::endl; + + EXPECT_OK(consumer->unlockBuffer(greyBuffer)); + + greyFrames++; + } + } + + dout << "Done, summary: depth frames " << std::dec << depthFrames + << ", grey frames " << std::dec << greyFrames << std::endl; + + // Done: clean up + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesSync) { + if (HasFatalFailure()) { + return; + } + + const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT; + + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, + /*synchronousMode*/true, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + + uint8_t streams[] = { streamId }; + ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1, + /*requests*/NUM_REQUESTS)); + + // Consume a couple of results + for (int i = 0; i < NUM_REQUESTS; ++i) { + int numFrames; + EXPECT_TRUE((numFrames = mCamera->waitForFrameBuffer(streamId)) > 0); + + // Drop all but the newest framebuffer + EXPECT_EQ(numFrames-1, mCamera->dropFrameBuffer(streamId, numFrames-1)); + + dout << "Dropped " << (numFrames - 1) << " frames" << std::endl; + + // Skip the counter ahead, don't try to consume these frames again + i += numFrames-1; + + // "Consume" the buffer + CpuConsumer::LockedBuffer buf; + EXPECT_OK(consumer->lockNextBuffer(&buf)); + + dout << "Buffer synchronously received on streamId = " << streamId << + ", dataPtr = " << (void*)buf.data << + ", timestamp = " << buf.timestamp << std::endl; + + // Process at 10fps, stream is at 15fps. + // This means we will definitely fill up the buffer queue with + // extra buffers and need to drop them. + usleep(TEST_FRAME_PROCESSING_DELAY_US); + + EXPECT_OK(consumer->unlockBuffer(buf)); + } + + // Done: clean up + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesAsync) { + if (HasFatalFailure()) { + return; + } + + const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT; + const int CONSECUTIVE_FAILS_ASSUME_TIME_OUT = 5; + + int streamId = -1; + sp<CpuConsumer> consumer; + EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960, + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, + /*synchronousMode*/false, &consumer, &streamId)); + EXPECT_NE(-1, streamId); + + EXPECT_OK(mCamera->exclusiveTryLock()); + + uint8_t streams[] = { streamId }; + ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1, + /*requests*/NUM_REQUESTS)); + + uint64_t lastFrameNumber = 0; + int numFrames; + + // Consume a couple of results + int i; + for (i = 0; i < NUM_REQUESTS && lastFrameNumber < NUM_REQUESTS; ++i) { + EXPECT_LT(0, (numFrames = mCamera->waitForFrameBuffer(streamId))); + + dout << "Dropped " << (numFrames - 1) << " frames" << std::endl; + + // Skip the counter ahead, don't try to consume these frames again + i += numFrames-1; + + // "Consume" the buffer + CpuConsumer::LockedBuffer buf; + + EXPECT_EQ(OK, consumer->lockNextBuffer(&buf)); + + lastFrameNumber = buf.frameNumber; + + dout << "Buffer asynchronously received on streamId = " << streamId << + ", dataPtr = " << (void*)buf.data << + ", timestamp = " << buf.timestamp << + ", framenumber = " << buf.frameNumber << std::endl; + + // Process at 10fps, stream is at 15fps. + // This means we will definitely fill up the buffer queue with + // extra buffers and need to drop them. + usleep(TEST_FRAME_PROCESSING_DELAY_US); + + EXPECT_OK(consumer->unlockBuffer(buf)); + } + + dout << "Done after " << i << " iterations " << std::endl; + + // Done: clean up + EXPECT_OK(mCamera->deleteStream(streamId)); + EXPECT_OK(mCamera->exclusiveUnlock()); +} + + + +//TODO: refactor into separate file +TEST_F(ProCameraTest, ServiceListenersSubscribe) { + + ASSERT_EQ(4u, sizeof(ServiceListener::Status)); + + sp<ServiceListener> listener = new ServiceListener(); + + EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener)); + EXPECT_OK(ProCamera::addServiceListener(listener)); + + EXPECT_EQ(ALREADY_EXISTS, ProCamera::addServiceListener(listener)); + EXPECT_OK(ProCamera::removeServiceListener(listener)); + + EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener)); +} + +//TODO: refactor into separate file +TEST_F(ProCameraTest, ServiceListenersFunctional) { + + sp<ServiceListener> listener = new ServiceListener(); + + EXPECT_OK(ProCamera::addServiceListener(listener)); + + sp<Camera> cam = Camera::connect(CAMERA_ID, + /*clientPackageName*/String16(), + -1); + EXPECT_NE((void*)NULL, cam.get()); + + ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN; + EXPECT_OK(listener->waitForStatusChange(/*out*/stat)); + + EXPECT_EQ(ServiceListener::STATUS_NOT_AVAILABLE, stat); + + if (cam.get()) { + cam->disconnect(); + } + + EXPECT_OK(listener->waitForStatusChange(/*out*/stat)); + EXPECT_EQ(ServiceListener::STATUS_PRESENT, stat); + + EXPECT_OK(ProCamera::removeServiceListener(listener)); +} + + + +} +} +} +} + diff --git a/camera/tests/main.cpp b/camera/tests/main.cpp new file mode 100644 index 0000000..8c8c515 --- /dev/null +++ b/camera/tests/main.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 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. + */ + +#include <gtest/gtest.h> + + +int main(int argc, char **argv) { + + ::testing::InitGoogleTest(&argc, argv); + + int ret = RUN_ALL_TESTS(); + + return ret; +} |