blob: c6f195b7bc7f1dc0223b17e9593e107722e3da8e [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2020, Google Inc.
*
* delayed_controls.cpp - libcamera delayed controls test
*/
#include <iostream>
#include "libcamera/internal/delayed_controls.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/v4l2_videodevice.h"
#include "test.h"
using namespace std;
using namespace libcamera;
class DelayedControlsTest : public Test
{
public:
DelayedControlsTest()
{
}
protected:
int init() override
{
enumerator_ = DeviceEnumerator::create();
if (!enumerator_) {
cerr << "Failed to create device enumerator" << endl;
return TestFail;
}
if (enumerator_->enumerate()) {
cerr << "Failed to enumerate media devices" << endl;
return TestFail;
}
DeviceMatch dm("vivid");
dm.add("vivid-000-vid-cap");
media_ = enumerator_->search(dm);
if (!media_) {
cerr << "vivid video device found" << endl;
return TestSkip;
}
dev_ = V4L2VideoDevice::fromEntityName(media_.get(), "vivid-000-vid-cap");
if (dev_->open()) {
cerr << "Failed to open video device" << endl;
return TestFail;
}
const ControlInfoMap &infoMap = dev_->controls();
/* Make sure the controls we require are present. */
if (infoMap.empty()) {
cerr << "Failed to enumerate controls" << endl;
return TestFail;
}
if (infoMap.find(V4L2_CID_BRIGHTNESS) == infoMap.end() ||
infoMap.find(V4L2_CID_CONTRAST) == infoMap.end()) {
cerr << "Missing controls" << endl;
return TestFail;
}
return TestPass;
}
int singleControlNoDelay()
{
std::unordered_map<uint32_t, DelayedControls::ControlParams> delays = {
{ V4L2_CID_BRIGHTNESS, { 0, false } },
};
std::unique_ptr<DelayedControls> delayed =
std::make_unique<DelayedControls>(dev_.get(), delays);
ControlList ctrls;
/* Reset control to value not used in test. */
ctrls.set(V4L2_CID_BRIGHTNESS, 1);
dev_->setControls(&ctrls);
delayed->reset();
/* Trigger the first frame start event */
delayed->applyControls(0);
/* Test control without delay are set at once. */
for (unsigned int i = 1; i < 100; i++) {
int32_t value = 100 + i;
ctrls.set(V4L2_CID_BRIGHTNESS, value);
delayed->push(ctrls);
delayed->applyControls(i);
ControlList result = delayed->get(i);
int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();
if (brightness != value) {
cerr << "Failed single control without delay"
<< " frame " << i
<< " expected " << value
<< " got " << brightness
<< endl;
return TestFail;
}
}
return TestPass;
}
int singleControlWithDelay()
{
std::unordered_map<uint32_t, DelayedControls::ControlParams> delays = {
{ V4L2_CID_BRIGHTNESS, { 1, false } },
};
std::unique_ptr<DelayedControls> delayed =
std::make_unique<DelayedControls>(dev_.get(), delays);
ControlList ctrls;
/* Reset control to value that will be first in test. */
int32_t expected = 4;
ctrls.set(V4L2_CID_BRIGHTNESS, expected);
dev_->setControls(&ctrls);
delayed->reset();
/* Trigger the first frame start event */
delayed->applyControls(0);
/* Test single control with delay. */
for (unsigned int i = 1; i < 100; i++) {
int32_t value = 10 + i;
ctrls.set(V4L2_CID_BRIGHTNESS, value);
delayed->push(ctrls);
delayed->applyControls(i);
ControlList result = delayed->get(i);
int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();
if (brightness != expected) {
cerr << "Failed single control with delay"
<< " frame " << i
<< " expected " << expected
<< " got " << brightness
<< endl;
return TestFail;
}
expected = value;
}
return TestPass;
}
int dualControlsWithDelay(uint32_t startOffset)
{
static const unsigned int maxDelay = 2;
std::unordered_map<uint32_t, DelayedControls::ControlParams> delays = {
{ V4L2_CID_BRIGHTNESS, { 1, false } },
{ V4L2_CID_CONTRAST, { maxDelay, false } },
};
std::unique_ptr<DelayedControls> delayed =
std::make_unique<DelayedControls>(dev_.get(), delays);
ControlList ctrls;
/* Reset control to value that will be first two frames in test. */
int32_t expected = 200;
ctrls.set(V4L2_CID_BRIGHTNESS, expected);
ctrls.set(V4L2_CID_CONTRAST, expected + 1);
dev_->setControls(&ctrls);
delayed->reset();
/* Trigger the first frame start event */
delayed->applyControls(startOffset);
/* Test dual control with delay. */
for (unsigned int i = 1; i < 100; i++) {
uint32_t frame = startOffset + i;
int32_t value = 10 + i;
ctrls.set(V4L2_CID_BRIGHTNESS, value);
ctrls.set(V4L2_CID_CONTRAST, value + 1);
delayed->push(ctrls);
delayed->applyControls(frame);
ControlList result = delayed->get(frame);
int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();
int32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>();
if (brightness != expected || contrast != expected + 1) {
cerr << "Failed dual controls"
<< " frame " << frame
<< " brightness " << brightness
<< " contrast " << contrast
<< " expected " << expected
<< endl;
return TestFail;
}
expected = i < maxDelay ? expected : value - 1;
}
return TestPass;
}
int dualControlsMultiQueue()
{
static const unsigned int maxDelay = 2;
std::unordered_map<uint32_t, DelayedControls::ControlParams> delays = {
{ V4L2_CID_BRIGHTNESS, { 1, false } },
{ V4L2_CID_CONTRAST, { maxDelay, false } }
};
std::unique_ptr<DelayedControls> delayed =
std::make_unique<DelayedControls>(dev_.get(), delays);
ControlList ctrls;
/* Reset control to value that will be first two frames in test. */
int32_t expected = 100;
ctrls.set(V4L2_CID_BRIGHTNESS, expected);
ctrls.set(V4L2_CID_CONTRAST, expected);
dev_->setControls(&ctrls);
delayed->reset();
/* Trigger the first frame start event */
delayed->applyControls(0);
/*
* Queue all controls before any fake frame start. Note we
* can't queue up more then the delayed controls history size
* which is 16. Where one spot is used by the reset control.
*/
for (unsigned int i = 0; i < 15; i++) {
int32_t value = 10 + i;
ctrls.set(V4L2_CID_BRIGHTNESS, value);
ctrls.set(V4L2_CID_CONTRAST, value);
delayed->push(ctrls);
}
/* Process all queued controls. */
for (unsigned int i = 1; i < 16; i++) {
int32_t value = 10 + i - 1;
delayed->applyControls(i);
ControlList result = delayed->get(i);
int32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();
int32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>();
if (brightness != expected || contrast != expected) {
cerr << "Failed multi queue"
<< " frame " << i
<< " brightness " << brightness
<< " contrast " << contrast
<< " expected " << expected
<< endl;
return TestFail;
}
expected = i < maxDelay ? expected : value - 1;
}
return TestPass;
}
int run() override
{
int ret;
/* Test single control without delay. */
ret = singleControlNoDelay();
if (ret)
return ret;
/* Test single control with delay. */
ret = singleControlWithDelay();
if (ret)
return ret;
/* Test dual controls with different delays. */
ret = dualControlsWithDelay(0);
if (ret)
return ret;
/* Test dual controls with non-zero sequence start. */
ret = dualControlsWithDelay(10000);
if (ret)
return ret;
/* Test dual controls with sequence number wraparound. */
ret = dualControlsWithDelay(UINT32_MAX - 50);
if (ret)
return ret;
/* Test control values produced faster than consumed. */
ret = dualControlsMultiQueue();
if (ret)
return ret;
return TestPass;
}
private:
std::unique_ptr<DeviceEnumerator> enumerator_;
std::shared_ptr<MediaDevice> media_;
std::unique_ptr<V4L2VideoDevice> dev_;
};
TEST_REGISTER(DelayedControlsTest)