[RFC,v1,45/54] v4l2: Use camera buffer pool
diff mbox series

Message ID 20260629163017.863145-46-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • libcamera: Split requests and buffers
Related show

Commit Message

Barnabás Pőcze June 29, 2026, 4:30 p.m. UTC
Port to using the camera's buffer pool.

TODO: this is probably suboptimal in multiple respects

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 src/v4l2/v4l2_camera.cpp | 84 ++++++++++++++++++++++++++++------------
 src/v4l2/v4l2_camera.h   |  4 +-
 2 files changed, 63 insertions(+), 25 deletions(-)

Patch
diff mbox series

diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
index aae9fae68c..3bcf841cd0 100644
--- a/src/v4l2/v4l2_camera.cpp
+++ b/src/v4l2/v4l2_camera.cpp
@@ -70,24 +70,30 @@  void V4L2Camera::unbind()
 
 void V4L2Camera::requestComplete(Request *request)
 {
-	if (request->status() == Request::RequestCancelled)
-		return;
+	bool hasBuffer = false;
 
 	/* We only have one stream at the moment. */
 	{
 		MutexLocker locker(bufferMutex_);
-		FrameBuffer *buffer = request->buffers().begin()->second;
-		completedBuffers_.emplace_back(buffer->cookie(), buffer->metadata());
+		FrameBuffer *buffer = request->findBuffer(config_->at(0).stream());
 
-		uint64_t data = 1;
-		int ret = ::write(efd_, &data, sizeof(data));
-		if (ret != sizeof(data))
-			LOG(V4L2Compat, Error) << "Failed to signal eventfd POLLIN";
+		if (buffer) {
+			completedBuffers_.emplace_back(buffer->cookie(), buffer->metadata());
+
+			uint64_t data = 1;
+			int ret = ::write(efd_, &data, sizeof(data));
+			if (ret != sizeof(data))
+				LOG(V4L2Compat, Error) << "Failed to signal eventfd POLLIN";
+
+			hasBuffer = true;
+		}
 
 		request->reuse();
+		freeRequests_.push_back(request);
 	}
 
-	bufferCV_.notify_all();
+	if (hasBuffer)
+		bufferCV_.notify_all();
 }
 
 int V4L2Camera::configure(StreamConfiguration *streamConfigOut,
@@ -150,6 +156,7 @@  int V4L2Camera::allocBuffers()
 		return ret;
 
 	const auto &buffers = bufferAllocator_->buffers(stream);
+	MutexLocker locker(bufferMutex_);
 
 	for (size_t i = 0; i < buffers.size(); i++) {
 		std::unique_ptr<Request> request = camera_->createRequest(i);
@@ -157,6 +164,7 @@  int V4L2Camera::allocBuffers()
 			requestPool_.clear();
 			return -ENOMEM;
 		}
+		freeRequests_.push_back(request.get());
 		requestPool_.push_back(std::move(request));
 
 		buffers[i]->setCookie(i);
@@ -167,9 +175,15 @@  int V4L2Camera::allocBuffers()
 
 void V4L2Camera::freeBuffers()
 {
-	pendingRequests_.clear();
+	{
+		MutexLocker locker(bufferMutex_);
+		freeRequests_.clear();
+	}
+
 	requestPool_.clear();
 
+	pendingBuffers_.clear();
+
 	Stream *stream = config_->at(0).stream();
 	bufferAllocator_->free(stream);
 }
@@ -199,21 +213,34 @@  int V4L2Camera::streamOn()
 
 	isRunning_ = true;
 
-	for (Request *req : pendingRequests_) {
+	Stream *stream = config_->at(0).stream();
+
+	MutexLocker locker(bufferMutex_);
+
+	for (FrameBuffer *buffer : pendingBuffers_) {
+		ASSERT(!pendingBuffers_.empty());
+		Request *req = freeRequests_.back();
+		freeRequests_.pop_back();
+
+		req->enableStream(stream, true);
+
 		/* \todo What should we do if this returns -EINVAL? */
 		ret = camera_->queueRequest(req);
 		if (ret < 0)
 			return ret == -EACCES ? -EBUSY : ret;
+
+		/* \todo error handling how? */
+		std::ignore = camera_->addBuffer(stream, buffer);
 	}
 
-	pendingRequests_.clear();
+	pendingBuffers_.clear();
 
 	return 0;
 }
 
 int V4L2Camera::streamOff()
 {
-	pendingRequests_.clear();
+	pendingBuffers_.clear();
 
 	if (!isRunning_) {
 		for (std::unique_ptr<Request> &req : requestPool_)
@@ -238,33 +265,42 @@  int V4L2Camera::streamOff()
 
 int V4L2Camera::qbuf(unsigned int index)
 {
-	if (index >= requestPool_.size()) {
+	Stream *stream = config_->at(0).stream();
+	const auto &buffers = bufferAllocator_->buffers(stream);
+
+	if (index >= buffers.size()) {
 		LOG(V4L2Compat, Error) << "Invalid index";
 		return -EINVAL;
 	}
-	Request *request = requestPool_[index].get();
 
-	Stream *stream = config_->at(0).stream();
-	FrameBuffer *buffer = bufferAllocator_->buffers(stream)[index].get();
-	int ret = request->addBuffer(stream, buffer);
-	if (ret < 0) {
-		LOG(V4L2Compat, Error) << "Can't set buffer for request";
-		return -ENOMEM;
-	}
+	FrameBuffer *buffer = buffers[index].get();
 
 	if (!isRunning_) {
-		pendingRequests_.push_back(request);
+		pendingBuffers_.push_back(buffer);
 		return 0;
 	}
 
+	MutexLocker locker(bufferMutex_);
+
+	if (freeRequests_.empty())
+		return -EBUSY;
+
+	Request *request = freeRequests_.back();
+
 	request->controls().merge(std::move(controls_));
+	request->enableStream(stream, true);
 
-	ret = camera_->queueRequest(request);
+	int ret = camera_->queueRequest(request);
 	if (ret < 0) {
 		LOG(V4L2Compat, Error) << "Can't queue request";
 		return ret == -EACCES ? -EBUSY : ret;
 	}
 
+	freeRequests_.pop_back();
+
+	/* \todo error handling how? */
+	std::ignore = camera_->addBuffer(stream, buffer);
+
 	return 0;
 }
 
diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
index 06e37077b2..f51313ef36 100644
--- a/src/v4l2/v4l2_camera.h
+++ b/src/v4l2/v4l2_camera.h
@@ -79,8 +79,10 @@  private:
 	std::unique_ptr<libcamera::FrameBufferAllocator> bufferAllocator_;
 
 	std::vector<std::unique_ptr<libcamera::Request>> requestPool_;
+	std::vector<libcamera::Request *> freeRequests_
+		LIBCAMERA_TSA_GUARDED_BY(bufferMutex_);
 
-	std::deque<libcamera::Request *> pendingRequests_;
+	std::deque<libcamera::FrameBuffer *> pendingBuffers_;
 	std::deque<CompletedBuffer> completedBuffers_
 		LIBCAMERA_TSA_GUARDED_BY(bufferMutex_);