From c0767f148e29ce821281b5965c0e25b4c143e76d Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Wed, 20 Feb 2013 19:29:53 -0800 Subject: Camera: ProCamera - add createStreamCpu and unit test Change-Id: I468172dbfdd78510b273bf9d119c950cbeda7ea3 --- camera/ProCamera.cpp | 111 +++++++++++++++++++------ camera/tests/ProCameraTests.cpp | 175 +++++++++++++++++++++++++++++++++------- 2 files changed, 235 insertions(+), 51 deletions(-) (limited to 'camera') diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp index 8fd08f4..5ee0e4d 100644 --- a/camera/ProCamera.cpp +++ b/camera/ProCamera.cpp @@ -246,19 +246,16 @@ status_t ProCamera::cancelRequest(int requestId) return c->cancelRequest(requestId); } -status_t ProCamera::requestStream(int streamId) +status_t ProCamera::deleteStream(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; + status_t s = c->cancelStream(streamId); + + mStreams.removeItem(streamId); - return c->cancelStream(streamId); + return s; } status_t ProCamera::createStream(int width, int height, int format, @@ -275,38 +272,76 @@ status_t ProCamera::createStream(int width, int height, int format, return BAD_VALUE; } - sp c = mCamera; - if (c == 0) return NO_INIT; - - return c->createStream(width, height, format, surface->getIGraphicBufferProducer(), - streamId); + return createStream(width, height, format, surface->getIGraphicBufferProducer(), + streamId); } status_t ProCamera::createStream(int width, int height, int format, const sp& bufferProducer, /*out*/ int* streamId) { + *streamId = -1; ALOGV("%s: createStreamT %dx%d (fmt=0x%x)", __FUNCTION__, width, height, format); - sp binder; - status_t stat = INVALID_OPERATION; + if (bufferProducer == 0) { + return BAD_VALUE; + } - if (bufferProducer != 0) { - sp c = mCamera; - if (c == 0) return NO_INIT; + sp c = mCamera; + status_t stat = c->createStream(width, height, format, bufferProducer, + streamId); - return c->createStream(width, height, format, bufferProducer, streamId); - } - else { - *streamId = -1; - return BAD_VALUE; + 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*/ + int* streamId) +{ + ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height, + format); + + sp c = mCamera; + if (c == 0) return NO_INIT; + + sp cc = new CpuConsumer(heapCount); + cc->setName(String8("ProCamera::mCpuConsumer")); + + sp 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 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); + + return s; +} + int ProCamera::getNumberOfCameras() { ALOGE("%s: not implemented yet", __FUNCTION__); return 1; @@ -329,4 +364,34 @@ status_t ProCamera::createDefaultRequest(int templateId, return c->createDefaultRequest(templateId, request); } +void ProCamera::onFrameAvailable(int streamId) { + ALOGV("%s: streamId = %d", __FUNCTION__, streamId); + + sp listener = mListener; + if (listener.get() != NULL) { + StreamInfo& stream = getStreamInfo(streamId); + + CpuConsumer::LockedBuffer buf; + + status_t stat = stream.cpuConsumer->lockNextBuffer(&buf); + if (stat != OK) { + ALOGE("%s: Failed to lock buffer, error code = %d", __FUNCTION__, + stat); + return; + } + + listener->onBufferReceived(streamId, buf); + stat = stream.cpuConsumer->unlockBuffer(buf); + + if (stat != OK) { + ALOGE("%s: Failed to unlock buffer, error code = %d", __FUNCTION__, + stat); + } + } +} + +ProCamera::StreamInfo& ProCamera::getStreamInfo(int streamId) { + return mStreams.editValueFor(streamId); +} + }; // namespace android diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp index 230e160..f0a36e8 100644 --- a/camera/tests/ProCameraTests.cpp +++ b/camera/tests/ProCameraTests.cpp @@ -41,7 +41,12 @@ namespace client { #define TEST_DEBUGGING 0 #define TEST_LISTENER_TIMEOUT 1000000000 // 1 second listener timeout -#define TEST_FORMAT HAL_PIXEL_FORMAT_RGBA_8888 //TODO: YUY2 instead +#define TEST_FORMAT HAL_PIXEL_FORMAT_Y16 //TODO: YUY2 instead + +#define TEST_FORMAT_DEPTH HAL_PIXEL_FORMAT_Y16 + +#define TEST_CPU_FRAME_COUNT 2 +#define TEST_CPU_HEAP_COUNT 5 #if TEST_DEBUGGING #define dout std::cerr @@ -54,14 +59,15 @@ namespace client { class ProCameraTest; -enum LockEvent { +enum ProEvent { UNKNOWN, ACQUIRED, RELEASED, - STOLEN + STOLEN, + BUFFER_RECEIVED, }; -typedef Vector EventList; +typedef Vector EventList; class ProCameraTestThread : public Thread { @@ -92,7 +98,7 @@ public: { Mutex::Autolock al(mListenerMutex); - if (mLockEventList.size() > 0) { + if (mProEventList.size() > 0) { return OK; } } @@ -105,35 +111,35 @@ public: void ReadEvents(EventList& out) { Mutex::Autolock al(mListenerMutex); - for (size_t i = 0; i < mLockEventList.size(); ++i) { - out.push(mLockEventList[i]); + for (size_t i = 0; i < mProEventList.size(); ++i) { + out.push(mProEventList[i]); } - mLockEventList.clear(); + mProEventList.clear(); } /** * Dequeue 1 event from the event queue. * Returns UNKNOWN if queue is empty */ - LockEvent ReadEvent() { + ProEvent ReadEvent() { Mutex::Autolock al(mListenerMutex); - if (mLockEventList.size() == 0) { + if (mProEventList.size() == 0) { return UNKNOWN; } - LockEvent ev = mLockEventList[0]; - mLockEventList.removeAt(0); + ProEvent ev = mProEventList[0]; + mProEventList.removeAt(0); return ev; } private: - void QueueEvent(LockEvent ev) { + void QueueEvent(ProEvent ev) { { Mutex::Autolock al(mListenerMutex); - mLockEventList.push(ev); + mProEventList.push(ev); } @@ -168,6 +174,20 @@ protected: << " " << ext3 << std::endl; } + virtual void onBufferReceived(int streamId, + const CpuConsumer::LockedBuffer& buf) { + + dout << "Buffer received on streamId = " << streamId << + ", dataPtr = " << (void*)buf.data << std::endl; + + QueueEvent(BUFFER_RECEIVED); + + } + virtual void onRequestReceived( + camera_metadata* request) { + free_camera_metadata(request); + } + // TODO: remove virtual void notify(int32_t , int32_t , int32_t ) {} @@ -176,7 +196,7 @@ protected: virtual void postDataTimestamp(nsecs_t , int32_t , const sp& ) {} - Vector mLockEventList; + Vector mProEventList; Mutex mListenerMutex; Mutex mConditionMutex; Condition mListenerCondition; @@ -217,6 +237,9 @@ protected: sp mComposerClient; sp mSurfaceControl; + sp mDepthComposerClient; + sp mDepthSurfaceControl; + int getSurfaceWidth() { return 512; } @@ -233,6 +256,8 @@ protected: getSurfaceWidth(), getSurfaceHeight(), PIXEL_FORMAT_RGB_888, 0); + mSurfaceControl->setPosition(640, 0); + ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); @@ -247,6 +272,31 @@ protected: ASSERT_NE((void*)NULL, surface.get()); } + void createDepthOnScreenSurface(sp& 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 window = mDepthSurfaceControl->getSurface(); + surface = mDepthSurfaceControl->getSurface(); + + ASSERT_NE((void*)NULL, surface.get()); + } + }; sp ProCameraTest::mTestThread; @@ -316,14 +366,15 @@ TEST_F(ProCameraTest, StreamingImage) { mDisplaySecs = 0; } - sp surface; + sp depthSurface; if (mDisplaySecs > 0) { - createOnScreenSurface(/*out*/surface); + createDepthOnScreenSurface(/*out*/depthSurface); } - int streamId = -1; - EXPECT_OK(mCamera->createStream(/*width*/640, /*height*/480, TEST_FORMAT, - 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. @@ -345,23 +396,26 @@ TEST_F(ProCameraTest, StreamingImage) { // wow what a verbose API. // i would give a loaf of bread for // metadata->updateOrInsert(keys.request.output.streams, streamId); + uint8_t allStreams[] = { depthStreamId }; + size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]); + camera_metadata_entry_t entry; uint32_t tag = static_cast(ANDROID_REQUEST_OUTPUT_STREAMS); int find = find_camera_metadata_entry(request, tag, &entry); if (find == -ENOENT) { - if (add_camera_metadata_entry(request, tag, &streamId, /*data_count*/1) - != OK) { + 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, &streamId, - /*data_count*/1)); + ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams, + /*data_count*/streamCount)); } } else { - ASSERT_OK(update_camera_metadata_entry(request, entry.index, &streamId, - /*data_count*/1, &entry)); + ASSERT_OK(update_camera_metadata_entry(request, entry.index, + &allStreams, /*data_count*/streamCount, &entry)); } EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true)); @@ -370,7 +424,72 @@ TEST_F(ProCameraTest, StreamingImage) { sleep(mDisplaySecs); free_camera_metadata(request); - EXPECT_OK(mCamera->cancelStream(streamId)); + + for (int i = 0; i < streamCount; ++i) { + EXPECT_OK(mCamera->deleteStream(allStreams[i])); + } + EXPECT_OK(mCamera->exclusiveUnlock()); +} + +TEST_F(ProCameraTest, CpuConsumer) { + if (HasFatalFailure()) { + return; + } + int streamId = -1; + EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240, + TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &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(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()); } -- cgit v1.1