summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/ProCamera.cpp110
-rw-r--r--camera/tests/ProCameraTests.cpp172
-rw-r--r--include/camera/ProCamera.h45
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);