From d2a82466d82bdf5019ba86c3b8a6f0da2acef57e Mon Sep 17 00:00:00 2001 From: Changyeon Jo Date: Tue, 30 Jul 2019 11:57:17 -0700 Subject: [PATCH] Add methods to get or set camera parameters This change adds new methods to set and get camera parameters. As the vendor may handle a parameter set request with invalid value, a set method returns an effective value with a status code. Bug: 138328396 Test: VTS Change-Id: I278dad6c285fb9b341be3517cde09359da14cda6 Signed-off-by: Changyeon Jo --- automotive/evs/1.1/IEvsCamera.hal | 55 +++ automotive/evs/1.1/IEvsCameraStream.hal | 2 +- automotive/evs/1.1/default/EvsCamera.cpp | 36 +- automotive/evs/1.1/default/EvsCamera.h | 5 + automotive/evs/1.1/types.hal | 94 +++++- .../evs/1.1/vts/functional/FrameHandler.cpp | 53 ++- .../evs/1.1/vts/functional/FrameHandler.h | 6 +- .../functional/VtsHalEvsV1_1TargetTest.cpp | 315 +++++++++++++++++- 8 files changed, 540 insertions(+), 26 deletions(-) diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal index e7f6bb7b5b..d4263b7e20 100644 --- a/automotive/evs/1.1/IEvsCamera.hal +++ b/automotive/evs/1.1/IEvsCamera.hal @@ -54,4 +54,59 @@ interface IEvsCamera extends @1.0::IEvsCamera { * @return result Return EvsResult::OK if this call is successful. */ doneWithFrame_1_1(BufferDesc buffer) generates (EvsResult result); + + /** + * Requests to be a master client. + * + * When multiple clients subscribe to a single camera hardware and one of + * them adjusts a camera parameter such as the contrast, it may disturb + * other clients' operations. Therefore, the client must call this method + * to be a master client. Once it becomes a master, it will be able to + * change camera parameters until either it dies or explicitly gives up the + * role. + * + * @return result EvsResult::OK if a master role is granted. + * EvsResult::OWNERSHIP_LOST if there is already a + * master client. + */ + setMaster() generates (EvsResult result); + + + /** + * Retires from a master client role. + * + * @return result EvsResult::OK if this call is successful. + * EvsResult::INVALID_ARG if the caller client is not a + * master client. + */ + unsetMaster() generates (EvsResult result); + + /** + * Requests to set a camera parameter. + * + * @param id The identifier of camera parameter, CameraParam enum. + * value A desired parameter value. + * @return result EvsResult::OK if it succeeds to set a parameter. + * EvsResult::INVALID_ARG if either the request is + * not made by a master client, or a requested + * parameter is not supported. + * EvsResult::UNDERLYING_SERVICE_ERROR if it fails to + * program a value by any other reason. + * effectiveValue A programmed parameter value. This may differ + * from what the client gives if, for example, the + * driver does not support a target parameter. + */ + setParameter(CameraParam id, int32_t value) + generates (EvsResult result, int32_t effectiveValue); + + /** + * Retrieves a value of given camera parameter. + * + * @param id The identifier of camera parameter, CameraParam enum. + * @return result EvsResult::OK if it succeeds to read a parameter. + * EvsResult::INVALID_ARG if either a requested parameter is + * not supported. + * value A value of requested camera parameter. + */ + getParameter(CameraParam id) generates(EvsResult result, int32_t value); }; diff --git a/automotive/evs/1.1/IEvsCameraStream.hal b/automotive/evs/1.1/IEvsCameraStream.hal index cd058a5540..7c7f832103 100644 --- a/automotive/evs/1.1/IEvsCameraStream.hal +++ b/automotive/evs/1.1/IEvsCameraStream.hal @@ -19,7 +19,7 @@ package android.hardware.automotive.evs@1.1; import @1.0::IEvsCameraStream; /** - * Implemented on client side to receive asynchronous video frame deliveries. + * Implemented on client side to receive asynchronous streaming event deliveries. */ interface IEvsCameraStream extends @1.0::IEvsCameraStream { /** diff --git a/automotive/evs/1.1/default/EvsCamera.cpp b/automotive/evs/1.1/default/EvsCamera.cpp index 62d9826f24..ae293b6ba0 100644 --- a/automotive/evs/1.1/default/EvsCamera.cpp +++ b/automotive/evs/1.1/default/EvsCamera.cpp @@ -258,6 +258,38 @@ Return EvsCamera::resumeVideoStream() { } +Return EvsCamera::setMaster() { + // Default implementation does not expect multiple subscribers and therefore + // return a success code always. + return EvsResult::OK; +} + + +Return EvsCamera::unsetMaster() { + // Default implementation does not expect multiple subscribers and therefore + // return a success code always. + return EvsResult::OK; +} + + +Return EvsCamera::setParameter(CameraParam id, int32_t value, + setParameter_cb _hidl_cb) { + // Default implementation does not support this. + (void)id; + (void)value; + _hidl_cb(EvsResult::INVALID_ARG, 0); + return Void(); +} + + +Return EvsCamera::getParameter(CameraParam id, getParameter_cb _hidl_cb) { + // Default implementation does not support this. + (void)id; + _hidl_cb(EvsResult::INVALID_ARG, 0); + return Void(); +} + + bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) { if (bufferCount < 1) { ALOGE("Ignoring request to set buffer count to zero"); @@ -468,7 +500,9 @@ void EvsCamera::generateFrames() { // If we've been asked to stop, send an event to signal the actual end of stream EvsEvent event; - event.info(EvsEventType::STREAM_STOPPED); + InfoEventDesc desc = {}; + desc.aType = InfoEventType::STREAM_STOPPED; + event.info(desc); auto result = mStream->notifyEvent(event); if (!result.isOk()) { ALOGE("Error delivering end of stream marker"); diff --git a/automotive/evs/1.1/default/EvsCamera.h b/automotive/evs/1.1/default/EvsCamera.h index 0982464b59..6cb1cfbcff 100644 --- a/automotive/evs/1.1/default/EvsCamera.h +++ b/automotive/evs/1.1/default/EvsCamera.h @@ -60,6 +60,11 @@ public: Return pauseVideoStream() override; Return resumeVideoStream() override; Return doneWithFrame_1_1(const BufferDesc_1_1& buffer) override; + Return setMaster() override; + Return unsetMaster() override; + Return setParameter(CameraParam id, int32_t value, + setParameter_cb _hidl_cb) override; + Return getParameter(CameraParam id, getParameter_cb _hidl_cb) override; // Implementation details EvsCamera(const char *id); diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal index ff6ab4e785..2677018a40 100644 --- a/automotive/evs/1.1/types.hal +++ b/automotive/evs/1.1/types.hal @@ -48,9 +48,9 @@ struct BufferDesc { }; /** - * EVS event types + * Types of informative streaming events */ -enum EvsEventType : uint32_t { +enum InfoEventType : uint32_t { /** * Video stream is started */ @@ -67,6 +67,25 @@ enum EvsEventType : uint32_t { * Timeout happens */ TIMEOUT, + /** + * Camera parameter is changed; payload contains a changed parameter ID and + * its value + */ + PARAMETER_CHANGED, +}; + +/** + * Structure that describes informative events occurred during EVS is streaming + */ +struct InfoEventDesc { + /** + * Type of an informative event + */ + InfoEventType aType; + /** + * Possible additional information + */ + uint32_t[4] payload; }; /** @@ -80,5 +99,74 @@ safe_union EvsEvent { /** * General streaming events */ - EvsEventType info; + InfoEventDesc info; +}; + +/** + * EVS Camera Parameter + */ +enum CameraParam : uint32_t { + /** + * The brightness of image frames + */ + BRIGHTNESS, + /** + * The contrast of image frames + */ + CONTRAST, + /** + * Automatic gain/exposure control + */ + AUTOGAIN, + /** + * Gain control + */ + GAIN, + /** + * Mirror the image horizontally + */ + HFLIP, + /** + * Mirror the image vertically + */ + VFLIP, + /** + * Automatic Whitebalance + */ + AUTO_WHITE_BALANCE, + /** + * Manual white balance setting as a color temperature in Kelvin. + */ + WHITE_BALANCE_TEMPERATURE, + /** + * Image sharpness adjustment + */ + SHARPNESS, + /** + * Auto Exposure Control modes; auto, manual, shutter priority, or + * aperture priority. + */ + AUTO_EXPOSURE, + /** + * Manual exposure time of the camera + */ + ABSOLUTE_EXPOSURE, + /** + * When AEC is running in either auto or aperture priority, this parameter + * sets whether a frame rate varies. + */ + AUTO_EXPOSURE_PRIORITY, + /** + * Set the focal point of the camera to the specified position. This + * parameter may not be effective when auto focus is enabled. + */ + ABSOLUTE_FOCUS, + /** + * Enables continuous automatic focus adjustments. + */ + AUTO_FOCUS, + /** + * Specify the objective lens focal length as an absolute value. + */ + ABSOLUTE_ZOOM, }; diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.cpp b/automotive/evs/1.1/vts/functional/FrameHandler.cpp index b7c7f21c2d..a39346b845 100644 --- a/automotive/evs/1.1/vts/functional/FrameHandler.cpp +++ b/automotive/evs/1.1/vts/functional/FrameHandler.cpp @@ -79,7 +79,7 @@ void FrameHandler::blockingStopStream() { // Wait until the stream has actually stopped std::unique_lock lock(mLock); if (mRunning) { - mSignal.wait(lock, [this]() { return !mRunning; }); + mEventSignal.wait(lock, [this]() { return !mRunning; }); } } @@ -110,7 +110,9 @@ bool FrameHandler::isRunning() { void FrameHandler::waitForFrameCount(unsigned frameCount) { // Wait until we've seen at least the requested number of frames (could be more) std::unique_lock lock(mLock); - mSignal.wait(lock, [this, frameCount](){ return mFramesReceived >= frameCount; }); + mFrameSignal.wait(lock, [this, frameCount](){ + return mFramesReceived >= frameCount; + }); } @@ -135,16 +137,21 @@ Return FrameHandler::deliverFrame(const BufferDesc_1_0& bufferArg) { Return FrameHandler::notifyEvent(const EvsEvent& event) { // Local flag we use to keep track of when the stream is stopping - bool timeToStop = false; - auto type = event.getDiscriminator(); if (type == EvsEvent::hidl_discriminator::info) { - if (event.info() == EvsEventType::STREAM_STOPPED) { + mLock.lock(); + mLatestEventDesc = event.info(); + if (mLatestEventDesc.aType == InfoEventType::STREAM_STOPPED) { // Signal that the last frame has been received and the stream is stopped - timeToStop = true; + mRunning = false; + } else if (mLatestEventDesc.aType == InfoEventType::PARAMETER_CHANGED) { + ALOGD("Camera parameter 0x%X is changed to 0x%X", + mLatestEventDesc.payload[0], mLatestEventDesc.payload[1]); } else { - ALOGD("Received an event 0x%X", event.info()); + ALOGD("Received an event 0x%X", mLatestEventDesc.aType); } + mLock.unlock(); + mEventSignal.notify_all(); } else { auto bufDesc = event.buffer(); const AHardwareBuffer_Desc* pDesc = @@ -207,21 +214,14 @@ Return FrameHandler::notifyEvent(const EvsEvent& event) { mHeldBuffers.push(bufDesc); } + mLock.lock(); + ++mFramesReceived; + mLock.unlock(); + mFrameSignal.notify_all(); ALOGD("Frame handling complete"); } - - // Update our received frame count and notify anybody who cares that things have changed - mLock.lock(); - if (timeToStop) { - mRunning = false; - } else { - mFramesReceived++; - } - mLock.unlock(); - mSignal.notify_all(); - return Void(); } @@ -338,3 +338,20 @@ void FrameHandler::getFrameDimension(unsigned* width, unsigned* height) { *height = mFrameHeight; } } + +void FrameHandler::waitForEvent(const InfoEventType aTargetEvent, + InfoEventDesc &eventDesc) { + // Wait until we get an expected parameter change event. + std::unique_lock lock(mLock); + mEventSignal.wait(lock, [this, aTargetEvent, &eventDesc](){ + bool flag = mLatestEventDesc.aType == aTargetEvent; + if (flag) { + eventDesc.aType = mLatestEventDesc.aType; + eventDesc.payload[0] = mLatestEventDesc.payload[0]; + eventDesc.payload[1] = mLatestEventDesc.payload[1]; + } + + return flag; + }); +} + diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.h b/automotive/evs/1.1/vts/functional/FrameHandler.h index 49fa736435..c5faecde5f 100644 --- a/automotive/evs/1.1/vts/functional/FrameHandler.h +++ b/automotive/evs/1.1/vts/functional/FrameHandler.h @@ -67,6 +67,8 @@ public: bool isRunning(); void waitForFrameCount(unsigned frameCount); + void waitForEvent(const InfoEventType aTargetEvent, + InfoEventDesc &eventDesc); void getFramesCounters(unsigned* received, unsigned* displayed); void getFrameDimension(unsigned* width, unsigned* height); @@ -88,7 +90,8 @@ private: // we need to protect all member variables that may be modified while we're streaming // (ie: those below) std::mutex mLock; - std::condition_variable mSignal; + std::condition_variable mEventSignal; + std::condition_variable mFrameSignal; std::queue mHeldBuffers; bool mRunning = false; @@ -96,6 +99,7 @@ private: unsigned mFramesDisplayed = 0; // Simple counter -- rolls over eventually! unsigned mFrameWidth = 0; unsigned mFrameHeight = 0; + InfoEventDesc mLatestEventDesc; }; diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp index 4f7082aa26..09a1bd936e 100644 --- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp +++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp @@ -124,8 +124,8 @@ protected: sp pEnumerator; // Every test needs access to the service std::vector cameraInfo; // Empty unless/until loadCameraList() is called - bool mIsHwModule; // boolean to tell current module under testing - // is HW module implementation. + bool mIsHwModule; // boolean to tell current module under testing + // is HW module implementation. }; @@ -516,6 +516,317 @@ TEST_F(EvsHidlTest, MultiCameraStream) { } +/* + * CameraParameter: + * Verify that a client can adjust a camera parameter. + */ +TEST_F(EvsHidlTest, CameraParameter) { + ALOGI("Starting CameraParameter test"); + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam: cameraInfo) { + // Create a camera client + sp pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Set up per-client frame receiver objects which will fire up its own thread + sp frameHandler = new FrameHandler(pCam, cam, + nullptr, + FrameHandler::eAutoReturn); + ASSERT_NE(frameHandler, nullptr); + + // Start the camera's video stream + bool startResult = frameHandler->startStream(); + ASSERT_TRUE(startResult); + + // Ensure the stream starts + frameHandler->waitForFrameCount(1); + + // Try to program few parameters + EvsResult result = EvsResult::OK; + int32_t val0 = 100; + int32_t val1 = 0; + + result = pCam->setMaster(); + ASSERT_TRUE(result == EvsResult::OK); + + pCam->setParameter(CameraParam::BRIGHTNESS, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::OK || + result == EvsResult::INVALID_ARG); + + pCam->getParameter(CameraParam::BRIGHTNESS, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_TRUE(result == EvsResult::OK || + result == EvsResult::INVALID_ARG); + ASSERT_EQ(val0, val1) << "Values are not matched."; + + val0 = 80; + val1 = 0; + pCam->setParameter(CameraParam::CONTRAST, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::OK || + result == EvsResult::INVALID_ARG); + + pCam->getParameter(CameraParam::CONTRAST, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_TRUE(result == EvsResult::OK || + result == EvsResult::INVALID_ARG); + ASSERT_EQ(val0, val1) << "Values are not matched."; + + val0 = 300; + val1 = 0; + pCam->setParameter(CameraParam::ABSOLUTE_ZOOM, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::OK || + result == EvsResult::INVALID_ARG); + + pCam->getParameter(CameraParam::ABSOLUTE_ZOOM, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_TRUE(result == EvsResult::OK || + result == EvsResult::INVALID_ARG); + ASSERT_EQ(val0, val1) << "Values are not matched."; + + result = pCam->unsetMaster(); + ASSERT_TRUE(result == EvsResult::OK); + + // Shutdown another + frameHandler->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCam); + } +} + + +/* + * MultiCameraParameter: + * Verify that master and non-master clients behave as expected when they try to adjust + * camera parameters. + */ +TEST_F(EvsHidlTest, MultiCameraParameter) { + ALOGI("Starting CameraParameter test"); + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Test each reported camera + for (auto&& cam: cameraInfo) { + // Create two camera clients. + sp pCamMaster = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId)) + .withDefault(nullptr); + ASSERT_NE(pCamMaster, nullptr); + sp pCamNonMaster = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId)) + .withDefault(nullptr); + ASSERT_NE(pCamNonMaster, nullptr); + + // Set up per-client frame receiver objects which will fire up its own thread + sp frameHandlerMaster = + new FrameHandler(pCamMaster, cam, + nullptr, + FrameHandler::eAutoReturn); + ASSERT_NE(frameHandlerMaster, nullptr); + sp frameHandlerNonMaster = + new FrameHandler(pCamNonMaster, cam, + nullptr, + FrameHandler::eAutoReturn); + ASSERT_NE(frameHandlerNonMaster, nullptr); + + // Set one client as the master + EvsResult result = pCamMaster->setMaster(); + ASSERT_TRUE(result == EvsResult::OK); + + // Try to set another client as the master. + result = pCamNonMaster->setMaster(); + ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST); + + // Start the camera's video stream via a master client. + bool startResult = frameHandlerMaster->startStream(); + ASSERT_TRUE(startResult); + + // Ensure the stream starts + frameHandlerMaster->waitForFrameCount(1); + + // Start the camera's video stream via another client + startResult = frameHandlerNonMaster->startStream(); + ASSERT_TRUE(startResult); + + // Ensure the stream starts + frameHandlerNonMaster->waitForFrameCount(1); + + // Try to program CameraParam::BRIGHTNESS + int32_t val0 = 100; + int32_t val1 = 0; + + pCamMaster->setParameter(CameraParam::BRIGHTNESS, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program + result == EvsResult::INVALID_ARG); // Camera parameter is not supported + + // Non-master client expects to receive a parameter change notification + // whenever a master client adjusts it. + InfoEventDesc aNotification = {}; + + pCamMaster->getParameter(CameraParam::BRIGHTNESS, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program + result == EvsResult::INVALID_ARG); // Camera parameter is not supported + if (result == EvsResult::OK) { + ASSERT_EQ(val0, val1) << "Values are not matched."; + + // Verify a change notification + frameHandlerNonMaster->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification); + ASSERT_EQ(InfoEventType::PARAMETER_CHANGED, + static_cast(aNotification.aType)); + ASSERT_EQ(CameraParam::BRIGHTNESS, + static_cast(aNotification.payload[0])); + ASSERT_EQ(val1, + static_cast(aNotification.payload[1])); + } + + // Try to program CameraParam::CONTRAST + val0 = 80; + val1 = 0; + pCamMaster->setParameter(CameraParam::CONTRAST, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program + result == EvsResult::INVALID_ARG); // Camera parameter is not supported + + if (result == EvsResult::OK) { + pCamMaster->getParameter(CameraParam::CONTRAST, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_TRUE(result == EvsResult::OK); + ASSERT_EQ(val0, val1) << "Values are not matched."; + + + // Verify a change notification + frameHandlerNonMaster->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification); + ASSERT_EQ(InfoEventType::PARAMETER_CHANGED, + static_cast(aNotification.aType)); + ASSERT_EQ(CameraParam::CONTRAST, + static_cast(aNotification.payload[0])); + ASSERT_EQ(val1, + static_cast(aNotification.payload[1])); + } + + // Try to adjust a parameter via non-master client + pCamNonMaster->setParameter(CameraParam::CONTRAST, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::INVALID_ARG); + + // Non-master client attemps to be a master + result = pCamNonMaster->setMaster(); + ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST); + + // Master client retires from a master role + result = pCamMaster->unsetMaster(); + ASSERT_TRUE(result == EvsResult::OK); + + // Try to adjust a parameter after being retired + pCamMaster->setParameter(CameraParam::BRIGHTNESS, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::INVALID_ARG); + + // Non-master client becomes a master + result = pCamNonMaster->setMaster(); + ASSERT_TRUE(result == EvsResult::OK); + + // Try to adjust a parameter via new master client + pCamNonMaster->setParameter(CameraParam::BRIGHTNESS, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program + result == EvsResult::INVALID_ARG); // Camera parameter is not supported + + // Wait a moment + sleep(1); + + // Verify a change notification + if (result == EvsResult::OK) { + frameHandlerMaster->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification); + ASSERT_EQ(static_cast(aNotification.aType), + InfoEventType::PARAMETER_CHANGED); + ASSERT_EQ(static_cast(aNotification.payload[0]), + CameraParam::BRIGHTNESS); + ASSERT_EQ(val1, + static_cast(aNotification.payload[1])); + } + + // New master retires from a master role + result = pCamNonMaster->unsetMaster(); + ASSERT_TRUE(result == EvsResult::OK); + + // Shutdown + frameHandlerMaster->shutdown(); + frameHandlerNonMaster->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCamMaster); + pEnumerator->closeCamera(pCamNonMaster); + } +} + + int main(int argc, char** argv) { ::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance()); ::testing::InitGoogleTest(&argc, argv);