[RFC,v1,35/54] libcamera: pipeline: uvcvideo: Use buffer pool prototype
diff mbox series

Message ID 20260629163017.863145-36-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:29 p.m. UTC
Convert the uvcvideo pipeline handler to use the buffer pool interface
of cameras. Do that by keeping track of the number of queued buffers,
and each time
  * a request is queued,
  * a buffer is added, or
  * a buffer is returned from the kernel
the pipeline handler tries to consume as many requests as possible.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 71 +++++++++++++++-----
 1 file changed, 53 insertions(+), 18 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 928ee79559..e70cbe23c3 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -62,6 +62,8 @@  public:
 	std::optional<v4l2_exposure_auto_type> autoExposureMode_;
 	std::optional<v4l2_exposure_auto_type> manualExposureMode_;
 
+	size_t processedRequests_ = 0;
+
 private:
 	bool generateId();
 
@@ -95,6 +97,8 @@  public:
 	void stopDevice(Camera *camera) override;
 
 	int queueRequestDevice(Camera *camera, Request *request) override;
+	void buffersAddedDevice(Camera *camera) override;
+	void tryRunCamera(UVCCameraData *data);
 
 	bool match(DeviceEnumerator *enumerator) override;
 
@@ -230,7 +234,7 @@  CameraConfiguration::Status UVCCameraConfiguration::validate()
 }
 
 PipelineHandlerUVC::PipelineHandlerUVC(CameraManager *manager)
-	: PipelineHandler(manager, {})
+	: PipelineHandler(manager, { .usesBufferPool = true })
 {
 }
 
@@ -323,6 +327,9 @@  void PipelineHandlerUVC::stopDevice(Camera *camera)
 	UVCCameraData *data = cameraData(camera);
 	data->video_->streamOff();
 	data->video_->releaseBuffers();
+
+	while (!data->queuedRequests_.empty())
+		cancelRequest(data->queuedRequests_.front());
 }
 
 int PipelineHandlerUVC::processControl(const UVCCameraData *data, ControlList *controls,
@@ -446,26 +453,49 @@  int PipelineHandlerUVC::processControls(UVCCameraData *data, const ControlList &
 	return ret;
 }
 
-int PipelineHandlerUVC::queueRequestDevice(Camera *camera, Request *request)
+int PipelineHandlerUVC::queueRequestDevice(Camera *camera, [[maybe_unused]] Request *request)
 {
-	UVCCameraData *data = cameraData(camera);
-	FrameBuffer *buffer = request->findBuffer(&data->stream_);
-	if (!buffer) {
-		LOG(UVC, Error)
-			<< "Attempt to queue request with invalid stream";
+	/* `request` is already in `Camera::Private::queuedRequests_` */
+	tryRunCamera(cameraData(camera));
+	return 0;
+}
 
-		return -ENOENT;
-	}
+void PipelineHandlerUVC::buffersAddedDevice(Camera *camera)
+{
+	tryRunCamera(cameraData(camera));
+}
 
-	int ret = processControls(data, request->controls());
-	if (ret < 0)
-		return ret;
+void PipelineHandlerUVC::tryRunCamera(UVCCameraData *data)
+{
+	ASSERT(data->processedRequests_ <= data->queuedRequests_.size());
 
-	ret = data->video_->queueBuffer(buffer);
-	if (ret < 0)
-		return ret;
+	auto it = std::next(data->queuedRequests_.begin(), data->processedRequests_);
+	for (; it != data->queuedRequests_.end(); ++it, data->processedRequests_++) {
+		Request *request = *it;
+		ASSERT(request->status() == Request::RequestPending);
 
-	return 0;
+		int ret = processControls(data, request->controls());
+		if (ret < 0) {
+			cancelRequest(request);
+			continue;
+		}
+
+again:;
+		auto buffer = data->acquireBuffer(&data->stream_);
+		if (!buffer)
+			break;
+
+		ret = data->video_->queueBuffer(buffer.get());
+		if (ret < 0) {
+			// \todo do not do this if the error is "too many buffers"
+			// but that probably shouldn't ever be the case because
+			// VIDEO_MAX_FRAME == 32 == maxQueuedRequestsDevice
+			data->rejectBuffer(std::move(buffer));
+			goto again;
+		}
+
+		buffer.release();
+	}
 }
 
 bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)
@@ -893,14 +923,19 @@  void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
 
 void UVCCameraData::imageBufferReady(FrameBuffer *buffer)
 {
-	Request *request = buffer->_d()->request();
+	ASSERT(!queuedRequests_.empty());
+	ASSERT(processedRequests_ > 0);
+
+	Request *request = queuedRequests_.front();
 
 	/* \todo Use the UVC metadata to calculate a more precise timestamp */
 	request->_d()->metadata().set(controls::SensorTimestamp,
 				      buffer->metadata().timestamp);
 
 	pipe()->completeBuffer(request, buffer);
-	pipe()->completeRequest(request);
+	processedRequests_ -= pipe()->completeRequest(request);
+
+	static_cast<PipelineHandlerUVC *>(pipe())->tryRunCamera(this);
 }
 
 REGISTER_PIPELINE_HANDLER(PipelineHandlerUVC, "uvcvideo")