An attempt at getting image data back

This commit is contained in:
2024-07-14 00:27:33 +02:00
parent e026bc93f7
commit 6452d2e774
1314 changed files with 218350 additions and 38 deletions

View File

@@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2020, Google Inc.
*
* Test the buffer cache different operation modes
*/
#include <iostream>
#include <random>
#include <vector>
#include <libcamera/formats.h>
#include <libcamera/stream.h>
#include "buffer_source.h"
#include "test.h"
using namespace libcamera;
namespace {
class BufferCacheTest : public Test
{
public:
/*
* Test that a cache with the same size as there are buffers results in
* a sequential run over; 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, ...
*
* The test is only valid when the cache size is as least as big as the
* number of buffers.
*/
int testSequential(V4L2BufferCache *cache,
const std::vector<std::unique_ptr<FrameBuffer>> &buffers)
{
for (unsigned int i = 0; i < buffers.size() * 100; i++) {
int nBuffer = i % buffers.size();
int index = cache->get(*buffers[nBuffer].get());
if (index != nBuffer) {
std::cout << "Expected index " << nBuffer
<< " got " << index << std::endl;
return TestFail;
}
cache->put(index);
}
return TestPass;
}
/*
* Test that randomly putting buffers to the cache always results in a
* valid index.
*/
int testRandom(V4L2BufferCache *cache,
const std::vector<std::unique_ptr<FrameBuffer>> &buffers)
{
std::uniform_int_distribution<> dist(0, buffers.size() - 1);
for (unsigned int i = 0; i < buffers.size() * 100; i++) {
int nBuffer = dist(generator_);
int index = cache->get(*buffers[nBuffer].get());
if (index < 0) {
std::cout << "Failed lookup from cache"
<< std::endl;
return TestFail;
}
cache->put(index);
}
return TestPass;
}
/*
* Test that using a buffer more frequently keeps it hot in the cache at
* all times.
*/
int testHot(V4L2BufferCache *cache,
const std::vector<std::unique_ptr<FrameBuffer>> &buffers,
unsigned int hotFrequency)
{
/* Run the random test on the cache to make it messy. */
if (testRandom(cache, buffers) != TestPass)
return TestFail;
std::uniform_int_distribution<> dist(0, buffers.size() - 1);
/* Pick a hot buffer at random and store its index. */
int hotBuffer = dist(generator_);
int hotIndex = cache->get(*buffers[hotBuffer].get());
cache->put(hotIndex);
/*
* Queue hot buffer at the requested frequency and make sure
* it stays hot.
*/
for (unsigned int i = 0; i < buffers.size() * 100; i++) {
int nBuffer, index;
bool hotQueue = i % hotFrequency == 0;
if (hotQueue)
nBuffer = hotBuffer;
else
nBuffer = dist(generator_);
index = cache->get(*buffers[nBuffer].get());
if (index < 0) {
std::cout << "Failed lookup from cache"
<< std::endl;
return TestFail;
}
if (hotQueue && index != hotIndex) {
std::cout << "Hot buffer got cold"
<< std::endl;
return TestFail;
}
cache->put(index);
}
return TestPass;
}
int testIsEmpty(const std::vector<std::unique_ptr<FrameBuffer>> &buffers)
{
V4L2BufferCache cache(buffers.size());
if (!cache.isEmpty())
return TestFail;
for (auto const &buffer : buffers) {
FrameBuffer &b = *buffer.get();
cache.get(b);
}
if (cache.isEmpty())
return TestFail;
unsigned int i;
for (i = 0; i < buffers.size() - 1; i++)
cache.put(i);
if (cache.isEmpty())
return TestFail;
cache.put(i);
if (!cache.isEmpty())
return TestFail;
return TestPass;
}
int init() override
{
std::random_device rd;
unsigned int seed = rd();
std::cout << "Random seed is " << seed << std::endl;
generator_.seed(seed);
return TestPass;
}
int run() override
{
const unsigned int numBuffers = 8;
StreamConfiguration cfg;
cfg.pixelFormat = formats::YUYV;
cfg.size = Size(600, 800);
cfg.bufferCount = numBuffers;
BufferSource source;
int ret = source.allocate(cfg);
if (ret != TestPass)
return ret;
const std::vector<std::unique_ptr<FrameBuffer>> &buffers =
source.buffers();
if (buffers.size() != numBuffers) {
std::cout << "Got " << buffers.size()
<< " buffers, expected " << numBuffers
<< std::endl;
return TestFail;
}
/*
* Test cache of same size as there are buffers, the cache is
* created from a list of buffers and will be pre-populated.
*/
V4L2BufferCache cacheFromBuffers(buffers);
if (testSequential(&cacheFromBuffers, buffers) != TestPass)
return TestFail;
if (testRandom(&cacheFromBuffers, buffers) != TestPass)
return TestFail;
if (testHot(&cacheFromBuffers, buffers, numBuffers) != TestPass)
return TestFail;
/*
* Test cache of same size as there are buffers, the cache is
* not pre-populated.
*/
V4L2BufferCache cacheFromNumbers(numBuffers);
if (testSequential(&cacheFromNumbers, buffers) != TestPass)
return TestFail;
if (testRandom(&cacheFromNumbers, buffers) != TestPass)
return TestFail;
if (testHot(&cacheFromNumbers, buffers, numBuffers) != TestPass)
return TestFail;
/*
* Test cache half the size of number of buffers used, the cache
* is not pre-populated.
*/
V4L2BufferCache cacheHalf(numBuffers / 2);
if (testRandom(&cacheHalf, buffers) != TestPass)
return TestFail;
if (testHot(&cacheHalf, buffers, numBuffers / 2) != TestPass)
return TestFail;
/*
* Test that the isEmpty function reports the correct result at
* various levels of cache fullness.
*/
if (testIsEmpty(buffers) != TestPass)
return TestFail;
return TestPass;
}
private:
std::mt19937 generator_;
};
} /* namespace */
TEST_REGISTER(BufferCacheTest)

View File

@@ -0,0 +1,206 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 API tests
*
* Validate the function of exporting buffers from a V4L2VideoDevice and
* the ability to import them to another V4L2VideoDevice instance.
* Ensure that the Buffers can successfully be queued and dequeued
* between both devices.
*/
#include <iostream>
#include <libcamera/framebuffer.h>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include "v4l2_videodevice_test.h"
using namespace libcamera;
using namespace std::chrono_literals;
class BufferSharingTest : public V4L2VideoDeviceTest
{
public:
BufferSharingTest()
: V4L2VideoDeviceTest("vivid", "vivid-000-vid-cap"),
output_(nullptr), framesCaptured_(0), framesOutput_(0) {}
protected:
int init()
{
int ret = V4L2VideoDeviceTest::init();
if (ret)
return ret;
/* media_ already represents VIVID */
MediaEntity *entity = media_->getEntityByName("vivid-000-vid-out");
if (!entity)
return TestSkip;
output_ = new V4L2VideoDevice(entity);
if (!output_) {
std::cout << "Failed to create output device" << std::endl;
return TestFail;
}
ret = output_->open();
if (ret) {
std::cout << "Failed to open output device" << std::endl;
return TestFail;
}
V4L2DeviceFormat format = {};
ret = capture_->getFormat(&format);
if (ret) {
std::cout << "Failed to get capture format" << std::endl;
return TestFail;
}
format.size.width = 320;
format.size.height = 180;
ret = capture_->setFormat(&format);
if (ret) {
std::cout << "Failed to set capture format" << std::endl;
return TestFail;
}
ret = output_->setFormat(&format);
if (ret) {
std::cout << "Failed to set output format" << std::endl;
return TestFail;
}
ret = capture_->allocateBuffers(bufferCount, &buffers_);
if (ret < 0) {
std::cout << "Failed to allocate buffers" << std::endl;
return TestFail;
}
ret = output_->importBuffers(bufferCount);
if (ret < 0) {
std::cout << "Failed to import buffers" << std::endl;
return TestFail;
}
return 0;
}
void captureBufferReady(FrameBuffer *buffer)
{
const FrameMetadata &metadata = buffer->metadata();
std::cout << "Received capture buffer" << std::endl;
if (metadata.status != FrameMetadata::FrameSuccess)
return;
output_->queueBuffer(buffer);
framesCaptured_++;
}
void outputBufferReady(FrameBuffer *buffer)
{
const FrameMetadata &metadata = buffer->metadata();
std::cout << "Received output buffer" << std::endl;
if (metadata.status != FrameMetadata::FrameSuccess)
return;
capture_->queueBuffer(buffer);
framesOutput_++;
}
int run()
{
EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
Timer timeout;
int ret;
capture_->bufferReady.connect(this, &BufferSharingTest::captureBufferReady);
output_->bufferReady.connect(this, &BufferSharingTest::outputBufferReady);
for (const std::unique_ptr<FrameBuffer> &buffer : buffers_) {
if (capture_->queueBuffer(buffer.get())) {
std::cout << "Failed to queue buffer" << std::endl;
return TestFail;
}
}
ret = capture_->streamOn();
if (ret) {
std::cout << "Failed to start streaming on the capture device" << std::endl;
return TestFail;
}
ret = output_->streamOn();
if (ret) {
std::cout << "Failed to start streaming on the output device" << std::endl;
return TestFail;
}
timeout.start(10000ms);
while (timeout.isRunning()) {
dispatcher->processEvents();
if (framesCaptured_ > 30 && framesOutput_ > 30)
break;
}
if ((framesCaptured_ < 1) || (framesOutput_ < 1)) {
std::cout << "Failed to process any frames within timeout." << std::endl;
return TestFail;
}
if ((framesCaptured_ < 30) || (framesOutput_ < 30)) {
std::cout << "Failed to process 30 frames within timeout." << std::endl;
return TestFail;
}
ret = capture_->streamOff();
if (ret) {
std::cout << "Failed to stop streaming on the capture device" << std::endl;
return TestFail;
}
ret = output_->streamOff();
if (ret) {
std::cout << "Failed to stop streaming on the output device" << std::endl;
return TestFail;
}
return TestPass;
}
void cleanup()
{
std::cout
<< "Captured " << framesCaptured_ << " frames and "
<< "output " << framesOutput_ << " frames"
<< std::endl;
output_->streamOff();
output_->releaseBuffers();
output_->close();
delete output_;
V4L2VideoDeviceTest::cleanup();
}
private:
const unsigned int bufferCount = 4;
V4L2VideoDevice *output_;
unsigned int framesCaptured_;
unsigned int framesOutput_;
};
TEST_REGISTER(BufferSharingTest)

View File

@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 API tests
*/
#include <iostream>
#include <libcamera/framebuffer.h>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include "v4l2_videodevice_test.h"
using namespace libcamera;
using namespace std::chrono_literals;
class CaptureAsyncTest : public V4L2VideoDeviceTest
{
public:
CaptureAsyncTest()
: V4L2VideoDeviceTest("vimc", "Raw Capture 0"), frames(0) {}
void receiveBuffer(FrameBuffer *buffer)
{
std::cout << "Buffer received" << std::endl;
frames++;
/* Requeue the buffer for further use. */
capture_->queueBuffer(buffer);
}
protected:
int run()
{
const unsigned int bufferCount = 8;
EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
Timer timeout;
int ret;
ret = capture_->allocateBuffers(bufferCount, &buffers_);
if (ret < 0) {
std::cout << "Failed to allocate buffers" << std::endl;
return TestFail;
}
capture_->bufferReady.connect(this, &CaptureAsyncTest::receiveBuffer);
for (const std::unique_ptr<FrameBuffer> &buffer : buffers_) {
if (capture_->queueBuffer(buffer.get())) {
std::cout << "Failed to queue buffer" << std::endl;
return TestFail;
}
}
ret = capture_->streamOn();
if (ret)
return TestFail;
const unsigned int nFrames = 30;
timeout.start(500ms * nFrames);
while (timeout.isRunning()) {
dispatcher->processEvents();
if (frames > nFrames)
break;
}
if (frames < 1) {
std::cout << "Failed to capture any frames within timeout." << std::endl;
return TestFail;
}
if (frames < nFrames) {
std::cout << "Failed to capture " << nFrames
<< " frames within timeout." << std::endl;
return TestFail;
}
std::cout << "Processed " << frames << " frames" << std::endl;
ret = capture_->streamOff();
if (ret)
return TestFail;
return TestPass;
}
private:
unsigned int frames;
};
TEST_REGISTER(CaptureAsyncTest)

View File

@@ -0,0 +1,141 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* V4L2 device controls handling test
*/
#include <algorithm>
#include <array>
#include <iostream>
#include <limits.h>
#include "libcamera/internal/v4l2_videodevice.h"
#include "v4l2_videodevice_test.h"
/* These come from the vivid driver. */
#define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
#define VIVID_CID_INTEGER64 (VIVID_CID_CUSTOM_BASE + 3)
#define VIVID_CID_U8_4D_ARRAY (VIVID_CID_CUSTOM_BASE + 10)
/* Helper for VIVID_CID_U8_4D_ARRAY control array size: not from kernel. */
#define VIVID_CID_U8_ARRAY_SIZE (2 * 3 * 4 * 5)
using namespace std;
using namespace libcamera;
class V4L2ControlTest : public V4L2VideoDeviceTest
{
public:
V4L2ControlTest()
: V4L2VideoDeviceTest("vivid", "vivid-000-vid-cap")
{
}
protected:
int run()
{
const ControlInfoMap &infoMap = capture_->controls();
/* Test control enumeration. */
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() ||
infoMap.find(V4L2_CID_SATURATION) == infoMap.end() ||
infoMap.find(VIVID_CID_INTEGER64) == infoMap.end() ||
infoMap.find(VIVID_CID_U8_4D_ARRAY) == infoMap.end()) {
cerr << "Missing controls" << endl;
return TestFail;
}
const ControlInfo &brightness = infoMap.find(V4L2_CID_BRIGHTNESS)->second;
const ControlInfo &contrast = infoMap.find(V4L2_CID_CONTRAST)->second;
const ControlInfo &saturation = infoMap.find(V4L2_CID_SATURATION)->second;
const ControlInfo &int64 = infoMap.find(VIVID_CID_INTEGER64)->second;
const ControlInfo &u8 = infoMap.find(VIVID_CID_U8_4D_ARRAY)->second;
/* Test getting controls. */
ControlList ctrls = capture_->getControls({ V4L2_CID_BRIGHTNESS,
V4L2_CID_CONTRAST,
V4L2_CID_SATURATION,
VIVID_CID_INTEGER64,
VIVID_CID_U8_4D_ARRAY });
if (ctrls.empty()) {
cerr << "Failed to get controls" << endl;
return TestFail;
}
if (ctrls.infoMap() != &infoMap) {
cerr << "Incorrect infoMap for retrieved controls" << endl;
return TestFail;
}
if (ctrls.get(V4L2_CID_BRIGHTNESS).get<int32_t>() == -1 ||
ctrls.get(V4L2_CID_CONTRAST).get<int32_t>() == -1 ||
ctrls.get(V4L2_CID_SATURATION).get<int32_t>() == -1) {
cerr << "Incorrect value for retrieved controls" << endl;
return TestFail;
}
/*
* The VIVID_CID_INTEGER64 control can take any value, just test
* that its value can be retrieved and has the right type.
*/
ctrls.get(VIVID_CID_INTEGER64).get<int64_t>();
uint8_t u8Min = u8.min().get<uint8_t>();
uint8_t u8Max = u8.max().get<uint8_t>();
Span<const uint8_t> u8Span = ctrls.get(VIVID_CID_U8_4D_ARRAY).get<Span<const uint8_t>>();
bool valid = std::all_of(u8Span.begin(), u8Span.end(),
[&](uint8_t v) { return v >= u8Min && v <= u8Max; });
if (!valid) {
cerr << "Incorrect value for retrieved array control"
<< endl;
return TestFail;
}
/* Test setting controls. */
ctrls.set(V4L2_CID_BRIGHTNESS, brightness.min());
ctrls.set(V4L2_CID_CONTRAST, contrast.max());
ctrls.set(V4L2_CID_SATURATION, saturation.min());
ctrls.set(VIVID_CID_INTEGER64, int64.min());
std::array<uint8_t, VIVID_CID_U8_ARRAY_SIZE> u8Values;
std::fill(u8Values.begin(), u8Values.end(), u8.min().get<uint8_t>());
ctrls.set(VIVID_CID_U8_4D_ARRAY, Span<const uint8_t>(u8Values));
int ret = capture_->setControls(&ctrls);
if (ret) {
cerr << "Failed to set controls" << endl;
return TestFail;
}
/* Test setting controls outside of range. */
ctrls.set(V4L2_CID_BRIGHTNESS, brightness.min().get<int32_t>() - 1);
ctrls.set(V4L2_CID_CONTRAST, contrast.max().get<int32_t>() + 1);
ctrls.set(V4L2_CID_SATURATION, saturation.min().get<int32_t>() + 1);
ret = capture_->setControls(&ctrls);
if (ret) {
cerr << "Failed to set controls (out of range)" << endl;
return TestFail;
}
if (ctrls.get(V4L2_CID_BRIGHTNESS) != brightness.min() ||
ctrls.get(V4L2_CID_CONTRAST) != contrast.max() ||
ctrls.get(V4L2_CID_SATURATION) != saturation.min().get<int32_t>() + 1) {
cerr << "Controls not updated when set" << endl;
return TestFail;
}
return TestPass;
}
};
TEST_REGISTER(V4L2ControlTest)

View File

@@ -0,0 +1,102 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2022, Ideas on Board Oy.
*
* libcamera V4L2 dequeue watchdog test
*/
#include <iostream>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include <libcamera/framebuffer.h>
#include "v4l2_videodevice_test.h"
using namespace libcamera;
using namespace std::chrono_literals;
class DequeueWatchdogTest : public V4L2VideoDeviceTest
{
public:
DequeueWatchdogTest()
: V4L2VideoDeviceTest("vimc", "Raw Capture 0"), frames_(0), barks_(0) {}
protected:
int run()
{
constexpr unsigned int bufferCount = 8;
EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
Timer timeout;
int ret = capture_->allocateBuffers(bufferCount, &buffers_);
if (ret < 0) {
std::cout << "Failed to allocate buffers" << std::endl;
return TestFail;
}
capture_->dequeueTimeout.connect(this, &DequeueWatchdogTest::barkCounter);
capture_->setDequeueTimeout(5ms);
capture_->bufferReady.connect(this, &DequeueWatchdogTest::receiveBuffer);
for (const std::unique_ptr<FrameBuffer> &buffer : buffers_) {
if (capture_->queueBuffer(buffer.get())) {
std::cout << "Failed to queue buffer" << std::endl;
return TestFail;
}
}
ret = capture_->streamOn();
if (ret < 0) {
std::cout << "Failed to start streaming" << std::endl;
return TestFail;
}
timeout.start(5s);
while (timeout.isRunning()) {
dispatcher->processEvents();
if (frames_ > 5)
break;
}
std::cout << "Processed " << frames_ << " frames_ and heard "
<< barks_ << " barks_" << std::endl;
if (!barks_) {
std::cout << "Failed to hear any barks_." << std::endl;
return TestFail;
}
capture_->streamOff();
return TestPass;
}
private:
void receiveBuffer(FrameBuffer *buffer)
{
if (buffer->metadata().status == FrameMetadata::FrameCancelled)
return;
std::cout << "Buffer received" << std::endl;
frames_++;
/* Requeue the buffer for further use. */
capture_->queueBuffer(buffer);
}
void barkCounter()
{
std::cout << "Watchdog is barking" << std::endl;
barks_++;
}
unsigned int frames_;
unsigned int barks_;
};
TEST_REGISTER(DequeueWatchdogTest)

View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 API tests
*/
#include <iostream>
#include "v4l2_videodevice_test.h"
namespace {
class DoubleOpen : public V4L2VideoDeviceTest
{
public:
DoubleOpen()
: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
protected:
int run()
{
int ret;
/*
* Expect failure: The device has already been opened by the
* V4L2VideoDeviceTest base class
*/
ret = capture_->open();
if (!ret) {
std::cout << "Double open erroneously succeeded" << std::endl;
capture_->close();
return TestFail;
}
return TestPass;
}
};
} /* namespace */
TEST_REGISTER(DoubleOpen)

View File

@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 device format handling test
*/
#include <iostream>
#include <limits.h>
#include <libcamera/base/utils.h>
#include "libcamera/internal/v4l2_videodevice.h"
#include "v4l2_videodevice_test.h"
using namespace std;
using namespace libcamera;
class Format : public V4L2VideoDeviceTest
{
public:
Format()
: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
protected:
int run()
{
V4L2DeviceFormat format = {};
int ret = capture_->getFormat(&format);
if (ret) {
cerr << "Failed to get format" << endl;
return TestFail;
}
format.size = { UINT_MAX, UINT_MAX };
ret = capture_->setFormat(&format);
if (ret) {
cerr << "Failed to set format: image resolution is invalid: "
<< "(UINT_MAX x UINT_MAX) but setFormat() should not fail."
<< endl;
return TestFail;
}
if (format.size.width == UINT_MAX ||
format.size.height == UINT_MAX) {
cerr << "Failed to update image format = (UINT_MAX x UINT_MAX)"
<< endl;
return TestFail;
}
std::vector<std::pair<uint32_t, const char *>> formats{
{ V4L2_PIX_FMT_YUYV, "YUYV" },
{ 0, "<INVALID>" },
{ v4l2_fourcc(0, 1, 2, 3), "...." },
{ V4L2_PIX_FMT_Y16_BE, "Y16 -BE" }
};
for (const auto &fmt : formats) {
if (V4L2PixelFormat(fmt.first).toString() != fmt.second) {
cerr << "Failed to convert V4L2PixelFormat"
<< utils::hex(fmt.first) << "to string"
<< endl;
return TestFail;
}
}
if (V4L2PixelFormat().toString() != "<INVALID>") {
cerr << "Failed to convert default V4L2PixelFormat to string"
<< endl;
return TestFail;
}
return TestPass;
}
};
TEST_REGISTER(Format)

View File

@@ -0,0 +1,24 @@
# SPDX-License-Identifier: CC0-1.0
# Tests are listed in order of complexity.
# They are not alphabetically sorted.
v4l2_videodevice_tests = [
{'name': 'double_open', 'sources': ['double_open.cpp']},
{'name': 'controls', 'sources': ['controls.cpp']},
{'name': 'formats', 'sources': ['formats.cpp']},
{'name': 'dequeue_watchdog', 'sources': ['dequeue_watchdog.cpp']},
{'name': 'request_buffers', 'sources': ['request_buffers.cpp']},
{'name': 'buffer_cache', 'sources': ['buffer_cache.cpp']},
{'name': 'stream_on_off', 'sources': ['stream_on_off.cpp']},
{'name': 'capture_async', 'sources': ['capture_async.cpp']},
{'name': 'buffer_sharing', 'sources': ['buffer_sharing.cpp']},
{'name': 'v4l2_m2mdevice', 'sources': ['v4l2_m2mdevice.cpp']},
]
foreach test : v4l2_videodevice_tests
exe = executable(test['name'], [test['sources'], 'v4l2_videodevice_test.cpp'],
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
test(test['name'], exe, suite : 'v4l2_videodevice', is_parallel : false)
endforeach

View File

@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 API tests
*/
#include "v4l2_videodevice_test.h"
class RequestBuffersTest : public V4L2VideoDeviceTest
{
public:
RequestBuffersTest()
: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
protected:
int run()
{
const unsigned int bufferCount = 8;
int ret = capture_->allocateBuffers(bufferCount, &buffers_);
if (ret != bufferCount)
return TestFail;
return TestPass;
}
};
TEST_REGISTER(RequestBuffersTest)

View File

@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 API tests
*/
#include "v4l2_videodevice_test.h"
class StreamOnStreamOffTest : public V4L2VideoDeviceTest
{
public:
StreamOnStreamOffTest()
: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
protected:
int run()
{
const unsigned int bufferCount = 8;
int ret = capture_->allocateBuffers(bufferCount, &buffers_);
if (ret < 0)
return TestFail;
ret = capture_->streamOn();
if (ret)
return TestFail;
ret = capture_->streamOff();
if (ret)
return TestFail;
return TestPass;
}
};
TEST_REGISTER(StreamOnStreamOffTest)

View File

@@ -0,0 +1,211 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 M2M video device tests
*/
#include <iostream>
#include <libcamera/framebuffer.h>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/v4l2_videodevice.h"
#include "test.h"
using namespace libcamera;
using namespace std;
using namespace std::chrono_literals;
class V4L2M2MDeviceTest : public Test
{
public:
V4L2M2MDeviceTest()
: vim2m_(nullptr), outputFrames_(0), captureFrames_(0)
{
}
void outputBufferComplete(FrameBuffer *buffer)
{
cout << "Received output buffer" << endl;
outputFrames_++;
/* Requeue the buffer for further use. */
vim2m_->output()->queueBuffer(buffer);
}
void receiveCaptureBuffer(FrameBuffer *buffer)
{
cout << "Received capture buffer" << endl;
captureFrames_++;
/* Requeue the buffer for further use. */
vim2m_->capture()->queueBuffer(buffer);
}
protected:
int init()
{
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("vim2m");
dm.add("vim2m-source");
dm.add("vim2m-sink");
media_ = enumerator_->search(dm);
if (!media_) {
cerr << "No vim2m device found" << endl;
return TestSkip;
}
return TestPass;
}
int run()
{
constexpr unsigned int bufferCount = 4;
EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
int ret;
MediaEntity *entity = media_->getEntityByName("vim2m-source");
vim2m_ = new V4L2M2MDevice(entity->deviceNode());
if (vim2m_->open()) {
cerr << "Failed to open VIM2M device" << endl;
return TestFail;
}
V4L2VideoDevice *capture = vim2m_->capture();
V4L2VideoDevice *output = vim2m_->output();
if (capture->controls().empty() || output->controls().empty()) {
cerr << "VIM2M device has no control" << endl;
return TestFail;
}
V4L2DeviceFormat format = {};
if (capture->getFormat(&format)) {
cerr << "Failed to get capture format" << endl;
return TestFail;
}
format.size.width = 640;
format.size.height = 480;
if (capture->setFormat(&format)) {
cerr << "Failed to set capture format" << endl;
return TestFail;
}
if (output->setFormat(&format)) {
cerr << "Failed to set output format" << endl;
return TestFail;
}
ret = capture->allocateBuffers(bufferCount, &captureBuffers_);
if (ret < 0) {
cerr << "Failed to allocate Capture Buffers" << endl;
return TestFail;
}
ret = output->allocateBuffers(bufferCount, &outputBuffers_);
if (ret < 0) {
cerr << "Failed to allocate Output Buffers" << endl;
return TestFail;
}
capture->bufferReady.connect(this, &V4L2M2MDeviceTest::receiveCaptureBuffer);
output->bufferReady.connect(this, &V4L2M2MDeviceTest::outputBufferComplete);
for (const std::unique_ptr<FrameBuffer> &buffer : captureBuffers_) {
if (capture->queueBuffer(buffer.get())) {
std::cout << "Failed to queue capture buffer" << std::endl;
return TestFail;
}
}
for (const std::unique_ptr<FrameBuffer> &buffer : outputBuffers_) {
if (output->queueBuffer(buffer.get())) {
std::cout << "Failed to queue output buffer" << std::endl;
return TestFail;
}
}
ret = capture->streamOn();
if (ret) {
cerr << "Failed to streamOn capture" << endl;
return TestFail;
}
ret = output->streamOn();
if (ret) {
cerr << "Failed to streamOn output" << endl;
return TestFail;
}
Timer timeout;
timeout.start(5000ms);
while (timeout.isRunning()) {
dispatcher->processEvents();
if (captureFrames_ > 30)
break;
}
cerr << "Output " << outputFrames_ << " frames" << std::endl;
cerr << "Captured " << captureFrames_ << " frames" << std::endl;
if (captureFrames_ < 30) {
cerr << "Failed to capture 30 frames within timeout." << std::endl;
return TestFail;
}
ret = capture->streamOff();
if (ret) {
cerr << "Failed to StreamOff the capture device." << std::endl;
return TestFail;
}
ret = output->streamOff();
if (ret) {
cerr << "Failed to StreamOff the output device." << std::endl;
return TestFail;
}
return TestPass;
}
void cleanup()
{
delete vim2m_;
}
private:
std::unique_ptr<DeviceEnumerator> enumerator_;
std::shared_ptr<MediaDevice> media_;
V4L2M2MDevice *vim2m_;
std::vector<std::unique_ptr<FrameBuffer>> captureBuffers_;
std::vector<std::unique_ptr<FrameBuffer>> outputBuffers_;
unsigned int outputFrames_;
unsigned int captureFrames_;
};
TEST_REGISTER(V4L2M2MDeviceTest)

View File

@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera V4L2 API tests
*/
#include <iostream>
#include <linux/media-bus-format.h>
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/media_device.h"
#include "v4l2_videodevice_test.h"
using namespace std;
using namespace libcamera;
int V4L2VideoDeviceTest::init()
{
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(driver_);
dm.add(entity_);
media_ = enumerator_->search(dm);
if (!media_)
return TestSkip;
MediaEntity *entity = media_->getEntityByName(entity_);
if (!entity)
return TestSkip;
capture_ = new V4L2VideoDevice(entity);
if (!capture_)
return TestFail;
if (!media_->acquire())
return TestFail;
int ret = media_->disableLinks();
media_->release();
if (ret)
return TestFail;
if (capture_->open())
return TestFail;
V4L2DeviceFormat format = {};
if (capture_->getFormat(&format))
return TestFail;
format.size.width = 640;
format.size.height = 480;
if (driver_ == "vimc") {
sensor_ = new CameraSensor(media_->getEntityByName("Sensor A"));
if (sensor_->init())
return TestFail;
debayer_ = new V4L2Subdevice(media_->getEntityByName("Debayer A"));
if (debayer_->open())
return TestFail;
format.fourcc = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8);
V4L2SubdeviceFormat subformat = {};
subformat.code = MEDIA_BUS_FMT_SBGGR8_1X8;
subformat.size = format.size;
if (sensor_->setFormat(&subformat))
return TestFail;
if (debayer_->setFormat(0, &subformat))
return TestFail;
}
if (capture_->setFormat(&format))
return TestFail;
return TestPass;
}
void V4L2VideoDeviceTest::cleanup()
{
capture_->streamOff();
capture_->releaseBuffers();
capture_->close();
delete debayer_;
delete sensor_;
delete capture_;
}

View File

@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2018, Google Inc.
*
* libcamera v4l2device test base class
*/
#pragma once
#include <memory>
#include <libcamera/framebuffer.h>
#include "libcamera/internal/camera_sensor.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
#include "test.h"
class V4L2VideoDeviceTest : public Test
{
public:
V4L2VideoDeviceTest(const char *driver, const char *entity)
: driver_(driver), entity_(entity), sensor_(nullptr),
debayer_(nullptr), capture_(nullptr)
{
}
protected:
int init();
void cleanup();
std::string driver_;
std::string entity_;
std::unique_ptr<libcamera::DeviceEnumerator> enumerator_;
std::shared_ptr<libcamera::MediaDevice> media_;
libcamera::CameraSensor *sensor_;
libcamera::V4L2Subdevice *debayer_;
libcamera::V4L2VideoDevice *capture_;
std::vector<std::unique_ptr<libcamera::FrameBuffer>> buffers_;
};