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,182 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera Camera API tests
*
* Test importing buffers exported from the VIVID output device into a Camera
*/
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
#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 "buffer_source.h"
#include "camera_test.h"
#include "test.h"
using namespace libcamera;
using namespace std::chrono_literals;
namespace {
class BufferImportTest : public CameraTest, public Test
{
public:
BufferImportTest()
: CameraTest("platform/vimc.0 Sensor B")
{
}
protected:
void bufferComplete([[maybe_unused]] Request *request,
FrameBuffer *buffer)
{
if (buffer->metadata().status != FrameMetadata::FrameSuccess)
return;
completeBuffersCount_++;
}
void requestComplete(Request *request)
{
if (request->status() != Request::RequestComplete)
return;
const Request::BufferMap &buffers = request->buffers();
completeRequestsCount_++;
/* Create a new request. */
const Stream *stream = buffers.begin()->first;
FrameBuffer *buffer = buffers.begin()->second;
request->reuse();
request->addBuffer(stream, buffer);
camera_->queueRequest(request);
dispatcher_->interrupt();
}
int init() override
{
if (status_ != TestPass)
return status_;
config_ = camera_->generateConfiguration({ StreamRole::VideoRecording });
if (!config_ || config_->size() != 1) {
std::cout << "Failed to generate default configuration" << std::endl;
return TestFail;
}
dispatcher_ = Thread::current()->eventDispatcher();
return TestPass;
}
int run() override
{
StreamConfiguration &cfg = config_->at(0);
if (camera_->acquire()) {
std::cout << "Failed to acquire the camera" << std::endl;
return TestFail;
}
if (camera_->configure(config_.get())) {
std::cout << "Failed to set default configuration" << std::endl;
return TestFail;
}
Stream *stream = cfg.stream();
BufferSource source;
int ret = source.allocate(cfg);
if (ret != TestPass)
return ret;
for (const std::unique_ptr<FrameBuffer> &buffer : source.buffers()) {
std::unique_ptr<Request> request = camera_->createRequest();
if (!request) {
std::cout << "Failed to create request" << std::endl;
return TestFail;
}
if (request->addBuffer(stream, buffer.get())) {
std::cout << "Failed to associating buffer with request" << std::endl;
return TestFail;
}
requests_.push_back(std::move(request));
}
completeRequestsCount_ = 0;
completeBuffersCount_ = 0;
camera_->bufferCompleted.connect(this, &BufferImportTest::bufferComplete);
camera_->requestCompleted.connect(this, &BufferImportTest::requestComplete);
if (camera_->start()) {
std::cout << "Failed to start camera" << std::endl;
return TestFail;
}
for (std::unique_ptr<Request> &request : requests_) {
if (camera_->queueRequest(request.get())) {
std::cout << "Failed to queue request" << std::endl;
return TestFail;
}
}
const unsigned int nFrames = cfg.bufferCount * 2;
Timer timer;
timer.start(500ms * nFrames);
while (timer.isRunning()) {
dispatcher_->processEvents();
if (completeRequestsCount_ > nFrames)
break;
}
if (completeRequestsCount_ < nFrames) {
std::cout << "Failed to capture enough frames (got "
<< completeRequestsCount_ << " expected at least "
<< nFrames << ")" << std::endl;
return TestFail;
}
if (completeRequestsCount_ != completeBuffersCount_) {
std::cout << "Number of completed buffers and requests differ" << std::endl;
return TestFail;
}
if (camera_->stop()) {
std::cout << "Failed to stop camera" << std::endl;
return TestFail;
}
return TestPass;
}
private:
EventDispatcher *dispatcher_;
std::vector<std::unique_ptr<Request>> requests_;
unsigned int completeBuffersCount_;
unsigned int completeRequestsCount_;
std::unique_ptr<CameraConfiguration> config_;
};
} /* namespace */
TEST_REGISTER(BufferImportTest)

View File

@@ -0,0 +1,264 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2021, Google Inc.
*
* Test:
* - Multiple reconfigurations of the Camera without stopping the CameraManager
* - Validate there are no file descriptor leaks when using IPC
*/
#include <dirent.h>
#include <fstream>
#include <iostream>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/file.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include <libcamera/framebuffer_allocator.h>
#include "camera_test.h"
#include "test.h"
using namespace libcamera;
using namespace std;
using namespace std::chrono_literals;
namespace {
class CameraReconfigure : public CameraTest, public Test
{
public:
/* Initialize CameraTest with isolated IPA */
CameraReconfigure()
: CameraTest(kCamId_, true)
{
}
private:
static constexpr const char *kCamId_ = "platform/vimc.0 Sensor B";
static constexpr const char *kIpaProxyName_ = "vimc_ipa_proxy";
static constexpr unsigned int kNumOfReconfigures_ = 10;
void requestComplete(Request *request)
{
if (request->status() != Request::RequestComplete)
return;
const Request::BufferMap &buffers = request->buffers();
const Stream *stream = buffers.begin()->first;
FrameBuffer *buffer = buffers.begin()->second;
/* Reuse the request and re-queue it with the same buffers. */
request->reuse();
request->addBuffer(stream, buffer);
camera_->queueRequest(request);
}
int startAndStop()
{
StreamConfiguration &cfg = config_->at(0);
if (camera_->acquire()) {
cerr << "Failed to acquire the camera" << endl;
return TestFail;
}
if (camera_->configure(config_.get())) {
cerr << "Failed to set default configuration" << endl;
return TestFail;
}
Stream *stream = cfg.stream();
/*
* The configuration is consistent so we can re-use the
* same buffer allocation for each run.
*/
if (!allocated_) {
int ret = allocator_->allocate(stream);
if (ret < 0) {
cerr << "Failed to allocate buffers" << endl;
return TestFail;
}
allocated_ = true;
}
for (const unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {
unique_ptr<Request> request = camera_->createRequest();
if (!request) {
cerr << "Failed to create request" << endl;
return TestFail;
}
if (request->addBuffer(stream, buffer.get())) {
cerr << "Failed to associate buffer with request" << endl;
return TestFail;
}
requests_.push_back(std::move(request));
}
camera_->requestCompleted.connect(this, &CameraReconfigure::requestComplete);
if (camera_->start()) {
cerr << "Failed to start camera" << endl;
return TestFail;
}
for (unique_ptr<Request> &request : requests_) {
if (camera_->queueRequest(request.get())) {
cerr << "Failed to queue request" << endl;
return TestFail;
}
}
EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
Timer timer;
timer.start(100ms);
while (timer.isRunning())
dispatcher->processEvents();
if (camera_->stop()) {
cerr << "Failed to stop camera" << endl;
return TestFail;
}
if (camera_->release()) {
cerr << "Failed to release camera" << endl;
return TestFail;
}
camera_->requestCompleted.disconnect(this);
requests_.clear();
return 0;
}
int fdsOpen(pid_t pid)
{
string proxyFdPath = "/proc/" + to_string(pid) + "/fd";
DIR *dir;
struct dirent *ptr;
unsigned int openFds = 0;
dir = opendir(proxyFdPath.c_str());
if (dir == nullptr) {
int err = errno;
cerr << "Error opening " << proxyFdPath << ": "
<< strerror(-err) << endl;
return 0;
}
while ((ptr = readdir(dir)) != nullptr) {
if ((strcmp(ptr->d_name, ".") == 0) ||
(strcmp(ptr->d_name, "..") == 0))
continue;
openFds++;
}
closedir(dir);
return openFds;
}
pid_t findProxyPid()
{
string proxyPid;
string proxyName(kIpaProxyName_);
DIR *dir;
struct dirent *ptr;
dir = opendir("/proc");
while ((ptr = readdir(dir)) != nullptr) {
if (ptr->d_type != DT_DIR)
continue;
string pname("/proc/" + string(ptr->d_name) + "/comm");
if (File::exists(pname)) {
ifstream pfile(pname.c_str());
string comm;
getline(pfile, comm);
pfile.close();
proxyPid = comm == proxyName ? string(ptr->d_name) : "";
}
if (!proxyPid.empty())
break;
}
closedir(dir);
if (!proxyPid.empty())
return atoi(proxyPid.c_str());
return -1;
}
int init() override
{
if (status_ != TestPass)
return status_;
config_ = camera_->generateConfiguration({ StreamRole::StillCapture });
if (!config_ || config_->size() != 1) {
cerr << "Failed to generate default configuration" << endl;
return TestFail;
}
allocator_ = make_unique<FrameBufferAllocator>(camera_);
allocated_ = false;
return TestPass;
}
int run() override
{
unsigned int openFdsAtStart = 0;
unsigned int openFds = 0;
pid_t proxyPid = findProxyPid();
if (proxyPid < 0) {
cerr << "Cannot find " << kIpaProxyName_
<< " pid, exiting" << endl;
return TestFail;
}
openFdsAtStart = fdsOpen(proxyPid);
for (unsigned int i = 0; i < kNumOfReconfigures_; i++) {
startAndStop();
openFds = fdsOpen(proxyPid);
if (openFds == 0) {
cerr << "No open fds found whereas "
<< "open fds at start: " << openFdsAtStart
<< endl;
return TestFail;
}
if (openFds != openFdsAtStart) {
cerr << "Leaking fds for " << kIpaProxyName_
<< " - Open fds: " << openFds << " vs "
<< "Open fds at start: " << openFdsAtStart
<< endl;
return TestFail;
}
}
return TestPass;
}
bool allocated_;
vector<unique_ptr<Request>> requests_;
unique_ptr<CameraConfiguration> config_;
unique_ptr<FrameBufferAllocator> allocator_;
};
} /* namespace */
TEST_REGISTER(CameraReconfigure)

View File

@@ -0,0 +1,181 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera Camera API tests
*/
#include <iostream>
#include <libcamera/framebuffer_allocator.h>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>
#include "camera_test.h"
#include "test.h"
using namespace libcamera;
using namespace std;
using namespace std::chrono_literals;
namespace {
class Capture : public CameraTest, public Test
{
public:
Capture()
: CameraTest("platform/vimc.0 Sensor B")
{
}
protected:
unsigned int completeBuffersCount_;
unsigned int completeRequestsCount_;
void bufferComplete([[maybe_unused]] Request *request,
FrameBuffer *buffer)
{
if (buffer->metadata().status != FrameMetadata::FrameSuccess)
return;
completeBuffersCount_++;
}
void requestComplete(Request *request)
{
if (request->status() != Request::RequestComplete)
return;
const Request::BufferMap &buffers = request->buffers();
completeRequestsCount_++;
/* Create a new request. */
const Stream *stream = buffers.begin()->first;
FrameBuffer *buffer = buffers.begin()->second;
request->reuse();
request->addBuffer(stream, buffer);
camera_->queueRequest(request);
dispatcher_->interrupt();
}
int init() override
{
if (status_ != TestPass)
return status_;
config_ = camera_->generateConfiguration({ StreamRole::VideoRecording });
if (!config_ || config_->size() != 1) {
cout << "Failed to generate default configuration" << endl;
return TestFail;
}
allocator_ = new FrameBufferAllocator(camera_);
dispatcher_ = Thread::current()->eventDispatcher();
return TestPass;
}
void cleanup() override
{
delete allocator_;
}
int run() override
{
StreamConfiguration &cfg = config_->at(0);
if (camera_->acquire()) {
cout << "Failed to acquire the camera" << endl;
return TestFail;
}
if (camera_->configure(config_.get())) {
cout << "Failed to set default configuration" << endl;
return TestFail;
}
Stream *stream = cfg.stream();
int ret = allocator_->allocate(stream);
if (ret < 0)
return TestFail;
for (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {
std::unique_ptr<Request> request = camera_->createRequest();
if (!request) {
cout << "Failed to create request" << endl;
return TestFail;
}
if (request->addBuffer(stream, buffer.get())) {
cout << "Failed to associate buffer with request" << endl;
return TestFail;
}
requests_.push_back(std::move(request));
}
completeRequestsCount_ = 0;
completeBuffersCount_ = 0;
camera_->bufferCompleted.connect(this, &Capture::bufferComplete);
camera_->requestCompleted.connect(this, &Capture::requestComplete);
if (camera_->start()) {
cout << "Failed to start camera" << endl;
return TestFail;
}
for (std::unique_ptr<Request> &request : requests_) {
if (camera_->queueRequest(request.get())) {
cout << "Failed to queue request" << endl;
return TestFail;
}
}
unsigned int nFrames = allocator_->buffers(stream).size() * 2;
Timer timer;
timer.start(500ms * nFrames);
while (timer.isRunning()) {
dispatcher_->processEvents();
if (completeRequestsCount_ > nFrames)
break;
}
if (completeRequestsCount_ < nFrames) {
cout << "Failed to capture enough frames (got "
<< completeRequestsCount_ << " expected at least "
<< nFrames * 2 << ")" << endl;
return TestFail;
}
if (completeRequestsCount_ != completeBuffersCount_) {
cout << "Number of completed buffers and requests differ" << endl;
return TestFail;
}
if (camera_->stop()) {
cout << "Failed to stop camera" << endl;
return TestFail;
}
return TestPass;
}
EventDispatcher *dispatcher_;
std::vector<std::unique_ptr<Request>> requests_;
std::unique_ptr<CameraConfiguration> config_;
FrameBufferAllocator *allocator_;
};
} /* namespace */
TEST_REGISTER(Capture)

View File

@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera Camera API tests
*/
#include <iostream>
#include "camera_test.h"
#include "test.h"
using namespace libcamera;
using namespace std;
namespace {
class ConfigurationDefault : public CameraTest, public Test
{
public:
ConfigurationDefault()
: CameraTest("platform/vimc.0 Sensor B")
{
}
protected:
int init() override
{
return status_;
}
int run() override
{
std::unique_ptr<CameraConfiguration> config;
/* Test asking for configuration for a video stream. */
config = camera_->generateConfiguration({ StreamRole::VideoRecording });
if (!config || config->size() != 1) {
cout << "Default configuration invalid" << endl;
return TestFail;
}
/*
* Test that asking for configuration for an empty array of
* stream roles returns an empty camera configuration.
*/
config = camera_->generateConfiguration({});
if (!config || config->size() != 0) {
cout << "Failed to retrieve configuration for empty roles list"
<< endl;
return TestFail;
}
return TestPass;
}
};
} /* namespace */
TEST_REGISTER(ConfigurationDefault)

View File

@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera Camera API tests
*/
#include <iostream>
#include "camera_test.h"
#include "test.h"
using namespace libcamera;
using namespace std;
namespace {
class ConfigurationSet : public CameraTest, public Test
{
public:
ConfigurationSet()
: CameraTest("platform/vimc.0 Sensor B")
{
}
protected:
int init() override
{
if (status_ != TestPass)
return status_;
config_ = camera_->generateConfiguration({ StreamRole::VideoRecording });
if (!config_ || config_->size() != 1) {
cout << "Failed to generate default configuration" << endl;
return TestFail;
}
return TestPass;
}
int run() override
{
StreamConfiguration &cfg = config_->at(0);
if (camera_->acquire()) {
cout << "Failed to acquire the camera" << endl;
return TestFail;
}
/* Test that setting the default configuration works. */
if (camera_->configure(config_.get())) {
cout << "Failed to set default configuration" << endl;
return TestFail;
}
/*
* Test that configuring the camera fails if it is not
* acquired, this will also test release and reacquiring
* of the camera.
*/
if (camera_->release()) {
cout << "Failed to release the camera" << endl;
return TestFail;
}
if (!camera_->configure(config_.get())) {
cout << "Setting configuration on a camera not acquired succeeded when it should have failed"
<< endl;
return TestFail;
}
if (camera_->acquire()) {
cout << "Failed to acquire the camera" << endl;
return TestFail;
}
/*
* Test that modifying the default configuration works. Doubling
* the default configuration of the VIMC camera is known to
* work.
*/
cfg.size.width *= 2;
cfg.size.height *= 2;
if (camera_->configure(config_.get())) {
cout << "Failed to set modified configuration" << endl;
return TestFail;
}
/*
* Test that setting an invalid configuration fails.
*/
cfg.size = { 0, 0 };
if (!camera_->configure(config_.get())) {
cout << "Invalid configuration incorrectly accepted" << endl;
return TestFail;
}
return TestPass;
}
std::unique_ptr<CameraConfiguration> config_;
};
} /* namespace */
TEST_REGISTER(ConfigurationSet)

View File

@@ -0,0 +1,20 @@
# SPDX-License-Identifier: CC0-1.0
# Tests are listed in order of complexity.
# They are not alphabetically sorted.
camera_tests = [
{'name': 'configuration_default', 'sources': ['configuration_default.cpp']},
{'name': 'configuration_set', 'sources': ['configuration_set.cpp']},
{'name': 'buffer_import', 'sources': ['buffer_import.cpp']},
{'name': 'statemachine', 'sources': ['statemachine.cpp']},
{'name': 'capture', 'sources': ['capture.cpp']},
{'name': 'camera_reconfigure', 'sources': ['camera_reconfigure.cpp']},
]
foreach test : camera_tests
exe = executable(test['name'], test['sources'],
dependencies : libcamera_private,
link_with : test_libraries,
include_directories : test_includes_internal)
test(test['name'], exe, suite : 'camera', is_parallel : false)
endforeach

View File

@@ -0,0 +1,216 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* libcamera Camera API tests
*/
#include <iostream>
#include <libcamera/framebuffer_allocator.h>
#include "camera_test.h"
#include "test.h"
using namespace libcamera;
using namespace std;
namespace {
class Statemachine : public CameraTest, public Test
{
public:
Statemachine()
: CameraTest("platform/vimc.0 Sensor B")
{
}
protected:
int testAvailable()
{
/* Test operations which should fail. */
if (camera_->configure(defconf_.get()) != -EACCES)
return TestFail;
if (camera_->createRequest())
return TestFail;
if (camera_->start() != -EACCES)
return TestFail;
Request request(camera_.get());
if (camera_->queueRequest(&request) != -EACCES)
return TestFail;
/* Test operations which should pass. */
if (camera_->release())
return TestFail;
if (camera_->stop())
return TestFail;
/* Test valid state transitions, end in Acquired state. */
if (camera_->acquire())
return TestFail;
return TestPass;
}
int testAcquired()
{
/* Test operations which should fail. */
if (camera_->acquire() != -EBUSY)
return TestFail;
if (camera_->createRequest())
return TestFail;
if (camera_->start() != -EACCES)
return TestFail;
Request request(camera_.get());
if (camera_->queueRequest(&request) != -EACCES)
return TestFail;
/* Test operations which should pass. */
if (camera_->stop())
return TestFail;
/* Test valid state transitions, end in Configured state. */
if (camera_->release())
return TestFail;
if (camera_->acquire())
return TestFail;
if (camera_->configure(defconf_.get()))
return TestFail;
return TestPass;
}
int testConfigured()
{
/* Test operations which should fail. */
if (camera_->acquire() != -EBUSY)
return TestFail;
Request request1(camera_.get());
if (camera_->queueRequest(&request1) != -EACCES)
return TestFail;
/* Test operations which should pass. */
std::unique_ptr<Request> request2 = camera_->createRequest();
if (!request2)
return TestFail;
if (camera_->stop())
return TestFail;
/* Test valid state transitions, end in Running state. */
if (camera_->release())
return TestFail;
if (camera_->acquire())
return TestFail;
if (camera_->configure(defconf_.get()))
return TestFail;
/* Use internally allocated buffers. */
allocator_ = new FrameBufferAllocator(camera_);
Stream *stream = *camera_->streams().begin();
if (allocator_->allocate(stream) < 0)
return TestFail;
if (camera_->start())
return TestFail;
return TestPass;
}
int testRuning()
{
/* Test operations which should fail. */
if (camera_->acquire() != -EBUSY)
return TestFail;
if (camera_->release() != -EBUSY)
return TestFail;
if (camera_->configure(defconf_.get()) != -EACCES)
return TestFail;
if (camera_->start() != -EACCES)
return TestFail;
/* Test operations which should pass. */
std::unique_ptr<Request> request = camera_->createRequest();
if (!request)
return TestFail;
Stream *stream = *camera_->streams().begin();
if (request->addBuffer(stream, allocator_->buffers(stream)[0].get()))
return TestFail;
if (camera_->queueRequest(request.get()))
return TestFail;
/* Test valid state transitions, end in Available state. */
if (camera_->stop())
return TestFail;
delete allocator_;
if (camera_->release())
return TestFail;
return TestPass;
}
int init() override
{
if (status_ != TestPass)
return status_;
defconf_ = camera_->generateConfiguration({ StreamRole::VideoRecording });
if (!defconf_) {
cout << "Failed to generate default configuration" << endl;
return TestFail;
}
return TestPass;
}
int run() override
{
if (testAvailable() != TestPass) {
cout << "State machine in Available state failed" << endl;
return TestFail;
}
if (testAcquired() != TestPass) {
cout << "State machine in Acquired state failed" << endl;
return TestFail;
}
if (testConfigured() != TestPass) {
cout << "State machine in Configured state failed" << endl;
return TestFail;
}
if (testRuning() != TestPass) {
cout << "State machine in Running state failed" << endl;
return TestFail;
}
return TestPass;
}
std::unique_ptr<CameraConfiguration> defconf_;
FrameBufferAllocator *allocator_;
};
} /* namespace */
TEST_REGISTER(Statemachine)