From 634a51509ee50475f3e9f8ccf897e90fc72ded31 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Wed, 20 Feb 2013 17:15:11 -0800 Subject: Camera: Add ProCamera private binder interface for an API2-light functionality Change-Id: I2af7a807c99df75ea659e6e6acc9c4fca6a56274 --- camera/Android.mk | 5 +- camera/Camera.cpp | 3 +- camera/ICameraService.cpp | 18 +++ camera/IProCameraCallbacks.cpp | 144 ++++++++++++++++++++++ camera/IProCameraUser.cpp | 264 ++++++++++++++++++++++++++++++++++++++++ camera/ProCamera.cpp | 230 ++++++++++++++++++++++++++++++++++ camera/tests/Android.mk | 1 + camera/tests/ProCameraTests.cpp | 68 +++++++++++ 8 files changed, 731 insertions(+), 2 deletions(-) create mode 100644 camera/IProCameraCallbacks.cpp create mode 100644 camera/IProCameraUser.cpp create mode 100644 camera/ProCamera.cpp create mode 100644 camera/tests/ProCameraTests.cpp (limited to 'camera') diff --git a/camera/Android.mk b/camera/Android.mk index a17ad1a..3e7e5a5 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -12,7 +12,10 @@ LOCAL_SRC_FILES:= \ ICameraClient.cpp \ ICameraService.cpp \ ICameraRecordingProxy.cpp \ - ICameraRecordingProxyListener.cpp + ICameraRecordingProxyListener.cpp \ + IProCameraUser.cpp \ + IProCameraCallbacks.cpp \ + ProCamera.cpp \ LOCAL_SHARED_LIBRARIES := \ libcutils \ 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 @@ sp Camera::connect(int cameraId) { ALOGV("connect"); sp c = new Camera(); + sp cl = c; const sp& 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/camera/ICameraService.cpp b/camera/ICameraService.cpp index f2d367e..8237c66 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -65,6 +65,17 @@ public: remote()->transact(BnCameraService::CONNECT, data, &reply); return interface_cast(reply.readStrongBinder()); } + + // connect to camera service (pro client) + virtual sp connect(const sp& 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(reply.readStrongBinder()); + } }; IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService"); @@ -97,6 +108,13 @@ status_t BnCameraService::onTransact( reply->writeStrongBinder(camera->asBinder()); return NO_ERROR; } break; + case CONNECT_PRO: { + CHECK_INTERFACE(ICameraService, data, reply); + sp cameraClient = interface_cast(data.readStrongBinder()); + sp 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..c2ad74f --- /dev/null +++ b/camera/IProCameraCallbacks.cpp @@ -0,0 +1,144 @@ +/* +** +** 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 +#include +#include + +#include +#include +#include +#include + +#include + +namespace android { + +enum { + NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, + DATA_CALLBACK, + DATA_CALLBACK_TIMESTAMP, +}; + +class BpProCameraCallbacks: public BpInterface +{ +public: + BpProCameraCallbacks(const sp& impl) + : BpInterface(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& 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& 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); + } +}; + +IMPLEMENT_META_INTERFACE(ProCameraCallbacks, + "android.hardware.IProCameraCallbacks"); + +// ---------------------------------------------------------------------- + +status_t BnProCameraCallbacks::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + 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 imageData = interface_cast( + 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 imageData = interface_cast( + data.readStrongBinder()); + dataCallbackTimestamp(timestamp, msgType, imageData); + 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..76c2dcd --- /dev/null +++ b/camera/IProCameraUser.cpp @@ -0,0 +1,264 @@ +/* +** +** 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 +#include +#include +#include +#include +#include +#include +#include + +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, +}; + +class BpProCameraUser: public BpInterface +{ +public: + BpProCameraUser(const sp& impl) + : BpInterface(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& 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 = metadataSize (int32) + size_t metadataSize = get_camera_metadata_compact_size(metadata); + data.writeInt32(static_cast(metadataSize)); + + // arg1 = metadata (blob) + WritableBlob blob; + { + data.writeBlob(metadataSize, &blob); + copy_camera_metadata(blob.data(), metadataSize, metadata); + } + blob.release(); + + // 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(); + } + +}; + +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 cameraClient = + interface_cast(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; + + // arg0 = metadataSize (int32) + size_t metadataSize = static_cast(data.readInt32()); + + // 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(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(); + + // 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; + 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..134a4a3 --- /dev/null +++ b/camera/ProCamera.cpp @@ -0,0 +1,230 @@ +/* +** +** 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 +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace android { + +// client singleton for camera service binder interface +Mutex ProCamera::mLock; +sp ProCamera::mCameraService; +sp ProCamera::mDeathNotifier; + +// establish binder interface to camera service +const sp& ProCamera::getCameraService() +{ + Mutex::Autolock _l(mLock); + if (mCameraService.get() == 0) { + sp sm = defaultServiceManager(); + sp 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(binder); + } + ALOGE_IF(mCameraService==0, "no CameraService!?"); + return mCameraService; +} + +sp ProCamera::connect(int cameraId) +{ + ALOGV("connect"); + sp c = new ProCamera(); + sp cl = c; + const sp& 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("disconnect"); + if (mCamera != 0) { + mCamera->disconnect(); + mCamera->asBinder()->unlinkToDeath(this); + mCamera = 0; + } +} + +ProCamera::ProCamera() +{ +} + +ProCamera::~ProCamera() +{ + +} + +sp ProCamera::remote() +{ + return mCamera; +} + +void ProCamera::binderDied(const wp& who) { + ALOGW("IProCameraUser died"); + notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, 0); +} + +void ProCamera::DeathNotifier::binderDied(const wp& who) { + ALOGV("binderDied"); + Mutex::Autolock _l(ProCamera::mLock); + ProCamera::mCameraService.clear(); + ALOGW("Camera service died!"); +} + + +// callback from camera service +void ProCamera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) +{ + sp 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& dataPtr, + camera_frame_metadata_t *metadata) +{ + sp 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& dataPtr) +{ + sp 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 */ + +status_t ProCamera::exclusiveTryLock() +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + + return c->exclusiveTryLock(); +} +status_t ProCamera::exclusiveLock() +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + + return c->exclusiveLock(); +} +status_t ProCamera::exclusiveUnlock() +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + + return c->exclusiveUnlock(); +} +bool ProCamera::hasExclusiveLock() +{ + sp 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 c = mCamera; + if (c == 0) return NO_INIT; + + return c->submitRequest(const_cast(metadata), + streaming); +} + +status_t ProCamera::cancelRequest(int requestId) +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + + return c->cancelRequest(requestId); +} + +status_t ProCamera::requestStream(int streamId) +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + + return c->requestStream(streamId); +} +status_t ProCamera::cancelStream(int streamId) +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + + return c->cancelStream(streamId); +} + +}; // namespace android diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk index 586e814..5d386c4 100644 --- a/camera/tests/Android.mk +++ b/camera/tests/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ main.cpp \ + ProCameraTests.cpp \ LOCAL_SHARED_LIBRARIES := \ libutils \ diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp new file mode 100644 index 0000000..4de9c10 --- /dev/null +++ b/camera/tests/ProCameraTests.cpp @@ -0,0 +1,68 @@ +/* + * 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 +#include + +#include "Camera.h" +#include "ProCamera.h" + +namespace android { +namespace camera2 { +namespace tests { +namespace client { + +#define CAMERA_ID 0 +#define TEST_DEBUGGING 0 + +#if TEST_DEBUGGING +#define dout std::cerr +#else +#define dout if (0) std::cerr +#endif + +class ProCameraTest : public ::testing::Test { + + virtual void SetUp() { + mCamera = ProCamera::connect(CAMERA_ID); + ASSERT_NE((void*)NULL, mCamera.get()); + } + + virtual void TearDown() { + ASSERT_NE((void*)NULL, mCamera.get()); + mCamera->disconnect(); + } + +protected: + sp mCamera; +}; + +TEST_F(ProCameraTest, Locking) { + + if (HasFatalFailure()) { + return; + } + + status_t res = mCamera->exclusiveTryLock(); + + EXPECT_EQ(OK, res); +} + +} +} +} +} + -- cgit v1.1