/* * 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 #include #include "Camera.h" #include "ProCamera.h" #include #include #include #include #include #include #include // for CAMERA2_TEMPLATE_PREVIEW only namespace android { namespace camera2 { namespace tests { namespace client { #define CAMERA_ID 0 #define TEST_DEBUGGING 0 #define TEST_LISTENER_TIMEOUT 1000000000 // 1 second listener timeout #define TEST_FORMAT HAL_PIXEL_FORMAT_RGBA_8888 //TODO: YUY2 instead #if TEST_DEBUGGING #define dout std::cerr #else #define dout if (0) std::cerr #endif #define EXPECT_OK(x) EXPECT_EQ(OK, (x)) #define ASSERT_OK(x) ASSERT_EQ(OK, (x)) class ProCameraTest; enum LockEvent { UNKNOWN, ACQUIRED, RELEASED, STOLEN }; typedef Vector EventList; class ProCameraTestThread : public Thread { public: ProCameraTestThread() { } virtual bool threadLoop() { mProc = ProcessState::self(); mProc->startThreadPool(); IPCThreadState *ptr = IPCThreadState::self(); ptr->joinThreadPool(); return false; } sp mProc; }; class ProCameraTestListener : public ProCameraListener { public: status_t WaitForEvent() { Mutex::Autolock cal(mConditionMutex); { Mutex::Autolock al(mListenerMutex); if (mLockEventList.size() > 0) { return OK; } } return mListenerCondition.waitRelative(mConditionMutex, TEST_LISTENER_TIMEOUT); } /* Read events into out. Existing queue is flushed */ void ReadEvents(EventList& out) { Mutex::Autolock al(mListenerMutex); for (size_t i = 0; i < mLockEventList.size(); ++i) { out.push(mLockEventList[i]); } mLockEventList.clear(); } /** * Dequeue 1 event from the event queue. * Returns UNKNOWN if queue is empty */ LockEvent ReadEvent() { Mutex::Autolock al(mListenerMutex); if (mLockEventList.size() == 0) { return UNKNOWN; } LockEvent ev = mLockEventList[0]; mLockEventList.removeAt(0); return ev; } private: void QueueEvent(LockEvent ev) { { Mutex::Autolock al(mListenerMutex); mLockEventList.push(ev); } mListenerCondition.broadcast(); } protected: ////////////////////////////////////////////////// ///////// ProCameraListener ////////////////////// ////////////////////////////////////////////////// // Lock has been acquired. Write operations now available. virtual void onLockAcquired() { QueueEvent(ACQUIRED); } // Lock has been released with exclusiveUnlock virtual void onLockReleased() { QueueEvent(RELEASED); } // Lock has been stolen by another client. virtual void onLockStolen() { QueueEvent(STOLEN); } // Lock free. virtual void onTriggerNotify(int32_t ext1, int32_t ext2, int32_t ext3) { dout << "Trigger notify: " << ext1 << " " << ext2 << " " << ext3 << std::endl; } // TODO: remove virtual void notify(int32_t , int32_t , int32_t ) {} virtual void postData(int32_t , const sp& , camera_frame_metadata_t *) {} virtual void postDataTimestamp(nsecs_t , int32_t , const sp& ) {} Vector mLockEventList; Mutex mListenerMutex; Mutex mConditionMutex; Condition mListenerCondition; }; class ProCameraTest : public ::testing::Test { public: ProCameraTest() { } static void SetUpTestCase() { // Binder Thread Pool Initialization mTestThread = new ProCameraTestThread(); mTestThread->run("ProCameraTestThread"); } virtual void SetUp() { mCamera = ProCamera::connect(CAMERA_ID); ASSERT_NE((void*)NULL, mCamera.get()); mListener = new ProCameraTestListener(); mCamera->setListener(mListener); } virtual void TearDown() { ASSERT_NE((void*)NULL, mCamera.get()); mCamera->disconnect(); } protected: sp mCamera; sp mListener; static sp mTestThread; int mDisplaySecs; sp mComposerClient; sp mSurfaceControl; int getSurfaceWidth() { return 512; } int getSurfaceHeight() { return 512; } void createOnScreenSurface(sp& surface) { mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); mSurfaceControl = mComposerClient->createSurface( String8("ProCameraTest StreamingImage Surface"), getSurfaceWidth(), getSurfaceHeight(), PIXEL_FORMAT_RGB_888, 0); ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); SurfaceComposerClient::openGlobalTransaction(); ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); SurfaceComposerClient::closeGlobalTransaction(); sp window = mSurfaceControl->getSurface(); surface = mSurfaceControl->getSurface(); ASSERT_NE((void*)NULL, surface.get()); } }; sp ProCameraTest::mTestThread; // test around exclusiveTryLock (immediate locking) TEST_F(ProCameraTest, LockingImmediate) { if (HasFatalFailure()) { return; } EXPECT_FALSE(mCamera->hasExclusiveLock()); EXPECT_EQ(OK, mCamera->exclusiveTryLock()); // at this point we definitely have the lock EXPECT_EQ(OK, mListener->WaitForEvent()); EXPECT_EQ(ACQUIRED, mListener->ReadEvent()); EXPECT_TRUE(mCamera->hasExclusiveLock()); EXPECT_EQ(OK, mCamera->exclusiveUnlock()); EXPECT_EQ(OK, mListener->WaitForEvent()); EXPECT_EQ(RELEASED, mListener->ReadEvent()); EXPECT_FALSE(mCamera->hasExclusiveLock()); } // test around exclusiveLock (locking at some future point in time) TEST_F(ProCameraTest, LockingAsynchronous) { if (HasFatalFailure()) { return; } // TODO: Add another procamera that has a lock here. // then we can be test that the lock wont immediately be acquired EXPECT_FALSE(mCamera->hasExclusiveLock()); EXPECT_EQ(OK, mCamera->exclusiveLock()); // at this point we may or may not have the lock // we cant be sure until we get an ACQUIRED event EXPECT_EQ(OK, mListener->WaitForEvent()); EXPECT_EQ(ACQUIRED, mListener->ReadEvent()); EXPECT_TRUE(mCamera->hasExclusiveLock()); EXPECT_EQ(OK, mCamera->exclusiveUnlock()); EXPECT_EQ(OK, mListener->WaitForEvent()); EXPECT_EQ(RELEASED, mListener->ReadEvent()); EXPECT_FALSE(mCamera->hasExclusiveLock()); } // Stream directly to the screen. TEST_F(ProCameraTest, StreamingImage) { if (HasFatalFailure()) { return; } char* displaySecsEnv = getenv("TEST_DISPLAY_SECS"); if (displaySecsEnv != NULL) { mDisplaySecs = atoi(displaySecsEnv); if (mDisplaySecs < 0) { mDisplaySecs = 0; } } else { mDisplaySecs = 0; } sp surface; if (mDisplaySecs > 0) { createOnScreenSurface(/*out*/surface); } int streamId = -1; EXPECT_OK(mCamera->createStream(/*width*/640, /*height*/480, TEST_FORMAT, surface, &streamId)); EXPECT_NE(-1, streamId); EXPECT_OK(mCamera->exclusiveTryLock()); /* 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 // wow what a verbose API. // i would give a loaf of bread for // metadata->updateOrInsert(keys.request.output.streams, 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, &streamId, /*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, &streamId, /*data_count*/1)); } } else { ASSERT_OK(update_camera_metadata_entry(request, entry.index, &streamId, /*data_count*/1, &entry)); } EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true)); dout << "will sleep now for " << mDisplaySecs << std::endl; sleep(mDisplaySecs); free_camera_metadata(request); EXPECT_OK(mCamera->cancelStream(streamId)); EXPECT_OK(mCamera->exclusiveUnlock()); } } } } }