From d11fa45c11dfce99a737f46d7d6e41f280e01ae0 Mon Sep 17 00:00:00 2001
From: Paul Elder <paul.elder@ideasonboard.com>
Date: Tue, 22 Nov 2022 10:54:52 +0000
Subject: stream: allow buffers to be allocated on uvc side

We want to allow video sources whose data is generated in userspace.
Currently buffers are allocated on the V4L2-backed video source and
exported to the uvc-gadget's V4L2 device. Since a video source with
data generated in userspace will not be backed by a V4L2 device, we
allow a way for buffers to be allocated on uvc-gadget's V4L2 device.

As a corollary, the way the buffers are passed has to be changed as
well. Previously they were queued and dequeued between the V4L2 video
source and uvc and vice versa. To allow a video source not backed by
a V4L2 device, we allow an alternative flow where they are dequeued and
queued from and to uvc, and giving a chance to the video source to fill
the buffer in between.

Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 lib/stream.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 79 insertions(+), 4 deletions(-)

(limited to 'lib')

diff --git a/lib/stream.c b/lib/stream.c
index 1988e7c..63ccb1b 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -60,13 +60,28 @@ static void uvc_stream_uvc_process(void *d)
 	video_source_queue_buffer(stream->src, &buf);
 }
 
-static int uvc_stream_start(struct uvc_stream *stream)
+static void uvc_stream_uvc_process_no_buf(void *d)
 {
+	struct uvc_stream *stream = d;
 	struct v4l2_device *sink = uvc_v4l2_device(stream->uvc);
-	struct video_buffer_set *buffers = NULL;
+	struct video_buffer buf;
 	int ret;
 
-	printf("Starting video stream.\n");
+	ret = v4l2_dequeue_buffer(sink, &buf);
+	if (ret < 0)
+		return;
+
+	video_source_fill_buffer(stream->src, &buf);
+
+	v4l2_queue_buffer(sink, &buf);
+}
+
+
+static int uvc_stream_start_alloc(struct uvc_stream *stream)
+{
+	struct v4l2_device *sink = uvc_v4l2_device(stream->uvc);
+	struct video_buffer_set *buffers = NULL;
+	int ret;
 
 	/* Allocate and export the buffers on the source. */
 	ret = video_source_alloc_buffers(stream->src, 4);
@@ -116,6 +131,64 @@ error_free_source:
 	return ret;
 }
 
+static int uvc_stream_start_no_alloc(struct uvc_stream *stream)
+{
+	struct v4l2_device *sink = uvc_v4l2_device(stream->uvc);
+	int ret;
+	unsigned int i;
+
+	/* Allocate buffers on the sink. */
+	ret = v4l2_alloc_buffers(sink, V4L2_MEMORY_MMAP, 4);
+	if (ret < 0) {
+		printf("Failed to allocate sink buffers: %s (%d)\n",
+		       strerror(-ret), -ret);
+		return ret;
+	}
+
+	/* mmap buffers. */
+	ret = v4l2_mmap_buffers(sink);
+	if (ret < 0) {
+		printf("Failed to query sink buffers: %s (%d)\n",
+				strerror(-ret), -ret);
+		return ret;
+	}
+
+	/* Queue buffers to sink. */
+	for (i = 0; i < sink->buffers.nbufs; ++i) {
+		struct video_buffer buf = {
+			.index = i,
+			.size = sink->buffers.buffers[i].size,
+			.mem = sink->buffers.buffers[i].mem,
+		};
+
+		video_source_fill_buffer(stream->src, &buf);
+		ret = v4l2_queue_buffer(sink, &buf);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Start the source and sink. */
+	video_source_stream_on(stream->src);
+	ret = v4l2_stream_on(sink);
+	if (ret < 0)
+		return ret;
+
+	events_watch_fd(stream->events, sink->fd, EVENT_WRITE,
+			uvc_stream_uvc_process_no_buf, stream);
+
+	return 0;
+}
+
+static int uvc_stream_start(struct uvc_stream *stream)
+{
+	printf("Starting video stream.\n");
+
+	if (stream->src->ops->alloc_buffers)
+		return uvc_stream_start_alloc(stream);
+	else
+		return uvc_stream_start_no_alloc(stream);
+}
+
 static int uvc_stream_stop(struct uvc_stream *stream)
 {
 	struct v4l2_device *sink = uvc_v4l2_device(stream->uvc);
@@ -216,5 +289,7 @@ void uvc_stream_set_video_source(struct uvc_stream *stream,
 {
 	stream->src = src;
 
-	video_source_set_buffer_handler(src, uvc_stream_source_process, stream);
+	if (stream->src->ops->alloc_buffers)
+		video_source_set_buffer_handler(src, uvc_stream_source_process,
+						stream);
 }
-- 
cgit v1.2.3