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 <changyeon@google.com>
diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal
index e7f6bb7..d4263b7 100644
--- a/automotive/evs/1.1/IEvsCamera.hal
+++ b/automotive/evs/1.1/IEvsCamera.hal
@@ -54,4 +54,59 @@
* @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 cd058a5..7c7f832 100644
--- a/automotive/evs/1.1/IEvsCameraStream.hal
+++ b/automotive/evs/1.1/IEvsCameraStream.hal
@@ -19,7 +19,7 @@
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 62d9826..ae293b6 100644
--- a/automotive/evs/1.1/default/EvsCamera.cpp
+++ b/automotive/evs/1.1/default/EvsCamera.cpp
@@ -258,6 +258,38 @@
}
+Return<EvsResult> EvsCamera::setMaster() {
+ // Default implementation does not expect multiple subscribers and therefore
+ // return a success code always.
+ return EvsResult::OK;
+}
+
+
+Return<EvsResult> EvsCamera::unsetMaster() {
+ // Default implementation does not expect multiple subscribers and therefore
+ // return a success code always.
+ return EvsResult::OK;
+}
+
+
+Return<void> 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<void> 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 @@
// 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 0982464..6cb1cfb 100644
--- a/automotive/evs/1.1/default/EvsCamera.h
+++ b/automotive/evs/1.1/default/EvsCamera.h
@@ -60,6 +60,11 @@
Return<EvsResult> pauseVideoStream() override;
Return<EvsResult> resumeVideoStream() override;
Return<EvsResult> doneWithFrame_1_1(const BufferDesc_1_1& buffer) override;
+ Return<EvsResult> setMaster() override;
+ Return<EvsResult> unsetMaster() override;
+ Return<void> setParameter(CameraParam id, int32_t value,
+ setParameter_cb _hidl_cb) override;
+ Return<void> 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 ff6ab4e..2677018 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -48,9 +48,9 @@
};
/**
- * EVS event types
+ * Types of informative streaming events
*/
-enum EvsEventType : uint32_t {
+enum InfoEventType : uint32_t {
/**
* Video stream is started
*/
@@ -67,6 +67,25 @@
* 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 @@
/**
* 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 b7c7f21..a39346b 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.cpp
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.cpp
@@ -79,7 +79,7 @@
// Wait until the stream has actually stopped
std::unique_lock<std::mutex> lock(mLock);
if (mRunning) {
- mSignal.wait(lock, [this]() { return !mRunning; });
+ mEventSignal.wait(lock, [this]() { return !mRunning; });
}
}
@@ -110,7 +110,9 @@
void FrameHandler::waitForFrameCount(unsigned frameCount) {
// Wait until we've seen at least the requested number of frames (could be more)
std::unique_lock<std::mutex> lock(mLock);
- mSignal.wait(lock, [this, frameCount](){ return mFramesReceived >= frameCount; });
+ mFrameSignal.wait(lock, [this, frameCount](){
+ return mFramesReceived >= frameCount;
+ });
}
@@ -135,16 +137,21 @@
Return<void> 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 @@
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 @@
*height = mFrameHeight;
}
}
+
+void FrameHandler::waitForEvent(const InfoEventType aTargetEvent,
+ InfoEventDesc &eventDesc) {
+ // Wait until we get an expected parameter change event.
+ std::unique_lock<std::mutex> 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 49fa736..c5faecd 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.h
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.h
@@ -67,6 +67,8 @@
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 @@
// 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<BufferDesc_1_1> mHeldBuffers;
bool mRunning = false;
@@ -96,6 +99,7 @@
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 4f7082a..09a1bd9 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 @@
sp<IEvsEnumerator> pEnumerator; // Every test needs access to the service
std::vector <CameraDesc> 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 @@
}
+/*
+ * 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<IEvsCamera_1_1> 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> 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<IEvsCamera_1_1> pCamMaster =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCamMaster, nullptr);
+ sp<IEvsCamera_1_1> 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<FrameHandler> frameHandlerMaster =
+ new FrameHandler(pCamMaster, cam,
+ nullptr,
+ FrameHandler::eAutoReturn);
+ ASSERT_NE(frameHandlerMaster, nullptr);
+ sp<FrameHandler> 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<InfoEventType>(aNotification.aType));
+ ASSERT_EQ(CameraParam::BRIGHTNESS,
+ static_cast<CameraParam>(aNotification.payload[0]));
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(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<InfoEventType>(aNotification.aType));
+ ASSERT_EQ(CameraParam::CONTRAST,
+ static_cast<CameraParam>(aNotification.payload[0]));
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(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<InfoEventType>(aNotification.aType),
+ InfoEventType::PARAMETER_CHANGED);
+ ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
+ CameraParam::BRIGHTNESS);
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(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);