diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/camera/buffer_import.cpp | 389 |
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); |