diff options
author | Igor Murashkin <iam@google.com> | 2013-02-20 19:29:53 -0800 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2013-02-22 10:50:15 -0800 |
commit | 5835cc46a2f06dbfa5fbdab70e091896ef2fb438 (patch) | |
tree | f303abd135f649d5beb940a238b906b4027564f5 | |
parent | 76f8b43909817179b317880202360863b8f976d0 (diff) | |
download | frameworks_av-5835cc46a2f06dbfa5fbdab70e091896ef2fb438.zip frameworks_av-5835cc46a2f06dbfa5fbdab70e091896ef2fb438.tar.gz frameworks_av-5835cc46a2f06dbfa5fbdab70e091896ef2fb438.tar.bz2 |
Camera: ProCamera - add createStreamCpu and unit test
Change-Id: I468172dbfdd78510b273bf9d119c950cbeda7ea3
-rw-r--r-- | camera/ProCamera.cpp | 111 | ||||
-rw-r--r-- | camera/tests/ProCameraTests.cpp | 175 | ||||
-rw-r--r-- | include/camera/ProCamera.h | 86 | ||||
-rw-r--r-- | services/camera/libcameraservice/ProCamera2Client.cpp | 7 |
4 files changed, 316 insertions, 63 deletions
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 <IProCameraUser> c = mCamera; if (c == 0) return NO_INIT; - return c->requestStream(streamId); -} -status_t ProCamera::cancelStream(int streamId) -{ - sp <IProCameraUser> 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 <IProCameraUser> 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<IGraphicBufferProducer>& bufferProducer, /*out*/ int* streamId) { + *streamId = -1; ALOGV("%s: createStreamT %dx%d (fmt=0x%x)", __FUNCTION__, width, height, format); - sp<IBinder> binder; - status_t stat = INVALID_OPERATION; + if (bufferProducer == 0) { + return BAD_VALUE; + } - if (bufferProducer != 0) { - sp <IProCameraUser> c = mCamera; - if (c == 0) return NO_INIT; + sp <IProCameraUser> 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 <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); + + 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<ProCameraListener> 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<LockEvent> EventList; +typedef Vector<ProEvent> 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<IMemory>& ) {} - Vector<LockEvent> mLockEventList; + Vector<ProEvent> mProEventList; Mutex mListenerMutex; Mutex mConditionMutex; Condition mListenerCondition; @@ -217,6 +237,9 @@ protected: sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mSurfaceControl; + sp<SurfaceComposerClient> mDepthComposerClient; + sp<SurfaceControl> 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>& 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()); + } + }; sp<Thread> ProCameraTest::mTestThread; @@ -316,14 +366,15 @@ TEST_F(ProCameraTest, StreamingImage) { mDisplaySecs = 0; } - sp<Surface> surface; + sp<Surface> 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<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, &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<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()); } diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h index 9b763a3..4dda533 100644 --- a/include/camera/ProCamera.h +++ b/include/camera/ProCamera.h @@ -18,17 +18,20 @@ #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 <gui/CpuConsumer.h> struct camera_metadata; namespace android { -// ref-counted object for callbacks +// All callbacks on this class are concurrent +// (they come from separate threads) class ProCameraListener : public CameraListener { public: @@ -42,6 +45,21 @@ public: // 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 + + // TODO: implement in IProCameraCallbacks, ProCamera2Client + + // A new frame buffer has been received for this stream. + // -- This callback only fires for createStreamCpu streams + // -- 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 onRequestReceived(camera_metadata* request) = 0; }; class ProCamera : public BnProCameraCallbacks, public IBinder::DeathRecipient @@ -109,22 +127,15 @@ public: * Lock free. Service maintains counter of streams. */ status_t requestStream(int streamId); - /** - * Ask for a stream to be disabled. - * Lock free. Service maintains counter of streams. - * Errors: BAD_VALUE if unknown stream ID. - */ // TODO: remove requestStream, its useless. -// TODO: rename cancelStream to deleteStream -// can probably do it with a grep/sed - /** - * Ask for a stream to be disabled. - * Lock free. Service maintains counter of streams. + * Delete a stream. + * Lock free. * Errors: BAD_VALUE if unknown stream ID. + * PERMISSION_DENIED if the stream wasn't yours */ - status_t cancelStream(int streamId); + status_t deleteStream(int streamId); /** * Create a new HW stream, whose sink will be the window. @@ -145,6 +156,10 @@ public: const sp<IGraphicBufferProducer>& bufferProducer, /*out*/ int* streamId); + status_t createStreamCpu(int width, int height, int format, + int heapCount, + /*out*/ + int* streamId); // Create a request object from a template. status_t createDefaultRequest(int templateId, @@ -203,6 +218,53 @@ private: 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; + } + + StreamInfo() { + streamID = -1; + cpuStream = false; + } + + int streamID; + bool cpuStream; + sp<CpuConsumer> cpuConsumer; + sp<ProFrameListener> frameAvailableListener; + sp<Surface> stc; + }; + + KeyedVector<int, StreamInfo> mStreams; + + + void onFrameAvailable(int streamId); + + StreamInfo& getStreamInfo(int streamId); + }; diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp index aa02f10..f850034 100644 --- a/services/camera/libcameraservice/ProCamera2Client.cpp +++ b/services/camera/libcameraservice/ProCamera2Client.cpp @@ -234,6 +234,13 @@ status_t ProCamera2Client::cancelStream(int streamId) { 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); } |