diff options
-rw-r--r-- | camera/ProCamera.cpp | 110 | ||||
-rw-r--r-- | camera/tests/ProCameraTests.cpp | 172 | ||||
-rw-r--r-- | include/camera/ProCamera.h | 45 |
3 files changed, 304 insertions, 23 deletions
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp index c95c4e0..d4a9556 100644 --- a/camera/ProCamera.cpp +++ b/camera/ProCamera.cpp @@ -86,12 +86,13 @@ sp<ProCamera> ProCamera::connect(int cameraId) void ProCamera::disconnect() { - ALOGV("disconnect"); + ALOGV("%s: disconnect", __FUNCTION__); if (mCamera != 0) { mCamera->disconnect(); mCamera->asBinder()->unlinkToDeath(this); mCamera = 0; } + ALOGV("%s: disconnect (done)", __FUNCTION__); } ProCamera::ProCamera() @@ -208,6 +209,19 @@ void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) { 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 { @@ -323,11 +337,14 @@ status_t ProCamera::createStream(int width, int height, int format, 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; @@ -357,6 +374,8 @@ status_t ProCamera::createStreamCpu(int width, int height, int format, cc->setFrameAvailableListener(frameAvailableListener); + *cpuConsumer = cc; + return s; } @@ -399,26 +418,91 @@ void ProCamera::onFrameAvailable(int streamId) { ALOGV("%s: streamId = %d", __FUNCTION__, streamId); sp<ProCameraListener> listener = mListener; - if (listener.get() != NULL) { - StreamInfo& stream = getStreamInfo(streamId); + StreamInfo& stream = getStreamInfo(streamId); - CpuConsumer::LockedBuffer buf; + CpuConsumer::LockedBuffer buf; - status_t stat = stream.cpuConsumer->lockNextBuffer(&buf); - if (stat != OK) { - ALOGE("%s: Failed to lock buffer, error code = %d", __FUNCTION__, - stat); + if (listener.get() != NULL) { + if (listener->useOnFrameAvailable()) { + listener->onFrameAvailable(streamId, stream.cpuConsumer); return; } + } + + // Unblock waitForFrame(id) callers + { + Mutex::Autolock al(mWaitMutex); + getStreamInfo(streamId).frameReady = true; + mWaitCondition.broadcast(); + } +} + +status_t ProCamera::waitForFrameBuffer(int streamId) { + status_t stat = BAD_VALUE; + Mutex::Autolock al(mWaitMutex); - listener->onBufferReceived(streamId, buf); - stat = stream.cpuConsumer->unlockBuffer(buf); + StreamInfo& si = getStreamInfo(streamId); - if (stat != OK) { - ALOGE("%s: Failed to unlock buffer, error code = %d", __FUNCTION__, - stat); + if (si.frameReady) { + si.frameReady = false; + return OK; + } 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) { + si.frameReady = false; + return OK; + } + // else it was some other stream that got unblocked } } + + return stat; +} + +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) { diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp index f1dd48c..33c9179 100644 --- a/camera/tests/ProCameraTests.cpp +++ b/camera/tests/ProCameraTests.cpp @@ -207,7 +207,8 @@ protected: const CpuConsumer::LockedBuffer& buf) { dout << "Buffer received on streamId = " << streamId << - ", dataPtr = " << (void*)buf.data << std::endl; + ", dataPtr = " << (void*)buf.data << + ", timestamp = " << buf.timestamp << std::endl; QueueEvent(BUFFER_RECEIVED); @@ -376,7 +377,7 @@ protected: return false; } - for (int i = 0; i < count; ++i) { + for (size_t i = 0; i < count; ++i) { if (array[i] == needle) { return true; } @@ -410,10 +411,10 @@ protected: * Creating a streaming request for these output streams from a template, * and submit it */ - void createSubmitRequestForStreams(uint8_t* streamIds, size_t count) { + void createSubmitRequestForStreams(uint8_t* streamIds, size_t count, int requestCount=-1) { ASSERT_NE((void*)NULL, streamIds); - ASSERT_LT(0, count); + ASSERT_LT(0u, count); camera_metadata_t *requestTmp = NULL; EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, @@ -427,7 +428,15 @@ protected: request.update(tag, streamIds, count); requestTmp = request.release(); - EXPECT_OK(mCamera->submitRequest(requestTmp, /*streaming*/true)); + + 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); } @@ -628,8 +637,9 @@ TEST_F(ProCameraTest, CpuConsumerSingle) { 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, &streamId)); + TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); EXPECT_NE(-1, streamId); EXPECT_OK(mCamera->exclusiveTryLock()); @@ -693,13 +703,14 @@ TEST_F(ProCameraTest, CpuConsumerDual) { 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, &streamId)); + 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, &depthStreamId)); + TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &depthStreamId)); EXPECT_NE(-1, depthStreamId); EXPECT_OK(mCamera->exclusiveTryLock()); @@ -772,8 +783,9 @@ TEST_F(ProCameraTest, ResultReceiver) { // 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, &streamId)); + TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId)); EXPECT_NE(-1, streamId); EXPECT_OK(mCamera->exclusiveTryLock()); @@ -828,6 +840,148 @@ TEST_F(ProCameraTest, ResultReceiver) { 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_OK(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_OK(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()); +} + + + + + } } } diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h index 11904f9..f813c1c 100644 --- a/include/camera/ProCamera.h +++ b/include/camera/ProCamera.h @@ -24,8 +24,12 @@ #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 { @@ -62,6 +66,20 @@ public: * 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 @@ -161,6 +179,7 @@ public: status_t createStreamCpu(int width, int height, int format, int heapCount, /*out*/ + sp<CpuConsumer>* cpuConsumer, int* streamId); // Create a request object from a template. @@ -174,6 +193,24 @@ public: // 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 + // Error codes: + // -ETIMEDOUT if it took too long to get a frame + status_t 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(); + sp<IProCameraUser> remote(); protected: @@ -249,6 +286,7 @@ private: StreamInfo(int streamId) { this->streamID = streamId; cpuStream = false; + frameReady = false; } StreamInfo() { @@ -261,10 +299,15 @@ private: sp<CpuConsumer> cpuConsumer; sp<ProFrameListener> frameAvailableListener; sp<Surface> stc; + bool frameReady; }; + Condition mWaitCondition; + Mutex mWaitMutex; + static const nsecs_t mWaitTimeout = 1000000000; // 1sec KeyedVector<int, StreamInfo> mStreams; - + bool mMetadataReady; + CameraMetadata mLatestMetadata; void onFrameAvailable(int streamId); |