Merge "Handles duplicated NAL start code to fix crash on HLS streams."
diff --git a/camera/Android.mk b/camera/Android.mk
index 7286f92..3e7e5a5 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -1,14 +1,21 @@
-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 \
ICameraRecordingProxy.cpp \
- ICameraRecordingProxyListener.cpp
+ ICameraRecordingProxyListener.cpp \
+ IProCameraUser.cpp \
+ IProCameraCallbacks.cpp \
+ ProCamera.cpp \
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -16,7 +23,11 @@
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 3aaacaf..be395ba 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -120,9 +120,10 @@
{
ALOGV("connect");
sp<Camera> c = new Camera();
+ sp<ICameraClient> cl = c;
const sp<ICameraService>& cs = getCameraService();
if (cs != 0) {
- c->mCamera = cs->connect(c, cameraId);
+ c->mCamera = cs->connect(cl, cameraId);
}
if (c->mCamera != 0) {
c->mCamera->asBinder()->linkToDeath(c);
diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.cpp b/camera/CameraMetadata.cpp
similarity index 96%
rename from services/camera/libcameraservice/camera2/CameraMetadata.cpp
rename to camera/CameraMetadata.cpp
index 835587d..fdd0610 100644
--- a/services/camera/libcameraservice/camera2/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -18,11 +18,10 @@
#include <utils/Log.h>
#include <utils/Errors.h>
-#include "CameraMetadata.h"
+#include <camera/CameraMetadata.h>
namespace android {
-namespace camera2 {
CameraMetadata::CameraMetadata() :
mBuffer(NULL) {
}
@@ -36,6 +35,10 @@
mBuffer = clone_camera_metadata(other.mBuffer);
}
+CameraMetadata::CameraMetadata(camera_metadata_t *buffer) : mBuffer(NULL) {
+ acquire(buffer);
+}
+
CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) {
return operator=(other.mBuffer);
}
@@ -205,6 +208,11 @@
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;
@@ -292,5 +300,4 @@
return OK;
}
-}; // namespace camera2
}; // namespace android
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index f2d367e..8237c66 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -65,6 +65,17 @@
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)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeStrongBinder(cameraCb->asBinder());
+ data.writeInt32(cameraId);
+ remote()->transact(BnCameraService::CONNECT_PRO, data, &reply);
+ return interface_cast<IProCameraUser>(reply.readStrongBinder());
+ }
};
IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
@@ -97,6 +108,13 @@
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;
+ case CONNECT_PRO: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<IProCameraCallbacks> cameraClient = interface_cast<IProCameraCallbacks>(data.readStrongBinder());
+ sp<IProCameraUser> camera = connect(cameraClient, data.readInt32());
+ reply->writeStrongBinder(camera->asBinder());
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/IProCameraCallbacks.cpp b/camera/IProCameraCallbacks.cpp
new file mode 100644
index 0000000..6cd36bf
--- /dev/null
+++ b/camera/IProCameraCallbacks.cpp
@@ -0,0 +1,188 @@
+/*
+**
+** 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,
+ DATA_CALLBACK,
+ DATA_CALLBACK_TIMESTAMP,
+ 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);
+ }
+
+ // generic data callback from camera service to app with image data
+ void dataCallback(int32_t msgType, const sp<IMemory>& imageData,
+ camera_frame_metadata_t *metadata)
+ {
+ ALOGV("dataCallback");
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+ data.writeInt32(msgType);
+ data.writeStrongBinder(imageData->asBinder());
+ if (metadata) {
+ data.writeInt32(metadata->number_of_faces);
+ data.write(metadata->faces,
+ sizeof(camera_face_t) * metadata->number_of_faces);
+ }
+ remote()->transact(DATA_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ // generic data callback from camera service to app with image data
+ void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+ const sp<IMemory>& imageData)
+ {
+ ALOGV("dataCallback");
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+ data.writeInt64(timestamp);
+ data.writeInt32(msgType);
+ data.writeStrongBinder(imageData->asBinder());
+ remote()->transact(DATA_CALLBACK_TIMESTAMP, 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 DATA_CALLBACK: {
+ ALOGV("DATA_CALLBACK");
+ CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+ int32_t msgType = data.readInt32();
+ sp<IMemory> imageData = interface_cast<IMemory>(
+ data.readStrongBinder());
+ camera_frame_metadata_t *metadata = NULL;
+ if (data.dataAvail() > 0) {
+ metadata = new camera_frame_metadata_t;
+ metadata->number_of_faces = data.readInt32();
+ metadata->faces = (camera_face_t *) data.readInplace(
+ sizeof(camera_face_t) * metadata->number_of_faces);
+ }
+ dataCallback(msgType, imageData, metadata);
+ if (metadata) delete metadata;
+ return NO_ERROR;
+ } break;
+ case DATA_CALLBACK_TIMESTAMP: {
+ ALOGV("DATA_CALLBACK_TIMESTAMP");
+ CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+ nsecs_t timestamp = data.readInt64();
+ int32_t msgType = data.readInt32();
+ sp<IMemory> imageData = interface_cast<IMemory>(
+ data.readStrongBinder());
+ dataCallbackTimestamp(timestamp, msgType, imageData);
+ 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..c9d98aa
--- /dev/null
+++ b/camera/IProCameraUser.cpp
@@ -0,0 +1,409 @@
+/*
+**
+** 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,
+ REQUEST_STREAM,
+ CANCEL_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
+ */
+void readMetadata(const Parcel& data, camera_metadata_t** out) {
+ camera_metadata_t* metadata;
+
+ // arg0 = metadataSize (int32)
+ size_t metadataSize = static_cast<size_t>(data.readInt32());
+
+ if (metadataSize == 0) {
+ if (out) {
+ *out = NULL;
+ }
+ 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)
+ {
+ data.readBlob(metadataSize, &blob);
+ const camera_metadata_t* tmp =
+ reinterpret_cast<const camera_metadata_t*>(blob.data());
+ size_t entry_capacity = get_camera_metadata_entry_capacity(tmp);
+ size_t data_capacity = get_camera_metadata_data_capacity(tmp);
+
+ metadata = allocate_camera_metadata(entry_capacity, data_capacity);
+ copy_camera_metadata(metadata, metadataSize, tmp);
+ }
+ blob.release();
+
+ if (out) {
+ *out = metadata;
+ } else {
+ 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)
+ size_t metadataSize;
+
+ if (metadata == NULL) {
+ data.writeInt32(0);
+ return;
+ }
+
+ 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);
+ }
+ 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 requestStream(int streamId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeInt32(streamId);
+
+ remote()->transact(REQUEST_STREAM, data, &reply);
+ return reply.readInt32();
+ }
+ virtual status_t cancelStream(int streamId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+ data.writeInt32(streamId);
+
+ remote()->transact(CANCEL_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 REQUEST_STREAM: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ int streamId = data.readInt32();
+ reply->writeInt32(requestStream(streamId));
+ return NO_ERROR;
+ } break;
+ case CANCEL_STREAM: {
+ CHECK_INTERFACE(IProCameraUser, data, reply);
+ int streamId = data.readInt32();
+ reply->writeInt32(cancelStream(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..7c66d62
--- /dev/null
+++ b/camera/ProCamera.cpp
@@ -0,0 +1,537 @@
+/*
+**
+** 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 <gui/Surface.h>
+
+#include <system/camera_metadata.h>
+
+namespace android {
+
+// client singleton for camera service binder interface
+Mutex ProCamera::mLock;
+sp<ICameraService> ProCamera::mCameraService;
+sp<ProCamera::DeathNotifier> ProCamera::mDeathNotifier;
+
+// establish binder interface to camera service
+const sp<ICameraService>& ProCamera::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;
+}
+
+sp<ProCamera> ProCamera::connect(int cameraId)
+{
+ ALOGV("connect");
+ sp<ProCamera> c = new ProCamera();
+ sp<IProCameraCallbacks> cl = c;
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs != 0) {
+ c->mCamera = cs->connect(cl, cameraId);
+ }
+ if (c->mCamera != 0) {
+ c->mCamera->asBinder()->linkToDeath(c);
+ c->mStatus = NO_ERROR;
+ } else {
+ c.clear();
+ }
+ return c;
+}
+
+void ProCamera::disconnect()
+{
+ ALOGV("%s: disconnect", __FUNCTION__);
+ if (mCamera != 0) {
+ mCamera->disconnect();
+ mCamera->asBinder()->unlinkToDeath(this);
+ mCamera = 0;
+ }
+ ALOGV("%s: disconnect (done)", __FUNCTION__);
+}
+
+ProCamera::ProCamera()
+{
+}
+
+ProCamera::~ProCamera()
+{
+
+}
+
+sp<IProCameraUser> ProCamera::remote()
+{
+ return mCamera;
+}
+
+void ProCamera::binderDied(const wp<IBinder>& who) {
+ ALOGW("IProCameraUser died");
+ notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, 0);
+}
+
+void ProCamera::DeathNotifier::binderDied(const wp<IBinder>& who) {
+ ALOGV("binderDied");
+ Mutex::Autolock _l(ProCamera::mLock);
+ ProCamera::mCameraService.clear();
+ ALOGW("Camera service died!");
+}
+
+void ProCamera::setListener(const sp<ProCameraListener>& listener)
+{
+ Mutex::Autolock _l(mLock);
+ mListener = listener;
+}
+
+
+// callback from camera service
+void ProCamera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
+{
+ sp<ProCameraListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+ if (listener != NULL) {
+ listener->notify(msgType, ext1, ext2);
+ }
+}
+
+// callback from camera service when frame or image is ready
+void ProCamera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
+ camera_frame_metadata_t *metadata)
+{
+ sp<ProCameraListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+ if (listener != NULL) {
+ listener->postData(msgType, dataPtr, metadata);
+ }
+}
+
+// callback from camera service when timestamped frame is ready
+void ProCamera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+ const sp<IMemory>& dataPtr)
+{
+ sp<ProCameraListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+ if (listener != NULL) {
+ listener->postDataTimestamp(timestamp, msgType, dataPtr);
+ } else {
+ ALOGW("No listener was set. Drop a recording frame.");
+ }
+}
+
+/* IProCameraUser's implementation */
+
+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;
+ 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->cancelStream(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)
+{
+ 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);
+ 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).stc = stc;
+ // for lifetime management
+ getStreamInfo(*streamId).frameAvailableListener = frameAvailableListener;
+
+ cc->setFrameAvailableListener(frameAvailableListener);
+
+ *cpuConsumer = cc;
+
+ return s;
+}
+
+int ProCamera::getNumberOfCameras() {
+ const sp<ICameraService> cs = getCameraService();
+
+ if (!cs.get()) {
+ return DEAD_OBJECT;
+ }
+ return cs->getNumberOfCameras();
+}
+
+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);
+
+ CpuConsumer::LockedBuffer buf;
+
+ if (listener.get() != NULL) {
+ if (listener->useOnFrameAvailable()) {
+ listener->onFrameAvailable(streamId, stream.cpuConsumer);
+ return;
+ }
+ }
+
+ // 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;
+ }
+
+ 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.release();
+
+ 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..e455943
--- /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_EXECUTABLE)
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
new file mode 100644
index 0000000..f93e5cd
--- /dev/null
+++ b/camera/tests/ProCameraTests.cpp
@@ -0,0 +1,1042 @@
+/*
+ * 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>
+
+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_Y16
+#define TEST_DISPLAY_WIDTH 1280
+#define TEST_DISPLAY_HEIGHT 960
+
+#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;
+
+enum ProEvent {
+ UNKNOWN,
+ ACQUIRED,
+ RELEASED,
+ STOLEN,
+ BUFFER_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;
+ }
+
+ 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;
+ }
+
+private:
+ void QueueEvent(ProEvent ev) {
+ bool eventAdded = false;
+ {
+ Mutex::Autolock al(mListenerMutex);
+
+ 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 onBufferReceived(int streamId,
+ const CpuConsumer::LockedBuffer& buf) {
+
+ dout << "Buffer received on streamId = " << streamId <<
+ ", dataPtr = " << (void*)buf.data <<
+ ", timestamp = " << buf.timestamp << std::endl;
+
+ QueueEvent(BUFFER_RECEIVED);
+
+ }
+ 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);
+ }
+
+ // TODO: remove
+
+ virtual void notify(int32_t , int32_t , int32_t ) {}
+ virtual void postData(int32_t , const sp<IMemory>& ,
+ camera_frame_metadata_t *) {}
+ virtual void postDataTimestamp(nsecs_t , int32_t , const sp<IMemory>& ) {}
+
+
+ Vector<ProEvent> mProEventList;
+ Mutex mListenerMutex;
+ Mutex mConditionMutex;
+ Condition mListenerCondition;
+ int mEventMask;
+};
+
+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;
+ EXPECT_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));
+
+ dout << "will sleep now for " << mDisplaySecs << std::endl;
+ sleep(mDisplaySecs);
+
+ 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(BUFFER_RECEIVED));
+
+ 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(BUFFER_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(BUFFER_RECEIVED));
+
+ 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(BUFFER_RECEIVED, mListener->ReadEvent());
+
+ // stream id 2
+ EXPECT_EQ(OK, mListener->WaitForEvent());
+ EXPECT_EQ(BUFFER_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));
+ //FIXME: if this is run right after the previous test we get BUFFER_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());
+}
+
+TEST_F(ProCameraTest, WaitForResult) {
+ 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));
+
+ // 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
+ consumer->abandon(); // since we didn't consume any of the buffers
+ 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());
+}
+
+TEST_F(ProCameraTest, 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));
+
+ // Consume two frames simultaneously. Unsynchronized by timestamps.
+ for (int i = 0; i < REQUEST_COUNT; ++i) {
+
+ // 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));
+
+
+ /** 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));
+ }
+ }
+
+ // Done: clean up
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFrames) {
+ 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, &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) {
+ // 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);
+
+ 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;
+
+ EXPECT_OK(consumer->unlockBuffer(buf));
+ }
+
+ // Done: clean up
+ EXPECT_OK(mCamera->deleteStream(streamId));
+ EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+
+
+}
+}
+}
+}
+
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;
+}
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 43dae1c..8b87de6 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -159,7 +159,7 @@
sp<Camera> mCamera;
};
-private:
+protected:
Camera();
Camera(const Camera&);
Camera& operator=(const Camera);
diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.h b/include/camera/CameraMetadata.h
similarity index 95%
rename from services/camera/libcameraservice/camera2/CameraMetadata.h
rename to include/camera/CameraMetadata.h
index aee6cd7..4289126 100644
--- a/services/camera/libcameraservice/camera2/CameraMetadata.h
+++ b/include/camera/CameraMetadata.h
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-#ifndef ANDROID_SERVERS_CAMERA_CAMERA2METADATA_CPP
-#define ANDROID_SERVERS_CAMERA_CAMERA2METADATA_CPP
+#ifndef ANDROID_CLIENT_CAMERA2_CAMERAMETADATA_CPP
+#define ANDROID_CLIENT_CAMERA2_CAMERAMETADATA_CPP
#include "system/camera_metadata.h"
#include <utils/String8.h>
#include <utils/Vector.h>
namespace android {
-namespace camera2 {
/**
* A convenience wrapper around the C-based camera_metadata_t library.
@@ -122,6 +121,12 @@
}
/**
+ * Check if a metadata entry exists for a given tag id
+ *
+ */
+ bool exists(uint32_t tag) const;
+
+ /**
* Get metadata entry by tag id
*/
camera_metadata_entry find(uint32_t tag);
@@ -167,7 +172,6 @@
};
-}; // namespace camera2
}; // namespace android
#endif
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index 7d70c1e..11d7b65 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -23,6 +23,7 @@
#include <camera/ICameraClient.h>
#include <camera/ICamera.h>
+#include <camera/IProCameraUser.h>
namespace android {
@@ -32,7 +33,8 @@
enum {
GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,
GET_CAMERA_INFO,
- CONNECT
+ CONNECT,
+ CONNECT_PRO
};
public:
@@ -43,6 +45,10 @@
struct CameraInfo* cameraInfo) = 0;
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient,
int cameraId) = 0;
+
+ virtual sp<IProCameraUser>
+ connect(const sp<IProCameraCallbacks>& cameraCb,
+ int cameraId) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/camera/IProCameraCallbacks.h b/include/camera/IProCameraCallbacks.h
new file mode 100644
index 0000000..fc24026
--- /dev/null
+++ b/include/camera/IProCameraCallbacks.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IPROCAMERA_CALLBACKS_H
+#define ANDROID_HARDWARE_IPROCAMERA_CALLBACKS_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <utils/Timers.h>
+#include <system/camera.h>
+
+struct camera_metadata;
+
+namespace android {
+
+class IProCameraCallbacks: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ProCameraCallbacks);
+
+ virtual void notifyCallback(int32_t msgType, int32_t ext1,
+ int32_t ext2) = 0;
+ virtual void dataCallback(int32_t msgType,
+ const sp<IMemory>& data,
+ camera_frame_metadata_t *metadata) = 0;
+ virtual void dataCallbackTimestamp(nsecs_t timestamp,
+ int32_t msgType,
+ const sp<IMemory>& data) = 0;
+
+ enum LockStatus {
+ LOCK_ACQUIRED,
+ LOCK_RELEASED,
+ LOCK_STOLEN,
+ };
+
+ virtual void onLockStatusChanged(LockStatus newLockStatus) = 0;
+
+ /** Missing by design: implementation is client-side in ProCamera.cpp **/
+ // virtual void onBufferReceived(int streamId,
+ // const CpuConsumer::LockedBufer& buf);
+ virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnProCameraCallbacks: public BnInterface<IProCameraCallbacks>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/camera/IProCameraUser.h b/include/camera/IProCameraUser.h
new file mode 100644
index 0000000..7bddb0c
--- /dev/null
+++ b/include/camera/IProCameraUser.h
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IPROCAMERAUSER_H
+#define ANDROID_HARDWARE_IPROCAMERAUSER_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <utils/String8.h>
+#include <camera/IProCameraCallbacks.h>
+
+struct camera_metadata;
+
+namespace android {
+
+class IProCameraUserClient;
+class IGraphicBufferProducer;
+class Surface;
+
+class IProCameraUser: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ProCameraUser);
+
+ virtual void disconnect() = 0;
+
+ // connect to the service, given a callbacks listener
+ virtual status_t connect(const sp<IProCameraCallbacks>& callbacks)
+ = 0;
+
+ /**
+ * Locking
+ **/
+ virtual status_t exclusiveTryLock() = 0;
+ virtual status_t exclusiveLock() = 0;
+ virtual status_t exclusiveUnlock() = 0;
+
+ virtual bool hasExclusiveLock() = 0;
+
+ /**
+ * Request Handling
+ **/
+
+ // Note that the callee gets a copy of the metadata.
+ virtual int submitRequest(struct camera_metadata* metadata,
+ bool streaming = false) = 0;
+ virtual status_t cancelRequest(int requestId) = 0;
+
+ virtual status_t requestStream(int streamId) = 0;
+ virtual status_t cancelStream(int streamId) = 0;
+ virtual status_t createStream(
+ int width, int height, int format,
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ /*out*/
+ int* streamId) = 0;
+
+ // Create a request object from a template.
+ virtual status_t createDefaultRequest(int templateId,
+ /*out*/
+ camera_metadata** request)
+ = 0;
+
+ // Get static camera metadata
+ virtual status_t getCameraInfo(int cameraId,
+ /*out*/
+ camera_metadata** info) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnProCameraUser: public BnInterface<IProCameraUser>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h
new file mode 100644
index 0000000..cd2772c
--- /dev/null
+++ b/include/camera/ProCamera.h
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_PRO_CAMERA_H
+#define ANDROID_HARDWARE_PRO_CAMERA_H
+
+#include <utils/Timers.h>
+#include <utils/KeyedVector.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <system/camera.h>
+#include <camera/IProCameraCallbacks.h>
+#include <camera/IProCameraUser.h>
+#include <camera/Camera.h>
+#include <camera/CameraMetadata.h>
+#include <gui/CpuConsumer.h>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+struct camera_metadata;
+
+namespace android {
+
+// All callbacks on this class are concurrent
+// (they come from separate threads)
+class ProCameraListener : public CameraListener
+{
+public:
+ // Lock has been acquired. Write operations now available.
+ virtual void onLockAcquired() = 0;
+ // Lock has been released with exclusiveUnlock.
+ virtual void onLockReleased() = 0;
+ // Lock has been stolen by another client.
+ virtual void onLockStolen() = 0;
+
+ // Lock free.
+ virtual void onTriggerNotify(int32_t msgType, int32_t ext1, int32_t ext2)
+ = 0;
+
+ // OnBufferReceived and OnRequestReceived can come in with any order,
+ // use android.sensor.timestamp and LockedBuffer.timestamp to correlate them
+
+ // A new frame buffer has been received for this stream.
+ // -- This callback only fires for createStreamCpu streams
+ // -- Use buf.timestamp to correlate with metadata's
+ // android.sensor.timestamp
+ // -- The buffer must not be accessed after this function call completes
+ virtual void onBufferReceived(int streamId,
+ const CpuConsumer::LockedBuffer& buf) = 0;
+ /**
+ * A new metadata buffer has been received.
+ * -- Ownership of request passes on to the callee, free with
+ * free_camera_metadata.
+ */
+ virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
+
+
+ // A new frame buffer has been received for this stream.
+ // -- This callback only fires for createStreamCpu streams
+ // -- Use buf.timestamp to correlate with metadata's android.sensor.timestamp
+ // -- The buffer should be accessed with CpuConsumer::lockNextBuffer
+ // and CpuConsumer::unlockBuffer
+ virtual void onFrameAvailable(int streamId,
+ const sp<CpuConsumer>& cpuConsumer) {
+ }
+
+ virtual bool useOnFrameAvailable() {
+ return false;
+ }
+};
+
+class ProCamera : public BnProCameraCallbacks, public IBinder::DeathRecipient
+{
+public:
+ /**
+ * Connect a shared camera. By default access is restricted to read only
+ * (Lock free) operations. To be able to submit custom requests a lock needs
+ * to be acquired with exclusive[Try]Lock.
+ */
+ static sp<ProCamera> connect(int cameraId);
+ virtual void disconnect();
+ virtual ~ProCamera();
+
+ void setListener(const sp<ProCameraListener>& listener);
+
+ /**
+ * Exclusive Locks:
+ * - We may request exclusive access to a camera if no other
+ * clients are using the camera. This works as a traditional
+ * client, writing/reading any camera state.
+ * - An application opening the camera (a regular 'Camera') will
+ * always steal away the exclusive lock from a ProCamera,
+ * this will call onLockReleased.
+ * - onLockAcquired will be called again once it is possible
+ * to again exclusively lock the camera.
+ *
+ */
+
+ /**
+ * All exclusiveLock/unlock functions are asynchronous. The remote endpoint
+ * shall not block while waiting to acquire the lock. Instead the lock
+ * notifications will come in asynchronously on the listener.
+ */
+
+ /**
+ * Attempt to acquire the lock instantly (non-blocking)
+ * - If this succeeds, you do not need to wait for onLockAcquired
+ * but the event will still be fired
+ *
+ * Returns -EBUSY if already locked. 0 on success.
+ */
+ status_t exclusiveTryLock();
+ // always returns 0. wait for onLockAcquired before lock is acquired.
+ status_t exclusiveLock();
+ // release a lock if we have one, or cancel the lock request.
+ status_t exclusiveUnlock();
+
+ // exclusive lock = do whatever we want. no lock = read only.
+ bool hasExclusiveLock();
+
+ /**
+ * < 0 error, >= 0 the request ID. streaming to have the request repeat
+ * until cancelled.
+ * The request queue is flushed when a lock is released or stolen
+ * if not locked will return PERMISSION_DENIED
+ */
+ int submitRequest(const struct camera_metadata* metadata,
+ bool streaming = false);
+ // if not locked will return PERMISSION_DENIED, BAD_VALUE if requestId bad
+ status_t cancelRequest(int requestId);
+
+ /**
+ * Ask for a stream to be enabled.
+ * Lock free. Service maintains counter of streams.
+ */
+ status_t requestStream(int streamId);
+// TODO: remove requestStream, its useless.
+
+ /**
+ * Delete a stream.
+ * Lock free.
+ * Errors: BAD_VALUE if unknown stream ID.
+ * PERMISSION_DENIED if the stream wasn't yours
+ */
+ status_t deleteStream(int streamId);
+
+ /**
+ * Create a new HW stream, whose sink will be the window.
+ * Lock free. Service maintains counter of streams.
+ * Errors: -EBUSY if too many streams created
+ */
+ status_t createStream(int width, int height, int format,
+ const sp<Surface>& surface,
+ /*out*/
+ int* streamId);
+
+ /**
+ * Create a new HW stream, whose sink will be the SurfaceTexture.
+ * Lock free. Service maintains counter of streams.
+ * Errors: -EBUSY if too many streams created
+ */
+ status_t createStream(int width, int height, int format,
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ /*out*/
+ int* streamId);
+ status_t createStreamCpu(int width, int height, int format,
+ int heapCount,
+ /*out*/
+ sp<CpuConsumer>* cpuConsumer,
+ int* streamId);
+
+ // Create a request object from a template.
+ status_t createDefaultRequest(int templateId,
+ /*out*/
+ camera_metadata** request) const;
+
+ // Get number of cameras
+ static int getNumberOfCameras();
+
+ // Get static camera metadata
+ camera_metadata* getCameraInfo(int cameraId);
+
+ // Blocks until a frame is available (CPU streams only)
+ // - Obtain the frame data by calling CpuConsumer::lockNextBuffer
+ // - Release the frame data after use with CpuConsumer::unlockBuffer
+ // Return value:
+ // - >0 - number of frames available to be locked
+ // - <0 - error (refer to error codes)
+ // Error codes:
+ // -ETIMEDOUT if it took too long to get a frame
+ int waitForFrameBuffer(int streamId);
+
+ // Blocks until a metadata result is available
+ // - Obtain the metadata by calling consumeFrameMetadata()
+ // Error codes:
+ // -ETIMEDOUT if it took too long to get a frame
+ status_t waitForFrameMetadata();
+
+ // Get the latest metadata. This is destructive.
+ // - Calling this repeatedly will produce empty metadata objects.
+ // - Use waitForFrameMetadata to sync until new data is available.
+ CameraMetadata consumeFrameMetadata();
+
+ // Convenience method to drop frame buffers (CPU streams only)
+ // Return values:
+ // >=0 - number of frames dropped (up to count)
+ // <0 - error code
+ // Error codes:
+ // BAD_VALUE - invalid streamId or count passed
+ int dropFrameBuffer(int streamId, int count);
+
+ sp<IProCameraUser> remote();
+
+protected:
+ ////////////////////////////////////////////////////////
+ // IProCameraCallbacks implementation
+ ////////////////////////////////////////////////////////
+ virtual void notifyCallback(int32_t msgType, int32_t ext,
+ int32_t ext2);
+ virtual void dataCallback(int32_t msgType,
+ const sp<IMemory>& dataPtr,
+ camera_frame_metadata_t *metadata);
+ virtual void dataCallbackTimestamp(nsecs_t timestamp,
+ int32_t msgType,
+ const sp<IMemory>& dataPtr);
+ virtual void onLockStatusChanged(
+ IProCameraCallbacks::LockStatus newLockStatus);
+
+ virtual void onResultReceived(int32_t frameId,
+ camera_metadata* result);
+
+ class DeathNotifier: public IBinder::DeathRecipient
+ {
+ public:
+ DeathNotifier() {
+ }
+
+ virtual void binderDied(const wp<IBinder>& who);
+ };
+
+private:
+ ProCamera();
+
+ virtual void binderDied(const wp<IBinder>& who);
+
+ // helper function to obtain camera service handle
+ static const sp<ICameraService>& getCameraService();
+
+ static sp<DeathNotifier> mDeathNotifier;
+
+ sp<IProCameraUser> mCamera;
+ status_t mStatus;
+
+ sp<ProCameraListener> mListener;
+
+ friend class DeathNotifier;
+
+ static Mutex mLock;
+ static sp<ICameraService> mCameraService;
+
+ class ProFrameListener : public CpuConsumer::FrameAvailableListener {
+ public:
+ ProFrameListener(wp<ProCamera> camera, int streamID) {
+ mCamera = camera;
+ mStreamId = streamID;
+ }
+
+ protected:
+ virtual void onFrameAvailable() {
+ sp<ProCamera> c = mCamera.promote();
+ if (c.get() != NULL) {
+ c->onFrameAvailable(mStreamId);
+ }
+ }
+
+ private:
+ wp<ProCamera> mCamera;
+ int mStreamId;
+ };
+ friend class ProFrameListener;
+
+ struct StreamInfo
+ {
+ StreamInfo(int streamId) {
+ this->streamID = streamId;
+ cpuStream = false;
+ frameReady = 0;
+ }
+
+ StreamInfo() {
+ streamID = -1;
+ cpuStream = false;
+ }
+
+ int streamID;
+ bool cpuStream;
+ sp<CpuConsumer> cpuConsumer;
+ sp<ProFrameListener> frameAvailableListener;
+ sp<Surface> stc;
+ int frameReady;
+ };
+
+ Condition mWaitCondition;
+ Mutex mWaitMutex;
+ static const nsecs_t mWaitTimeout = 1000000000; // 1sec
+ KeyedVector<int, StreamInfo> mStreams;
+ bool mMetadataReady;
+ CameraMetadata mLatestMetadata;
+
+ void onFrameAvailable(int streamId);
+
+ StreamInfo& getStreamInfo(int streamId);
+
+
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 6d42143..0855db6 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -66,14 +66,6 @@
LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
-# uncomment for dumpsys to write most recent audio output to .wav file
-# 47.5 seconds at 44.1 kHz, 8 megabytes
-# LOCAL_CFLAGS += -DTEE_SINK_FRAMES=0x200000
-
-# uncomment for dumpsys to write most recent audio input to .wav file
-# 47.5 seconds at 44.1 kHz, 8 megabytes
-# LOCAL_CFLAGS += -DTEE_SINK_INPUT_FRAMES=0x200000
-
# uncomment to enable the audio watchdog
# LOCAL_SRC_FILES += AudioWatchdog.cpp
# LOCAL_CFLAGS += -DAUDIO_WATCHDOG
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 47c2772..e0ab8cd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include <dirent.h>
#include <math.h>
#include <signal.h>
#include <sys/time.h>
@@ -61,6 +62,9 @@
#include <media/IMediaLogService.h>
+#include <media/nbaio/Pipe.h>
+#include <media/nbaio/PipeReader.h>
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -86,6 +90,14 @@
uint32_t AudioFlinger::mScreenState;
+bool AudioFlinger::mTeeSinkInputEnabled = false;
+bool AudioFlinger::mTeeSinkOutputEnabled = false;
+bool AudioFlinger::mTeeSinkTrackEnabled = false;
+
+size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault;
+size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
+size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
+
// ----------------------------------------------------------------------------
static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
@@ -134,6 +146,19 @@
if (doLog) {
mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters");
}
+ (void) property_get("ro.debuggable", value, "0");
+ int debuggable = atoi(value);
+ int teeEnabled = 0;
+ if (debuggable) {
+ (void) property_get("af.tee", value, "0");
+ teeEnabled = atoi(value);
+ }
+ if (teeEnabled & 1)
+ mTeeSinkInputEnabled = true;
+ if (teeEnabled & 2)
+ mTeeSinkOutputEnabled = true;
+ if (teeEnabled & 4)
+ mTeeSinkTrackEnabled = true;
}
void AudioFlinger::onFirstRef()
@@ -1602,7 +1627,6 @@
// Try to re-use most recently used Pipe to archive a copy of input for dumpsys,
// or (re-)create if current Pipe is idle and does not match the new format
sp<NBAIO_Sink> teeSink;
-#ifdef TEE_SINK_INPUT_FRAMES
enum {
TEE_SINK_NO, // don't copy input
TEE_SINK_NEW, // copy input using a new pipe
@@ -1610,7 +1634,9 @@
} kind;
NBAIO_Format format = Format_from_SR_C(inStream->common.get_sample_rate(&inStream->common),
popcount(inStream->common.get_channels(&inStream->common)));
- if (format == Format_Invalid) {
+ if (!mTeeSinkInputEnabled) {
+ kind = TEE_SINK_NO;
+ } else if (format == Format_Invalid) {
kind = TEE_SINK_NO;
} else if (mRecordTeeSink == 0) {
kind = TEE_SINK_NEW;
@@ -1623,7 +1649,7 @@
}
switch (kind) {
case TEE_SINK_NEW: {
- Pipe *pipe = new Pipe(TEE_SINK_INPUT_FRAMES, format);
+ Pipe *pipe = new Pipe(mTeeSinkInputFrames, format);
size_t numCounterOffers = 0;
const NBAIO_Format offers[1] = {format};
ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
@@ -1644,7 +1670,7 @@
default:
break;
}
-#endif
+
AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
// Start record thread
@@ -2199,19 +2225,80 @@
return NO_ERROR;
}
+struct Entry {
+#define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav
+ char mName[MAX_NAME];
+};
+
+int comparEntry(const void *p1, const void *p2)
+{
+ return strcmp(((const Entry *) p1)->mName, ((const Entry *) p2)->mName);
+}
+
void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id)
{
NBAIO_Source *teeSource = source.get();
if (teeSource != NULL) {
+ // .wav rotation
+ // There is a benign race condition if 2 threads call this simultaneously.
+ // They would both traverse the directory, but the result would simply be
+ // failures at unlink() which are ignored. It's also unlikely since
+ // normally dumpsys is only done by bugreport or from the command line.
+ char teePath[32+256];
+ strcpy(teePath, "/data/misc/media");
+ size_t teePathLen = strlen(teePath);
+ DIR *dir = opendir(teePath);
+ teePath[teePathLen++] = '/';
+ if (dir != NULL) {
+#define MAX_SORT 20 // number of entries to sort
+#define MAX_KEEP 10 // number of entries to keep
+ struct Entry entries[MAX_SORT];
+ size_t entryCount = 0;
+ while (entryCount < MAX_SORT) {
+ struct dirent de;
+ struct dirent *result = NULL;
+ int rc = readdir_r(dir, &de, &result);
+ if (rc != 0) {
+ ALOGW("readdir_r failed %d", rc);
+ break;
+ }
+ if (result == NULL) {
+ break;
+ }
+ if (result != &de) {
+ ALOGW("readdir_r returned unexpected result %p != %p", result, &de);
+ break;
+ }
+ // ignore non .wav file entries
+ size_t nameLen = strlen(de.d_name);
+ if (nameLen <= 4 || nameLen >= MAX_NAME ||
+ strcmp(&de.d_name[nameLen - 4], ".wav")) {
+ continue;
+ }
+ strcpy(entries[entryCount++].mName, de.d_name);
+ }
+ (void) closedir(dir);
+ if (entryCount > MAX_KEEP) {
+ qsort(entries, entryCount, sizeof(Entry), comparEntry);
+ for (size_t i = 0; i < entryCount - MAX_KEEP; ++i) {
+ strcpy(&teePath[teePathLen], entries[i].mName);
+ (void) unlink(teePath);
+ }
+ }
+ } else {
+ if (fd >= 0) {
+ fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
+ }
+ }
char teeTime[16];
struct timeval tv;
gettimeofday(&tv, NULL);
struct tm tm;
localtime_r(&tv.tv_sec, &tm);
- strftime(teeTime, sizeof(teeTime), "%T", &tm);
- char teePath[64];
- sprintf(teePath, "/data/misc/media/%s_%d.wav", teeTime, id);
- int teeFd = open(teePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
+ snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d.wav", teeTime, id);
+ // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
+ int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
if (teeFd >= 0) {
char wavHeader[44];
memcpy(wavHeader,
@@ -2253,9 +2340,13 @@
temp = total * channelCount * sizeof(short);
write(teeFd, &temp, sizeof(temp));
close(teeFd);
- fdprintf(fd, "FastMixer tee copied to %s\n", teePath);
+ if (fd >= 0) {
+ fdprintf(fd, "tee copied to %s\n", teePath);
+ }
} else {
- fdprintf(fd, "FastMixer unable to create tee %s: \n", strerror(errno));
+ if (fd >= 0) {
+ fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
+ }
}
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c3f08f6..44bd260 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -593,7 +593,24 @@
sp<NBAIO_Source> mRecordTeeSource;
public:
+ // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id = 0);
+
+ // whether tee sink is enabled by property
+ static bool mTeeSinkInputEnabled;
+ static bool mTeeSinkOutputEnabled;
+ static bool mTeeSinkTrackEnabled;
+
+ // runtime configured size of each tee sink pipe, in frames
+ static size_t mTeeSinkInputFrames;
+ static size_t mTeeSinkOutputFrames;
+ static size_t mTeeSinkTrackFrames;
+
+ // compile-time default size of tee sink pipes, in frames
+ // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
+ static const size_t kTeeSinkInputFramesDefault = 0x200000;
+ static const size_t kTeeSinkOutputFramesDefault = 0x200000;
+ static const size_t kTeeSinkTrackFramesDefault = 0x1000;
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ba848d7..1209ea6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2124,19 +2124,19 @@
(monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
mPipeSink = monoPipe;
-#ifdef TEE_SINK_FRAMES
- // create a Pipe to archive a copy of FastMixer's output for dumpsys
- Pipe *teeSink = new Pipe(TEE_SINK_FRAMES, format);
- numCounterOffers = 0;
- index = teeSink->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mTeeSink = teeSink;
- PipeReader *teeSource = new PipeReader(*teeSink);
- numCounterOffers = 0;
- index = teeSource->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mTeeSource = teeSource;
-#endif
+ if (mTeeSinkOutputEnabled) {
+ // create a Pipe to archive a copy of FastMixer's output for dumpsys
+ Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, format);
+ numCounterOffers = 0;
+ index = teeSink->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mTeeSink = teeSink;
+ PipeReader *teeSource = new PipeReader(*teeSink);
+ numCounterOffers = 0;
+ index = teeSource->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mTeeSource = teeSource;
+ }
// create fast mixer and configure it initially with just one fast track for our submix
mFastMixer = new FastMixer();
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index e0bd97a..fecbfda 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -141,4 +141,7 @@
Vector < sp<SyncEvent> >mSyncEvents;
const bool mIsOut;
ServerProxy* mServerProxy;
+ const int mId;
+ sp<NBAIO_Sink> mTeeSink;
+ sp<NBAIO_Source> mTeeSource;
};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 315cbbc..724ce38 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -32,6 +32,9 @@
#include "AudioFlinger.h"
#include "ServiceUtilities.h"
+#include <media/nbaio/Pipe.h>
+#include <media/nbaio/PipeReader.h>
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -53,6 +56,8 @@
// TrackBase
// ----------------------------------------------------------------------------
+static volatile int32_t nextTrackId = 55;
+
// TrackBase constructor must be called with AudioFlinger::mLock held
AudioFlinger::ThreadBase::TrackBase::TrackBase(
ThreadBase *thread,
@@ -82,7 +87,8 @@
mStepServerFailed(false),
mSessionId(sessionId),
mIsOut(isOut),
- mServerProxy(NULL)
+ mServerProxy(NULL),
+ mId(android_atomic_inc(&nextTrackId))
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -134,11 +140,30 @@
}
mBufferEnd = (uint8_t *)mBuffer + bufferSize;
mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut);
+
+ if (mTeeSinkTrackEnabled) {
+ NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount);
+ if (pipeFormat != Format_Invalid) {
+ Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat);
+ size_t numCounterOffers = 0;
+ const NBAIO_Format offers[1] = {pipeFormat};
+ ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ PipeReader *pipeReader = new PipeReader(*pipe);
+ numCounterOffers = 0;
+ index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mTeeSink = pipe;
+ mTeeSource = pipeReader;
+ }
+ }
+
}
}
AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
+ dumpTee(-1, mTeeSource, mId);
// delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
delete mServerProxy;
if (mCblk != NULL) {
@@ -164,6 +189,10 @@
// This implementation of releaseBuffer() is used by Track and RecordTrack, but not TimedTrack
void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
+ if (mTeeSink != 0) {
+ (void) mTeeSink->write(buffer->raw, buffer->frameCount);
+ }
+
buffer->raw = NULL;
mStepCount = buffer->frameCount;
// FIXME See note at getNextBuffer()
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 5245983..f76c861 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -10,8 +10,8 @@
CameraService.cpp \
CameraClient.cpp \
Camera2Client.cpp \
+ ProCamera2Client.cpp \
Camera2Device.cpp \
- camera2/CameraMetadata.cpp \
camera2/Parameters.cpp \
camera2/FrameProcessor.cpp \
camera2/StreamingProcessor.cpp \
@@ -20,7 +20,8 @@
camera2/ZslProcessor.cpp \
camera2/BurstCapture.cpp \
camera2/JpegCompressor.cpp \
- camera2/CaptureSequencer.cpp
+ camera2/CaptureSequencer.cpp \
+ camera2/ProFrameProcessor.cpp \
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 4669958..a4d4478 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -157,7 +157,6 @@
mutable Mutex mICameraLock;
typedef camera2::Parameters Parameters;
- typedef camera2::CameraMetadata CameraMetadata;
status_t setPreviewWindowL(const sp<IBinder>& binder,
sp<ANativeWindow> window);
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 5bfa085..921c8fc 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -202,7 +202,7 @@
return res;
}
-const camera2::CameraMetadata& Camera2Device::info() const {
+const CameraMetadata& Camera2Device::info() const {
ALOGVV("%s: E", __FUNCTION__);
return mDeviceInfo;
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 41df2e4..86ff80f 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -27,14 +27,12 @@
#include <utils/Vector.h>
#include "hardware/camera2.h"
-#include "camera2/CameraMetadata.h"
+#include "camera/CameraMetadata.h"
namespace android {
class Camera2Device : public virtual RefBase {
public:
- typedef camera2::CameraMetadata CameraMetadata;
-
Camera2Device(int id);
~Camera2Device();
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 31e20c5..eb8bc05 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -39,6 +39,7 @@
#include "CameraService.h"
#include "CameraClient.h"
#include "Camera2Client.h"
+#include "ProCamera2Client.h"
namespace android {
@@ -79,6 +80,8 @@
void CameraService::onFirstRef()
{
+ LOG1("CameraService::onFirstRef");
+
BnCameraService::onFirstRef();
if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
@@ -131,6 +134,26 @@
return rc;
}
+int CameraService::getDeviceVersion(int cameraId, int* facing) {
+ struct camera_info info;
+ if (mModule->get_camera_info(cameraId, &info) != OK) {
+ return -1;
+ }
+
+ int deviceVersion;
+ if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_0) {
+ deviceVersion = info.device_version;
+ } else {
+ deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
+ }
+
+ if (facing) {
+ *facing = info.facing;
+ }
+
+ return deviceVersion;
+}
+
sp<ICamera> CameraService::connect(
const sp<ICameraClient>& cameraClient, int cameraId) {
int callingPid = getCallingPid();
@@ -166,6 +189,7 @@
callingPid);
return client;
} else {
+ // TODOSC: need to support 1 regular client, multiple shared clients here
ALOGW("CameraService::connect X (pid %d) rejected (existing client).",
callingPid);
return NULL;
@@ -174,34 +198,37 @@
mClient[cameraId].clear();
}
+ /*
+ mBusy is set to false as the last step of the Client destructor,
+ after which it is guaranteed that the Client destructor has finished (
+ including any inherited destructors)
+
+ We only need this for a Client subclasses since we don't allow
+ multiple Clents to be opened concurrently, but multiple BasicClient
+ would be fine
+ */
if (mBusy[cameraId]) {
ALOGW("CameraService::connect X (pid %d) rejected"
" (camera %d is still busy).", callingPid, cameraId);
return NULL;
}
- struct camera_info info;
- if (mModule->get_camera_info(cameraId, &info) != OK) {
- ALOGE("Invalid camera id %d", cameraId);
- return NULL;
- }
-
- int deviceVersion;
- if (mModule->common.module_api_version == CAMERA_MODULE_API_VERSION_2_0) {
- deviceVersion = info.device_version;
- } else {
- deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
- }
+ int facing = -1;
+ int deviceVersion = getDeviceVersion(cameraId, &facing);
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
client = new CameraClient(this, cameraClient, cameraId,
- info.facing, callingPid, getpid());
+ facing, callingPid, getpid());
break;
case CAMERA_DEVICE_API_VERSION_2_0:
+ case CAMERA_DEVICE_API_VERSION_2_1:
client = new Camera2Client(this, cameraClient, cameraId,
- info.facing, callingPid, getpid());
+ facing, callingPid, getpid());
break;
+ case -1:
+ ALOGE("Invalid camera id %d", cameraId);
+ return NULL;
default:
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
return NULL;
@@ -218,16 +245,80 @@
return client;
}
-void CameraService::removeClient(const sp<ICameraClient>& cameraClient) {
+sp<IProCameraUser> CameraService::connect(
+ const sp<IProCameraCallbacks>& cameraCb,
+ int cameraId)
+{
int callingPid = getCallingPid();
- LOG1("CameraService::removeClient E (pid %d)", callingPid);
+
+ LOG1("CameraService::connectPro E (pid %d, id %d)", callingPid, cameraId);
+
+ if (!mModule) {
+ ALOGE("Camera HAL module not loaded");
+ return NULL;
+ }
+
+ sp<ProClient> client;
+ if (cameraId < 0 || cameraId >= mNumberOfCameras) {
+ ALOGE("CameraService::connectPro X (pid %d) rejected (invalid cameraId %d).",
+ callingPid, cameraId);
+ return NULL;
+ }
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("sys.secpolicy.camera.disabled", value, "0");
+ if (strcmp(value, "1") == 0) {
+ // Camera is disabled by DevicePolicyManager.
+ ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);
+ return NULL;
+ }
+
+ int facing = -1;
+ int deviceVersion = getDeviceVersion(cameraId, &facing);
+
+ switch(deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_1_0:
+ ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", cameraId);
+ return NULL;
+ break;
+ case CAMERA_DEVICE_API_VERSION_2_0:
+ case CAMERA_DEVICE_API_VERSION_2_1:
+ client = new ProCamera2Client(this, cameraCb, cameraId,
+ facing, callingPid, getpid());
+ break;
+ case -1:
+ ALOGE("Invalid camera id %d", cameraId);
+ return NULL;
+ default:
+ ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+ return NULL;
+ }
+
+ if (client->initialize(mModule) != OK) {
+ return NULL;
+ }
+
+ mProClientList[cameraId].push(client);
+
+ cameraCb->asBinder()->linkToDeath(this);
+
+ LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
+ return client;
+
+
+ return NULL;
+}
+
+void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) {
+ int callingPid = getCallingPid();
+ LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid);
// Declare this before the lock to make absolutely sure the
// destructor won't be called with the lock held.
Mutex::Autolock lock(mServiceLock);
int outIndex;
- sp<Client> client = findClientUnsafe(cameraClient->asBinder(), outIndex);
+ sp<Client> client = findClientUnsafe(remoteBinder, outIndex);
if (client != 0) {
// Found our camera, clear and leave.
@@ -235,9 +326,50 @@
mClient[outIndex].clear();
client->unlinkToDeath(this);
+ } else {
+
+ sp<ProClient> clientPro = findProClientUnsafe(remoteBinder);
+
+ if (clientPro != NULL) {
+ // Found our camera, clear and leave.
+ LOG1("removeClient: clear pro %p", clientPro.get());
+
+ clientPro->getRemoteCallback()->asBinder()->unlinkToDeath(this);
+ }
}
- LOG1("CameraService::removeClient X (pid %d)", callingPid);
+ LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid);
+}
+
+sp<CameraService::ProClient> CameraService::findProClientUnsafe(
+ const wp<IBinder>& cameraCallbacksRemote)
+{
+ sp<ProClient> clientPro;
+
+ for (int i = 0; i < mNumberOfCameras; ++i) {
+ Vector<size_t> removeIdx;
+
+ for (size_t j = 0; j < mProClientList[i].size(); ++j) {
+ wp<ProClient> cl = mProClientList[i][j];
+
+ sp<ProClient> clStrong = cl.promote();
+ if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) {
+ clientPro = clStrong;
+ break;
+ } else if (clStrong == NULL) {
+ // mark to clean up dead ptr
+ removeIdx.push(j);
+ }
+ }
+
+ // remove stale ptrs (in reverse so the indices dont change)
+ for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) {
+ mProClientList[i].removeAt(removeIdx[j]);
+ }
+
+ }
+
+ return clientPro;
}
sp<CameraService::Client> CameraService::findClientUnsafe(
@@ -251,7 +383,7 @@
if (mClient[i] == 0) continue;
// Promote mClient. It can fail if we are called from this path:
- // Client::~Client() -> disconnect() -> removeClient().
+ // Client::~Client() -> disconnect() -> removeClientByRemote().
client = mClient[i].promote();
// Clean up stale client entry
@@ -281,12 +413,12 @@
return &mClientLock[cameraId];
}
-sp<CameraService::Client> CameraService::getClientByRemote(
+sp<CameraService::BasicClient> CameraService::getClientByRemote(
const wp<IBinder>& cameraClient) {
// Declare this before the lock to make absolutely sure the
// destructor won't be called with the lock held.
- sp<Client> client;
+ sp<BasicClient> client;
Mutex::Autolock lock(mServiceLock);
@@ -301,6 +433,7 @@
// Permission checks
switch (code) {
case BnCameraService::CONNECT:
+ case BnCameraService::CONNECT_PRO:
const int pid = getCallingPid();
const int self_pid = getpid();
if (pid != self_pid) {
@@ -389,17 +522,15 @@
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
- int cameraId, int cameraFacing, int clientPid, int servicePid) {
+ int cameraId, int cameraFacing, int clientPid, int servicePid) :
+ CameraService::BasicClient(cameraService, cameraClient->asBinder(),
+ cameraId, cameraFacing,
+ clientPid, servicePid)
+{
int callingPid = getCallingPid();
LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId);
- mCameraService = cameraService;
mCameraClient = cameraClient;
- mCameraId = cameraId;
- mCameraFacing = cameraFacing;
- mClientPid = clientPid;
- mServicePid = servicePid;
- mDestructionStarted = false;
cameraService->setCameraBusy(cameraId);
cameraService->loadSound();
@@ -408,12 +539,37 @@
// tear down the client
CameraService::Client::~Client() {
+ mDestructionStarted = true;
+
mCameraService->releaseSound();
// unconditionally disconnect. function is idempotent
Client::disconnect();
}
+CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService,
+ const sp<IBinder>& remoteCallback,
+ int cameraId, int cameraFacing,
+ int clientPid, int servicePid)
+{
+ mCameraService = cameraService;
+ mRemoteCallback = remoteCallback;
+ mCameraId = cameraId;
+ mCameraFacing = cameraFacing;
+ mClientPid = clientPid;
+ mServicePid = servicePid;
+
+ mDestructionStarted = false;
+}
+
+CameraService::BasicClient::~BasicClient() {
+ mDestructionStarted = true;
+}
+
+void CameraService::BasicClient::disconnect() {
+ mCameraService->removeClientByRemote(mRemoteCallback);
+}
+
// ----------------------------------------------------------------------------
Mutex* CameraService::Client::getClientLockFromCookie(void* user) {
@@ -438,11 +594,96 @@
// NOTE: function is idempotent
void CameraService::Client::disconnect() {
- mCameraService->removeClient(mCameraClient);
+ BasicClient::disconnect();
mCameraService->setCameraFree(mCameraId);
}
// ----------------------------------------------------------------------------
+// IProCamera
+// ----------------------------------------------------------------------------
+
+CameraService::ProClient::ProClient(const sp<CameraService>& cameraService,
+ const sp<IProCameraCallbacks>& remoteCallback,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid)
+ : CameraService::BasicClient(cameraService, remoteCallback->asBinder(),
+ cameraId, cameraFacing,
+ clientPid, servicePid)
+{
+ mRemoteCallback = remoteCallback;
+}
+
+CameraService::ProClient::~ProClient() {
+ mDestructionStarted = true;
+
+ ProClient::disconnect();
+}
+
+status_t CameraService::ProClient::connect(const sp<IProCameraCallbacks>& callbacks) {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+
+ return INVALID_OPERATION;
+}
+
+void CameraService::ProClient::disconnect() {
+ BasicClient::disconnect();
+}
+
+status_t CameraService::ProClient::initialize(camera_module_t* module)
+{
+ ALOGW("%s: not implemented yet", __FUNCTION__);
+ return OK;
+}
+
+status_t CameraService::ProClient::exclusiveTryLock() {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::exclusiveLock() {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::exclusiveUnlock() {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+bool CameraService::ProClient::hasExclusiveLock() {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+ return false;
+}
+
+status_t CameraService::ProClient::submitRequest(camera_metadata_t* request, bool streaming) {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+
+ free_camera_metadata(request);
+
+ return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::cancelRequest(int requestId) {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+
+ return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::requestStream(int streamId) {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+
+ return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::cancelStream(int streamId) {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+
+ return INVALID_OPERATION;
+}
+
+// ----------------------------------------------------------------------------
static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 60000;
@@ -568,7 +809,7 @@
ALOGV("java clients' binder died");
- sp<Client> cameraClient = getClientByRemote(who);
+ sp<BasicClient> cameraClient = getClientByRemote(who);
if (cameraClient == 0) {
ALOGV("java clients' binder death already cleaned up (normal case)");
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 41365a0..9e0f62a 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -18,6 +18,7 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
+#include <utils/Vector.h>
#include <binder/BinderService.h>
#include <camera/ICameraService.h>
#include <hardware/camera.h>
@@ -40,27 +41,32 @@
friend class BinderService<CameraService>;
public:
class Client;
+ class BasicClient;
+
+ // Implementation of BinderService<T>
static char const* getServiceName() { return "media.camera"; }
CameraService();
virtual ~CameraService();
+ /////////////////////////////////////////////////////////////////////
+ // ICameraService
virtual int32_t getNumberOfCameras();
virtual status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo);
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId);
- virtual void removeClient(const sp<ICameraClient>& cameraClient);
- // returns plain pointer of client. Note that mClientLock should be acquired to
- // prevent the client from destruction. The result can be NULL.
- virtual Client* getClientByIdUnsafe(int cameraId);
- virtual Mutex* getClientLockById(int cameraId);
+ virtual sp<IProCameraUser>
+ connect(const sp<IProCameraCallbacks>& cameraCb, int cameraId);
- virtual sp<Client> getClientByRemote(const wp<IBinder>& cameraClient);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
+ // Extra permissions checks
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
- virtual void onFirstRef();
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ /////////////////////////////////////////////////////////////////////
+ // Client functionality
+ virtual void removeClientByRemote(const wp<IBinder>& remoteBinder);
enum sound_kind {
SOUND_SHUTTER = 0,
@@ -72,7 +78,53 @@
void playSound(sound_kind kind);
void releaseSound();
- class Client : public BnCamera
+
+ /////////////////////////////////////////////////////////////////////
+ // CameraClient functionality
+
+ // returns plain pointer of client. Note that mClientLock should be acquired to
+ // prevent the client from destruction. The result can be NULL.
+ virtual Client* getClientByIdUnsafe(int cameraId);
+ virtual Mutex* getClientLockById(int cameraId);
+
+ class BasicClient : public virtual RefBase {
+ public:
+ virtual status_t initialize(camera_module_t *module) = 0;
+
+ virtual void disconnect() = 0;
+
+ wp<IBinder> getRemote() {
+ return mRemoteCallback;
+ }
+
+ protected:
+ BasicClient(const sp<CameraService>& cameraService,
+ const sp<IBinder>& remoteCallback,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid);
+
+ virtual ~BasicClient();
+
+ // the instance is in the middle of destruction. When this is set,
+ // the instance should not be accessed from callback.
+ // CameraService's mClientLock should be acquired to access this.
+ // - subclasses should set this to true in their destructors.
+ bool mDestructionStarted;
+
+ // these are initialized in the constructor.
+ sp<CameraService> mCameraService; // immutable after constructor
+ int mCameraId; // immutable after constructor
+ int mCameraFacing; // immutable after constructor
+ pid_t mClientPid;
+ pid_t mServicePid; // immutable after constructor
+
+ // - The app-side Binder interface to receive callbacks from us
+ wp<IBinder> mRemoteCallback; // immutable after constructor
+ };
+
+ class Client : public BnCamera, public BasicClient
{
public:
// ICamera interface (see ICamera for details)
@@ -112,38 +164,82 @@
return mCameraClient;
}
- virtual status_t initialize(camera_module_t *module) = 0;
-
- virtual status_t dump(int fd, const Vector<String16>& args) = 0;
-
protected:
static Mutex* getClientLockFromCookie(void* user);
// convert client from cookie. Client lock should be acquired before getting Client.
static Client* getClientFromCookie(void* user);
- // the instance is in the middle of destruction. When this is set,
- // the instance should not be accessed from callback.
- // CameraService's mClientLock should be acquired to access this.
- bool mDestructionStarted;
+ // Initialized in constructor
- // these are initialized in the constructor.
- sp<CameraService> mCameraService; // immutable after constructor
+ // - The app-side Binder interface to receive callbacks from us
sp<ICameraClient> mCameraClient;
- int mCameraId; // immutable after constructor
- int mCameraFacing; // immutable after constructor
- pid_t mClientPid;
- pid_t mServicePid; // immutable after constructor
+ };
+
+ class ProClient : public BnProCameraUser, public BasicClient {
+ public:
+ ProClient(const sp<CameraService>& cameraService,
+ const sp<IProCameraCallbacks>& remoteCallback,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid);
+
+ virtual ~ProClient();
+
+ const sp<IProCameraCallbacks>& getRemoteCallback() {
+ return mRemoteCallback;
+ }
+
+ // BasicClient implementation
+ virtual status_t initialize(camera_module_t *module);
+
+ /***
+ IProCamera implementation
+ ***/
+
+
+ virtual status_t connect(
+ const sp<IProCameraCallbacks>& callbacks);
+ virtual void disconnect();
+
+ virtual status_t exclusiveTryLock();
+ virtual status_t exclusiveLock();
+ virtual status_t exclusiveUnlock();
+
+ virtual bool hasExclusiveLock();
+
+ // Note that the callee gets a copy of the metadata.
+ virtual int submitRequest(camera_metadata_t* metadata,
+ bool streaming = false);
+ virtual status_t cancelRequest(int requestId);
+
+ virtual status_t requestStream(int streamId);
+ virtual status_t cancelStream(int streamId);
+
+ protected:
+ sp<IProCameraCallbacks> mRemoteCallback;
};
private:
+
+ // Delay-load the Camera HAL module
+ virtual void onFirstRef();
+
+ virtual sp<BasicClient> getClientByRemote(const wp<IBinder>& cameraClient);
+
Mutex mServiceLock;
wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock
Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
int mNumberOfCameras;
+ typedef wp<ProClient> weak_pro_client_ptr;
+ Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS];
+
// needs to be called with mServiceLock held
sp<Client> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
+ sp<ProClient> findProClientUnsafe(
+ const wp<IBinder>& cameraCallbacksRemote);
// atomics to record whether the hardware is allocated to some client.
volatile int32_t mBusy[MAX_CAMERAS];
@@ -161,6 +257,9 @@
// IBinder::DeathRecipient implementation
virtual void binderDied(const wp<IBinder> &who);
+
+ // Helpers
+ int getDeviceVersion(int cameraId, int* facing);
};
} // namespace android
diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp
new file mode 100644
index 0000000..c264e2a
--- /dev/null
+++ b/services/camera/libcameraservice/ProCamera2Client.cpp
@@ -0,0 +1,498 @@
+/*
+ * 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_TAG "ProCamera2Client"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <cutils/properties.h>
+#include <gui/Surface.h>
+#include <gui/Surface.h>
+#include "camera2/Parameters.h"
+#include "ProCamera2Client.h"
+#include "camera2/ProFrameProcessor.h"
+
+namespace android {
+using namespace camera2;
+
+static int getCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+static int getCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+// Interface used by CameraService
+
+ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService,
+ const sp<IProCameraCallbacks>& remoteCallback,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid):
+ ProClient(cameraService, remoteCallback,
+ cameraId, cameraFacing, clientPid, servicePid),
+ mSharedCameraCallbacks(remoteCallback)
+{
+ ATRACE_CALL();
+ ALOGI("ProCamera %d: Opened", cameraId);
+
+ mDevice = new Camera2Device(cameraId);
+
+ mExclusiveLock = false;
+}
+
+status_t ProCamera2Client::checkPid(const char* checkLocation) const {
+ int callingPid = getCallingPid();
+ if (callingPid == mClientPid) return NO_ERROR;
+
+ ALOGE("%s: attempt to use a locked camera from a different process"
+ " (old pid %d, new pid %d)", checkLocation, mClientPid, callingPid);
+ return PERMISSION_DENIED;
+}
+
+status_t ProCamera2Client::initialize(camera_module_t *module)
+{
+ ATRACE_CALL();
+ ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
+ status_t res;
+
+ res = mDevice->initialize(module);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return NO_INIT;
+ }
+
+ res = mDevice->setNotifyCallback(this);
+
+ String8 threadName;
+ mFrameProcessor = new ProFrameProcessor(this);
+ threadName = String8::format("PC2-%d-FrameProc",
+ mCameraId);
+ mFrameProcessor->run(threadName.string());
+
+ mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+ FRAME_PROCESSOR_LISTENER_MAX_ID,
+ /*listener*/this);
+
+ return OK;
+}
+
+ProCamera2Client::~ProCamera2Client() {
+ ATRACE_CALL();
+
+ mDestructionStarted = true;
+
+ disconnect();
+
+ ALOGI("ProCamera %d: Closed", mCameraId);
+}
+
+status_t ProCamera2Client::exclusiveTryLock() {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+ SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+ if (!mExclusiveLock) {
+ mExclusiveLock = true;
+
+ if (mRemoteCallback != NULL) {
+ mRemoteCallback->onLockStatusChanged(
+ IProCameraCallbacks::LOCK_ACQUIRED);
+ }
+
+ ALOGV("%s: exclusive lock acquired", __FUNCTION__);
+
+ return OK;
+ }
+
+ // TODO: have a PERMISSION_DENIED case for when someone else owns the lock
+
+ // don't allow recursive locking
+ ALOGW("%s: exclusive lock already exists - recursive locking is not"
+ "allowed", __FUNCTION__);
+
+ return ALREADY_EXISTS;
+}
+
+status_t ProCamera2Client::exclusiveLock() {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+ SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+ /**
+ * TODO: this should asynchronously 'wait' until the lock becomes available
+ * if another client already has an exclusive lock.
+ *
+ * once we have proper sharing support this will need to do
+ * more than just return immediately
+ */
+ if (!mExclusiveLock) {
+ mExclusiveLock = true;
+
+ if (mRemoteCallback != NULL) {
+ mRemoteCallback->onLockStatusChanged(IProCameraCallbacks::LOCK_ACQUIRED);
+ }
+
+ ALOGV("%s: exclusive lock acquired", __FUNCTION__);
+
+ return OK;
+ }
+
+ // don't allow recursive locking
+ ALOGW("%s: exclusive lock already exists - recursive locking is not allowed"
+ , __FUNCTION__);
+ return ALREADY_EXISTS;
+}
+
+status_t ProCamera2Client::exclusiveUnlock() {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+ SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+ // don't allow unlocking if we have no lock
+ if (!mExclusiveLock) {
+ ALOGW("%s: cannot unlock, no lock was held in the first place",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ mExclusiveLock = false;
+ if (mRemoteCallback != NULL ) {
+ mRemoteCallback->onLockStatusChanged(
+ IProCameraCallbacks::LOCK_RELEASED);
+ }
+ ALOGV("%s: exclusive lock released", __FUNCTION__);
+
+ return OK;
+}
+
+bool ProCamera2Client::hasExclusiveLock() {
+ return mExclusiveLock;
+}
+
+status_t ProCamera2Client::submitRequest(camera_metadata_t* request,
+ bool streaming) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+ if (!mExclusiveLock) {
+ return PERMISSION_DENIED;
+ }
+
+ CameraMetadata metadata(request);
+
+ if (streaming) {
+ return mDevice->setStreamingRequest(metadata);
+ } else {
+ return mDevice->capture(metadata);
+ }
+
+ // unreachable. thx gcc for a useless warning
+ return OK;
+}
+
+status_t ProCamera2Client::cancelRequest(int requestId) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+ if (!mExclusiveLock) {
+ return PERMISSION_DENIED;
+ }
+
+ ALOGE("%s: not fully implemented yet", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+status_t ProCamera2Client::requestStream(int streamId) {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+
+ return INVALID_OPERATION;
+}
+
+status_t ProCamera2Client::cancelStream(int streamId) {
+ ATRACE_CALL();
+ ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+
+ mDevice->clearStreamingRequest();
+
+ status_t code;
+ if ((code = mDevice->waitUntilDrained()) != OK) {
+ ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code);
+ }
+
+ return mDevice->deleteStream(streamId);
+}
+
+status_t ProCamera2Client::createStream(int width, int height, int format,
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ /*out*/
+ int* streamId)
+{
+ if (streamId) {
+ *streamId = -1;
+ }
+
+ ATRACE_CALL();
+ ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+
+ sp<IBinder> binder;
+ sp<ANativeWindow> window;
+ if (bufferProducer != 0) {
+ binder = bufferProducer->asBinder();
+ window = new Surface(bufferProducer);
+ }
+
+ return mDevice->createStream(window, width, height, format, /*size*/1,
+ streamId);
+}
+
+// Create a request object from a template.
+// -- Caller owns the newly allocated metadata
+status_t ProCamera2Client::createDefaultRequest(int templateId,
+ /*out*/
+ camera_metadata** request)
+{
+ ATRACE_CALL();
+ ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId);
+
+ if (request) {
+ *request = NULL;
+ }
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+
+ CameraMetadata metadata;
+ if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) {
+ *request = metadata.release();
+ }
+
+ return res;
+}
+
+status_t ProCamera2Client::getCameraInfo(int cameraId,
+ /*out*/
+ camera_metadata** info)
+{
+ if (cameraId != mCameraId) {
+ return INVALID_OPERATION;
+ }
+
+ CameraMetadata deviceInfo = mDevice->info();
+ *info = deviceInfo.release();
+
+ return OK;
+}
+
+status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) {
+ String8 result;
+ result.appendFormat("ProCamera2Client[%d] (%p) PID: %d, dump:\n",
+ mCameraId,
+ getRemoteCallback()->asBinder().get(),
+ mClientPid);
+ result.append(" State: ");
+
+ // TODO: print dynamic/request section from most recent requests
+ mFrameProcessor->dump(fd, args);
+
+#define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break;
+
+ result = " Device dump:\n";
+ write(fd, result.string(), result.size());
+
+ status_t res = mDevice->dump(fd, args);
+ if (res != OK) {
+ result = String8::format(" Error dumping device: %s (%d)",
+ strerror(-res), res);
+ write(fd, result.string(), result.size());
+ }
+
+#undef CASE_APPEND_ENUM
+ return NO_ERROR;
+}
+
+// IProCameraUser interface
+
+void ProCamera2Client::disconnect() {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mIProCameraUserLock);
+ status_t res;
+
+ // Allow both client and the media server to disconnect at all times
+ int callingPid = getCallingPid();
+ if (callingPid != mClientPid && callingPid != mServicePid) return;
+
+ if (mDevice == 0) return;
+
+ ALOGV("Camera %d: Shutting down", mCameraId);
+ mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+ FRAME_PROCESSOR_LISTENER_MAX_ID,
+ /*listener*/this);
+ mFrameProcessor->requestExit();
+ ALOGV("Camera %d: Waiting for threads", mCameraId);
+ mFrameProcessor->join();
+ ALOGV("Camera %d: Disconnecting device", mCameraId);
+
+ mDevice->disconnect();
+
+ mDevice.clear();
+
+ ProClient::disconnect();
+}
+
+status_t ProCamera2Client::connect(const sp<IProCameraCallbacks>& client) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mIProCameraUserLock);
+
+ if (mClientPid != 0 && getCallingPid() != mClientPid) {
+ ALOGE("%s: Camera %d: Connection attempt from pid %d; "
+ "current locked to pid %d", __FUNCTION__,
+ mCameraId, getCallingPid(), mClientPid);
+ return BAD_VALUE;
+ }
+
+ mClientPid = getCallingPid();
+
+ mRemoteCallback = client;
+ mSharedCameraCallbacks = client;
+
+ return OK;
+}
+
+/** Device-related methods */
+
+void ProCamera2Client::notifyError(int errorCode, int arg1, int arg2) {
+ ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode,
+ arg1, arg2);
+}
+
+void ProCamera2Client::notifyShutter(int frameNumber, nsecs_t timestamp) {
+ ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__,
+ frameNumber, timestamp);
+}
+
+void ProCamera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
+ ALOGV("%s: Autofocus state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+
+ SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+ if (l.mRemoteCallback != 0) {
+ l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE,
+ 1, 0);
+ }
+ if (l.mRemoteCallback != 0) {
+ l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS,
+ 1, 0);
+ }
+}
+
+void ProCamera2Client::notifyAutoExposure(uint8_t newState, int triggerId) {
+ ALOGV("%s: Autoexposure state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+}
+
+void ProCamera2Client::notifyAutoWhitebalance(uint8_t newState, int triggerId) {
+ ALOGV("%s: Auto-whitebalance state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+}
+
+int ProCamera2Client::getCameraId() const {
+ return mCameraId;
+}
+
+const sp<Camera2Device>& ProCamera2Client::getCameraDevice() {
+ return mDevice;
+}
+
+const sp<CameraService>& ProCamera2Client::getCameraService() {
+ return mCameraService;
+}
+
+ProCamera2Client::SharedCameraCallbacks::Lock::Lock(
+ SharedCameraCallbacks &client):
+ mRemoteCallback(client.mRemoteCallback),
+ mSharedClient(client) {
+ mSharedClient.mRemoteCallbackLock.lock();
+}
+
+ProCamera2Client::SharedCameraCallbacks::Lock::~Lock() {
+ mSharedClient.mRemoteCallbackLock.unlock();
+}
+
+ProCamera2Client::SharedCameraCallbacks::SharedCameraCallbacks
+ (const sp<IProCameraCallbacks>&client):
+ mRemoteCallback(client) {
+}
+
+ProCamera2Client::SharedCameraCallbacks&
+ ProCamera2Client::SharedCameraCallbacks::operator=(
+ const sp<IProCameraCallbacks>&client) {
+ Mutex::Autolock l(mRemoteCallbackLock);
+ mRemoteCallback = client;
+ return *this;
+}
+
+void ProCamera2Client::SharedCameraCallbacks::clear() {
+ Mutex::Autolock l(mRemoteCallbackLock);
+ mRemoteCallback.clear();
+}
+
+void ProCamera2Client::onFrameAvailable(int32_t frameId,
+ const CameraMetadata& frame) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+ SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+ if (mRemoteCallback != NULL) {
+ CameraMetadata tmp(frame);
+ camera_metadata_t* meta = tmp.release();
+ ALOGV("%s: meta = %p ", __FUNCTION__, meta);
+ mRemoteCallback->onResultReceived(frameId, meta);
+ tmp.acquire(meta);
+ }
+
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/ProCamera2Client.h b/services/camera/libcameraservice/ProCamera2Client.h
new file mode 100644
index 0000000..cd0a2ae
--- /dev/null
+++ b/services/camera/libcameraservice/ProCamera2Client.h
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
+#define ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
+
+#include "Camera2Device.h"
+#include "CameraService.h"
+#include "camera2/ProFrameProcessor.h"
+
+namespace android {
+
+class IMemory;
+/**
+ * Implements the binder IProCameraUser API,
+ * meant for HAL2-level private API access.
+ */
+class ProCamera2Client :
+ public CameraService::ProClient,
+ public Camera2Device::NotificationListener,
+ public camera2::ProFrameProcessor::FilteredListener
+{
+public:
+ /**
+ * IProCameraUser interface (see IProCameraUser for details)
+ */
+ virtual status_t connect(const sp<IProCameraCallbacks>& callbacks);
+ virtual void disconnect();
+
+ virtual status_t exclusiveTryLock();
+ virtual status_t exclusiveLock();
+ virtual status_t exclusiveUnlock();
+
+ virtual bool hasExclusiveLock();
+
+ // Note that the callee gets a copy of the metadata.
+ virtual int submitRequest(camera_metadata_t* metadata,
+ bool streaming = false);
+ virtual status_t cancelRequest(int requestId);
+
+ virtual status_t requestStream(int streamId);
+ virtual status_t cancelStream(int streamId);
+
+ virtual status_t createStream(int width, int height, int format,
+ const sp<IGraphicBufferProducer>& bufferProducer,
+ /*out*/
+ int* streamId);
+
+ // Create a request object from a template.
+ // -- Caller owns the newly allocated metadata
+ virtual status_t createDefaultRequest(int templateId,
+ /*out*/
+ camera_metadata** request);
+
+ // Get the static metadata for the camera
+ // -- Caller owns the newly allocated metadata
+ virtual status_t getCameraInfo(int cameraId,
+ /*out*/
+ camera_metadata** info);
+
+ /**
+ * Interface used by CameraService
+ */
+
+ ProCamera2Client(const sp<CameraService>& cameraService,
+ const sp<IProCameraCallbacks>& remoteCallback,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid);
+ virtual ~ProCamera2Client();
+
+ status_t initialize(camera_module_t *module);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ /**
+ * Interface used by Camera2Device
+ */
+
+ virtual void notifyError(int errorCode, int arg1, int arg2);
+ virtual void notifyShutter(int frameNumber, nsecs_t timestamp);
+ virtual void notifyAutoFocus(uint8_t newState, int triggerId);
+ virtual void notifyAutoExposure(uint8_t newState, int triggerId);
+ virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId);
+
+
+ int getCameraId() const;
+ const sp<Camera2Device>& getCameraDevice();
+ const sp<CameraService>& getCameraService();
+
+ /**
+ * Interface used by independent components of ProCamera2Client.
+ */
+
+ // Simple class to ensure that access to IProCameraCallbacks is serialized
+ // by requiring mRemoteCallbackLock to be locked before access to
+ // mCameraClient is possible.
+ class SharedCameraCallbacks {
+ public:
+ class Lock {
+ public:
+ Lock(SharedCameraCallbacks &client);
+ ~Lock();
+ sp<IProCameraCallbacks> &mRemoteCallback;
+ private:
+ SharedCameraCallbacks &mSharedClient;
+ };
+ SharedCameraCallbacks(const sp<IProCameraCallbacks>& client);
+ SharedCameraCallbacks& operator=(const sp<IProCameraCallbacks>& client);
+ void clear();
+ private:
+ sp<IProCameraCallbacks> mRemoteCallback;
+ mutable Mutex mRemoteCallbackLock;
+ } mSharedCameraCallbacks;
+
+protected:
+ /** FilteredListener implementation **/
+ virtual void onFrameAvailable(int32_t frameId, const CameraMetadata& frame);
+
+private:
+ /** IProCameraUser interface-related private members */
+
+ // Mutex that must be locked by methods implementing the IProCameraUser
+ // interface. Ensures serialization between incoming IProCameraUser calls.
+ // All methods below that append 'L' to the name assume that
+ // mIProCameraUserLock is locked when they're called
+ mutable Mutex mIProCameraUserLock;
+
+ // Used with stream IDs
+ static const int NO_STREAM = -1;
+
+ /* Preview/Recording related members */
+
+ sp<IBinder> mPreviewSurface;
+
+ /** Preview callback related members */
+ sp<camera2::ProFrameProcessor> mFrameProcessor;
+ static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
+ static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
+
+ /** Camera2Device instance wrapping HAL2 entry */
+
+ sp<Camera2Device> mDevice;
+
+ /** Utility members */
+
+ // Verify that caller is the owner of the camera
+ status_t checkPid(const char *checkLocation) const;
+
+ // Whether or not we have an exclusive lock on the device
+ // - if no we can't modify the request queue.
+ // note that creating/deleting streams we own is still OK
+ bool mExclusiveLock;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.h b/services/camera/libcameraservice/camera2/BurstCapture.h
index dfc45eb..a2cc893 100644
--- a/services/camera/libcameraservice/camera2/BurstCapture.h
+++ b/services/camera/libcameraservice/camera2/BurstCapture.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
#define ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
-#include "camera2/CameraMetadata.h"
+#include "camera/CameraMetadata.h"
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <gui/CpuConsumer.h>
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.h b/services/camera/libcameraservice/camera2/CallbackProcessor.h
index c2a1372..e68bb75 100644
--- a/services/camera/libcameraservice/camera2/CallbackProcessor.h
+++ b/services/camera/libcameraservice/camera2/CallbackProcessor.h
@@ -24,7 +24,7 @@
#include <utils/Condition.h>
#include <gui/CpuConsumer.h>
#include "Parameters.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
#include "Camera2Heap.h"
namespace android {
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.h b/services/camera/libcameraservice/camera2/CaptureSequencer.h
index c42df05..7db8007 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.h
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.h
@@ -23,7 +23,7 @@
#include <utils/Vector.h>
#include <utils/Mutex.h>
#include <utils/Condition.h>
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
#include "Parameters.h"
#include "FrameProcessor.h"
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.h b/services/camera/libcameraservice/camera2/FrameProcessor.h
index 3bd4e25..66e3cda 100644
--- a/services/camera/libcameraservice/camera2/FrameProcessor.h
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.h
@@ -22,7 +22,7 @@
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
struct camera_frame_metadata;
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/camera2/JpegProcessor.h
index 836bd02..2283f28 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.h
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.h
@@ -24,7 +24,7 @@
#include <utils/Condition.h>
#include <gui/CpuConsumer.h>
#include "Parameters.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
namespace android {
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
index 9f5f03b..fe3ec1d 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -25,8 +25,7 @@
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include <camera/CameraParameters.h>
-
-#include "CameraMetadata.h"
+#include <camera/CameraMetadata.h>
namespace android {
namespace camera2 {
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp b/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp
new file mode 100644
index 0000000..8d4933c
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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_TAG "Camera2-ProFrameProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "ProFrameProcessor.h"
+#include "../Camera2Device.h"
+#include "../ProCamera2Client.h"
+
+namespace android {
+namespace camera2 {
+
+ProFrameProcessor::ProFrameProcessor(wp<ProCamera2Client> client):
+ Thread(false), mClient(client) {
+}
+
+ProFrameProcessor::~ProFrameProcessor() {
+ ALOGV("%s: Exit", __FUNCTION__);
+}
+
+status_t ProFrameProcessor::registerListener(int32_t minId,
+ int32_t maxId, wp<FilteredListener> listener) {
+ Mutex::Autolock l(mInputMutex);
+ ALOGV("%s: Registering listener for frame id range %d - %d",
+ __FUNCTION__, minId, maxId);
+ RangeListener rListener = { minId, maxId, listener };
+ mRangeListeners.push_back(rListener);
+ return OK;
+}
+
+status_t ProFrameProcessor::removeListener(int32_t minId,
+ int32_t maxId, wp<FilteredListener> listener) {
+ Mutex::Autolock l(mInputMutex);
+ List<RangeListener>::iterator item = mRangeListeners.begin();
+ while (item != mRangeListeners.end()) {
+ if (item->minId == minId &&
+ item->maxId == maxId &&
+ item->listener == listener) {
+ item = mRangeListeners.erase(item);
+ } else {
+ item++;
+ }
+ }
+ return OK;
+}
+
+void ProFrameProcessor::dump(int fd, const Vector<String16>& args) {
+ String8 result(" Latest received frame:\n");
+ write(fd, result.string(), result.size());
+ mLastFrame.dump(fd, 2, 6);
+}
+
+bool ProFrameProcessor::threadLoop() {
+ status_t res;
+
+ sp<Camera2Device> device;
+ {
+ sp<ProCamera2Client> client = mClient.promote();
+ if (client == 0) return false;
+ device = client->getCameraDevice();
+ if (device == 0) return false;
+ }
+
+ res = device->waitForNextFrame(kWaitDuration);
+ if (res == OK) {
+ sp<ProCamera2Client> client = mClient.promote();
+ if (client == 0) return false;
+ processNewFrames(client);
+ } else if (res != TIMED_OUT) {
+ ALOGE("ProCamera2Client::ProFrameProcessor: Error waiting for new "
+ "frames: %s (%d)", strerror(-res), res);
+ }
+
+ return true;
+}
+
+void ProFrameProcessor::processNewFrames(sp<ProCamera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+ CameraMetadata frame;
+ while ( (res = client->getCameraDevice()->getNextFrame(&frame)) == OK) {
+ camera_metadata_entry_t entry;
+
+ entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Error reading frame number",
+ __FUNCTION__, client->getCameraId());
+ break;
+ }
+ ATRACE_INT("cam2_frame", entry.data.i32[0]);
+
+ res = processListeners(frame, client);
+ if (res != OK) break;
+
+ if (!frame.isEmpty()) {
+ mLastFrame.acquire(frame);
+ }
+ }
+ if (res != NOT_ENOUGH_DATA) {
+ ALOGE("%s: Camera %d: Error getting next frame: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return;
+ }
+
+ return;
+}
+
+status_t ProFrameProcessor::processListeners(const CameraMetadata &frame,
+ sp<ProCamera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+ camera_metadata_ro_entry_t entry;
+
+ entry = frame.find(ANDROID_REQUEST_ID);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Error reading frame id",
+ __FUNCTION__, client->getCameraId());
+ return BAD_VALUE;
+ }
+ int32_t frameId = entry.data.i32[0];
+
+ List<sp<FilteredListener> > listeners;
+ {
+ Mutex::Autolock l(mInputMutex);
+
+ List<RangeListener>::iterator item = mRangeListeners.begin();
+ while (item != mRangeListeners.end()) {
+ if (frameId >= item->minId &&
+ frameId < item->maxId) {
+ sp<FilteredListener> listener = item->listener.promote();
+ if (listener == 0) {
+ item = mRangeListeners.erase(item);
+ continue;
+ } else {
+ listeners.push_back(listener);
+ }
+ }
+ item++;
+ }
+ }
+ ALOGV("Got %d range listeners out of %d", listeners.size(), mRangeListeners.size());
+ List<sp<FilteredListener> >::iterator item = listeners.begin();
+ for (; item != listeners.end(); item++) {
+ (*item)->onFrameAvailable(frameId, frame);
+ }
+ return OK;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.h b/services/camera/libcameraservice/camera2/ProFrameProcessor.h
new file mode 100644
index 0000000..e4094a6
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/ProFrameProcessor.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERA2_PROFRAMEPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_PROFRAMEPROCESSOR_H
+
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <camera/CameraMetadata.h>
+
+struct camera_frame_metadata;
+
+namespace android {
+
+class ProCamera2Client;
+
+namespace camera2 {
+
+/* Output frame metadata processing thread. This thread waits for new
+ * frames from the device, and analyzes them as necessary.
+ */
+class ProFrameProcessor: public Thread {
+ public:
+ ProFrameProcessor(wp<ProCamera2Client> client);
+ ~ProFrameProcessor();
+
+ struct FilteredListener: virtual public RefBase {
+ virtual void onFrameAvailable(int32_t frameId,
+ const CameraMetadata &frame) = 0;
+ };
+
+ // Register a listener for a range of IDs [minId, maxId). Multiple listeners
+ // can be listening to the same range
+ status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+ status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+
+ void dump(int fd, const Vector<String16>& args);
+ private:
+ static const nsecs_t kWaitDuration = 10000000; // 10 ms
+ wp<ProCamera2Client> mClient;
+
+ virtual bool threadLoop();
+
+ Mutex mInputMutex;
+
+ struct RangeListener {
+ int32_t minId;
+ int32_t maxId;
+ wp<FilteredListener> listener;
+ };
+ List<RangeListener> mRangeListeners;
+
+ void processNewFrames(sp<ProCamera2Client> &client);
+
+ status_t processListeners(const CameraMetadata &frame,
+ sp<ProCamera2Client> &client);
+
+ CameraMetadata mLastFrame;
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.h b/services/camera/libcameraservice/camera2/StreamingProcessor.h
index 96b100f..e5732ad 100644
--- a/services/camera/libcameraservice/camera2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.h
@@ -22,7 +22,7 @@
#include <gui/BufferItemConsumer.h>
#include "Parameters.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
namespace android {
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.h b/services/camera/libcameraservice/camera2/ZslProcessor.h
index c80e7f4..ec16eef 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor.h
+++ b/services/camera/libcameraservice/camera2/ZslProcessor.h
@@ -25,7 +25,7 @@
#include <gui/BufferItemConsumer.h>
#include "Parameters.h"
#include "FrameProcessor.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
#include "Camera2Heap.h"
#include "../Camera2Device.h"