From d11fa45c11dfce99a737f46d7d6e41f280e01ae0 Mon Sep 17 00:00:00 2001 From: Paul Elder 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 Reviewed-by: Kieran Bingham Signed-off-by: Paul Elder Signed-off-by: Kieran Bingham --- lib/stream.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) 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