summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/camera/buffer_import.cpp389
1 files changed, 111 insertions, 278 deletions
diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp
index 3ba6ce96..327db791 100644
--- a/test/camera/buffer_import.cpp
+++ b/test/camera/buffer_import.cpp
@@ -22,19 +22,32 @@
using namespace libcamera;
-/* Keep SINK_BUFFER_COUNT > CAMERA_BUFFER_COUNT + 1 */
-static constexpr unsigned int SINK_BUFFER_COUNT = 8;
-static constexpr unsigned int CAMERA_BUFFER_COUNT = 4;
+namespace {
-class FrameSink
+/* A provider of external buffers, suitable for import by a Camera. */
+class BufferSource
{
public:
- FrameSink()
+ BufferSource()
: video_(nullptr)
{
}
- int init()
+ ~BufferSource()
+ {
+ if (video_) {
+ video_->releaseBuffers();
+ video_->close();
+ }
+
+ delete video_;
+ video_ = nullptr;
+
+ if (media_)
+ media_->release();
+ }
+
+ int allocate(const StreamConfiguration &config)
{
int ret;
@@ -72,187 +85,30 @@ public:
return TestFail;
/* Configure the format. */
- ret = video_->getFormat(&format_);
+ V4L2DeviceFormat format;
+ ret = video_->getFormat(&format);
if (ret) {
std::cout << "Failed to get format on output device" << std::endl;
return ret;
}
- format_.size.width = 1920;
- format_.size.height = 1080;
- format_.fourcc = V4L2_PIX_FMT_RGB24;
- format_.planesCount = 1;
- format_.planes[0].size = 1920 * 1080 * 3;
- format_.planes[0].bpl = 1920 * 3;
-
- if (video_->setFormat(&format_)) {
- cleanup();
- return TestFail;
- }
-
- /* Export the buffers to a pool. */
- pool_.createBuffers(SINK_BUFFER_COUNT);
- ret = video_->exportBuffers(&pool_);
- if (ret) {
- std::cout << "Failed to export buffers" << std::endl;
- cleanup();
+ format.size = config.size;
+ format.fourcc = V4L2VideoDevice::toV4L2Fourcc(config.pixelFormat, false);
+ if (video_->setFormat(&format))
return TestFail;
- }
-
- /* Only use the first CAMERA_BUFFER_COUNT buffers to start with. */
- availableBuffers_.resize(CAMERA_BUFFER_COUNT);
- std::iota(availableBuffers_.begin(), availableBuffers_.end(), 0);
-
- /* Connect the buffer ready signal. */
- video_->bufferReady.connect(this, &FrameSink::bufferComplete);
-
- return TestPass;
- }
-
- void cleanup()
- {
- if (video_) {
- video_->streamOff();
- video_->releaseBuffers();
- video_->close();
-
- delete video_;
- video_ = nullptr;
- }
-
- if (media_)
- media_->release();
- }
-
- int start()
- {
- requestsCount_ = 0;
- done_ = false;
-
- int ret = video_->streamOn();
- if (ret < 0)
- return ret;
-
- /* Queue all the initial requests. */
- for (unsigned int index = 0; index < CAMERA_BUFFER_COUNT; ++index)
- queueRequest(index);
- return 0;
+ return video_->exportBuffers(config.bufferCount, &buffers_);
}
- int stop()
+ const std::vector<std::unique_ptr<FrameBuffer>> &buffers()
{
- return video_->streamOff();
+ return buffers_;
}
- void requestComplete(uint64_t cookie, const Buffer *metadata)
- {
- unsigned int index = cookie;
-
- Buffer *buffer = new Buffer(index, metadata);
- int ret = video_->queueBuffer(buffer);
- if (ret < 0)
- std::cout << "Failed to queue buffer to sink" << std::endl;
- }
-
- bool done() const { return done_; }
-
- PixelFormat format() const
- {
- return video_->toPixelFormat(format_.fourcc);
- }
-
- const Size &size() const
- {
- return format_.size;
- }
-
- Signal<uint64_t, int> requestReady;
-
private:
- void queueRequest(unsigned int index)
- {
- auto it = std::find(availableBuffers_.begin(),
- availableBuffers_.end(), index);
- availableBuffers_.erase(it);
-
- uint64_t cookie = index;
- BufferMemory &mem = pool_.buffers()[index];
- int dmabuf = mem.planes()[0].fd.fd();
-
- requestReady.emit(cookie, dmabuf);
-
- requestsCount_++;
- }
-
- void bufferComplete(Buffer *buffer)
- {
- availableBuffers_.push_back(buffer->index());
-
- /*
- * Pick the buffer for the next request among the available
- * buffers.
- *
- * For the first 20 frames, select the buffer that has just
- * completed to keep the mapping of dmabuf fds to buffers
- * unchanged in the camera.
- *
- * For the next 20 frames, cycle randomly over the available
- * buffers. The mapping should still be kept unchanged, as the
- * camera should map using the cached fds.
- *
- * For the last 20 frames, cycles through all buffers, which
- * should trash the mappings.
- */
- unsigned int index = buffer->index();
- delete buffer;
-
- std::cout << "Completed buffer, request=" << requestsCount_
- << ", available buffers=" << availableBuffers_.size()
- << std::endl;
-
- if (requestsCount_ >= 60) {
- if (availableBuffers_.size() == SINK_BUFFER_COUNT)
- done_ = true;
- return;
- }
-
- if (requestsCount_ == 40) {
- /* Add the remaining of the buffers. */
- for (unsigned int i = CAMERA_BUFFER_COUNT;
- i < SINK_BUFFER_COUNT; ++i)
- availableBuffers_.push_back(i);
- }
-
- if (requestsCount_ >= 20) {
- /*
- * Wait until we have enough buffers to make this
- * meaningful. Preferably half of the camera buffers,
- * but no less than 2 in any case.
- */
- const unsigned int min_pool_size =
- std::min(CAMERA_BUFFER_COUNT / 2, 2U);
- if (availableBuffers_.size() < min_pool_size)
- return;
-
- /* Pick a buffer at random. */
- unsigned int pos = random_() % availableBuffers_.size();
- index = availableBuffers_[pos];
- }
-
- queueRequest(index);
- }
-
std::shared_ptr<MediaDevice> media_;
V4L2VideoDevice *video_;
- BufferPool pool_;
- V4L2DeviceFormat format_;
-
- unsigned int requestsCount_;
- std::vector<int> availableBuffers_;
- std::random_device random_;
-
- bool done_;
+ std::vector<std::unique_ptr<FrameBuffer>> buffers_;
};
class BufferImportTest : public CameraTest, public Test
@@ -263,175 +119,152 @@ public:
{
}
- void queueRequest(uint64_t cookie, int dmabuf)
- {
- Request *request = camera_->createRequest(cookie);
-
- std::unique_ptr<Buffer> buffer = stream_->createBuffer({ dmabuf, -1, -1 });
- request->addBuffer(stream_, move(buffer));
- camera_->queueRequest(request);
- }
-
protected:
void bufferComplete(Request *request, Buffer *buffer)
{
if (buffer->metadata().status != FrameMetadata::FrameSuccess)
return;
- unsigned int index = buffer->index();
- int dmabuf = buffer->dmabufs()[0];
-
- /* Record dmabuf to index remappings. */
- bool remapped = false;
- if (bufferMappings_.find(index) != bufferMappings_.end()) {
- if (bufferMappings_[index] != dmabuf)
- remapped = true;
- }
+ completeBuffersCount_++;
+ }
- std::cout << "Completed request " << framesCaptured_
- << ": dmabuf fd " << dmabuf
- << " -> index " << index
- << " (" << (remapped ? 'R' : '-') << ")"
- << std::endl;
+ void requestComplete(Request *request)
+ {
+ if (request->status() != Request::RequestComplete)
+ return;
- if (remapped)
- bufferRemappings_.push_back(framesCaptured_);
+ const std::map<Stream *, Buffer *> &buffers = request->buffers();
- bufferMappings_[index] = dmabuf;
- framesCaptured_++;
+ completeRequestsCount_++;
- sink_.requestComplete(request->cookie(), buffer);
+ /* Create a new request. */
+ Stream *stream = buffers.begin()->first;
+ int dmabuf = buffers.begin()->second->dmabufs()[0];
+ std::unique_ptr<Buffer> buffer = stream->createBuffer({ dmabuf, -1, -1 });
- if (framesCaptured_ == 60)
- sink_.stop();
+ request = camera_->createRequest();
+ request->addBuffer(stream, std::move(buffer));
+ camera_->queueRequest(request);
}
- int initCamera()
+ int init() override
{
- if (camera_->acquire()) {
- std::cout << "Failed to acquire the camera" << std::endl;
- return TestFail;
- }
+ if (status_ != TestPass)
+ return status_;
- /*
- * Configure the Stream to work with externally allocated
- * buffers by setting the memoryType to ExternalMemory.
- */
- std::unique_ptr<CameraConfiguration> config;
- config = camera_->generateConfiguration({ StreamRole::VideoRecording });
- if (!config || config->size() != 1) {
- std::cout << "Failed to generate configuration" << std::endl;
+ config_ = camera_->generateConfiguration({ StreamRole::VideoRecording });
+ if (!config_ || config_->size() != 1) {
+ std::cout << "Failed to generate default configuration" << std::endl;
return TestFail;
}
- StreamConfiguration &cfg = config->at(0);
- cfg.size = sink_.size();
- cfg.pixelFormat = sink_.format();
- cfg.bufferCount = CAMERA_BUFFER_COUNT;
+ StreamConfiguration &cfg = config_->at(0);
cfg.memoryType = ExternalMemory;
- if (camera_->configure(config.get())) {
- std::cout << "Failed to set configuration" << std::endl;
+ return TestPass;
+ }
+
+ int run() override
+ {
+ StreamConfiguration &cfg = config_->at(0);
+
+ if (camera_->acquire()) {
+ std::cout << "Failed to acquire the camera" << std::endl;
return TestFail;
}
- stream_ = cfg.stream();
+ if (camera_->configure(config_.get())) {
+ std::cout << "Failed to set default configuration" << std::endl;
+ return TestFail;
+ }
- /* Allocate buffers. */
if (camera_->allocateBuffers()) {
std::cout << "Failed to allocate buffers" << std::endl;
return TestFail;
}
- /* Connect the buffer completed signal. */
- camera_->bufferCompleted.connect(this, &BufferImportTest::bufferComplete);
+ Stream *stream = cfg.stream();
- return TestPass;
- }
+ BufferSource source;
+ int ret = source.allocate(cfg);
+ if (ret < 0)
+ return TestFail;
- int init()
- {
- if (status_ != TestPass)
- return status_;
+ std::vector<Request *> requests;
+ for (const std::unique_ptr<FrameBuffer> &framebuffer : source.buffers()) {
+ int dmabuf = framebuffer->planes()[0].fd.fd();
- int ret = sink_.init();
- if (ret != TestPass) {
- cleanup();
- return ret;
- }
+ Request *request = camera_->createRequest();
+ if (!request) {
+ std::cout << "Failed to create request" << std::endl;
+ return TestFail;
+ }
- ret = initCamera();
- if (ret != TestPass) {
- cleanup();
- return ret;
- }
+ std::unique_ptr<Buffer> buffer = stream->createBuffer({ dmabuf, -1, -1 });
+ if (request->addBuffer(stream, std::move(buffer))) {
+ std::cout << "Failed to associating buffer with request" << std::endl;
+ return TestFail;
+ }
- sink_.requestReady.connect(this, &BufferImportTest::queueRequest);
- return TestPass;
- }
+ requests.push_back(request);
+ }
- int run()
- {
- int ret;
+ completeRequestsCount_ = 0;
+ completeBuffersCount_ = 0;
- framesCaptured_ = 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;
}
- ret = sink_.start();
- if (ret < 0) {
- std::cout << "Failed to start sink" << std::endl;
- return TestFail;
+ for (Request *request : requests) {
+ if (camera_->queueRequest(request)) {
+ std::cout << "Failed to queue request" << std::endl;
+ return TestFail;
+ }
}
EventDispatcher *dispatcher = cm_->eventDispatcher();
Timer timer;
- timer.start(5000);
- while (timer.isRunning() && !sink_.done())
+ timer.start(1000);
+ while (timer.isRunning())
dispatcher->processEvents();
- std::cout << framesCaptured_ << " frames captured, "
- << bufferRemappings_.size() << " buffers remapped"
- << std::endl;
+ if (completeRequestsCount_ <= cfg.bufferCount * 2) {
+ std::cout << "Failed to capture enough frames (got "
+ << completeRequestsCount_ << " expected at least "
+ << cfg.bufferCount * 2 << ")" << std::endl;
+ return TestFail;
+ }
- if (framesCaptured_ < 60) {
- std::cout << "Too few frames captured" << std::endl;
+ if (completeRequestsCount_ != completeBuffersCount_) {
+ std::cout << "Number of completed buffers and requests differ" << std::endl;
return TestFail;
}
- if (bufferRemappings_.empty()) {
- std::cout << "No buffer remappings" << std::endl;
+ if (camera_->stop()) {
+ std::cout << "Failed to stop camera" << std::endl;
return TestFail;
}
- if (bufferRemappings_[0] < 40) {
- std::cout << "Early buffer remapping" << std::endl;
+ if (camera_->freeBuffers()) {
+ std::cout << "Failed to free buffers" << std::endl;
return TestFail;
}
return TestPass;
}
- void cleanup()
- {
- sink_.cleanup();
-
- camera_->stop();
- camera_->freeBuffers();
- }
-
private:
- Stream *stream_;
-
- std::map<unsigned int, int> bufferMappings_;
- std::vector<unsigned int> bufferRemappings_;
- unsigned int framesCaptured_;
-
- FrameSink sink_;
+ unsigned int completeBuffersCount_;
+ unsigned int completeRequestsCount_;
+ std::unique_ptr<CameraConfiguration> config_;
};
+} /* namespace */
+
TEST_REGISTER(BufferImportTest);