summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Elder <paul.elder@ideasonboard.com>2018-05-23 23:07:33 +0300
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2018-05-24 01:27:34 +0300
commit00b892305694ea796728c1f97831bc3c59a8e3da (patch)
treedad8e5da8b6b3ad786b1ce109c5eb0c03c4d6f93
parent53b139a22810e00aa0ce872853b45107c4edee98 (diff)
uvc-gadget: Implement streaming from a capture device
Capture frames from v4l2 capture device and send frames to uvc gadget output device. This is done by passing buffers between these two devices; the buffers are shared through V4L2_MEMORY_DMABUF. Empty buffers dequeued from the uvc device are enqueued into the v4l2 capture device, from which full buffers are dequeued and enqueued back into the uvc device. Implement and use functions to ease manipulation of the capture device, and remove uvc_fill_buffer, as it is no longer needed to fill frames from a non-v4l2 capture source. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-rw-r--r--uvc-gadget.c192
1 files changed, 103 insertions, 89 deletions
diff --git a/uvc-gadget.c b/uvc-gadget.c
index 222e16c..62377d8 100644
--- a/uvc-gadget.c
+++ b/uvc-gadget.c
@@ -62,12 +62,9 @@ struct uvc_device
struct uvc_stream
{
+ struct v4l2_device *cap;
struct uvc_device *uvc;
- uint8_t color;
- unsigned int imgsize;
- void *imgdata;
-
struct events *events;
};
@@ -104,28 +101,17 @@ uvc_close(struct uvc_device *dev)
* Video streaming
*/
-static void
-uvc_video_fill_buffer(struct uvc_stream *stream, struct v4l2_video_buffer *buf)
+static void capture_video_process(void *d)
{
- struct uvc_device *dev = stream->uvc;
- unsigned int bpl;
- unsigned int i;
-
- switch (dev->fcc) {
- case V4L2_PIX_FMT_YUYV:
- /* Fill the buffer with video data. */
- bpl = dev->width * 2;
- for (i = 0; i < dev->height; ++i)
- memset(buf->mem + i*bpl, stream->color++, bpl);
+ struct uvc_stream *stream = d;
+ struct v4l2_video_buffer buf;
+ int ret;
- buf->bytesused = bpl * dev->height;
- break;
+ ret = v4l2_dequeue_buffer(stream->cap, &buf);
+ if (ret < 0)
+ return;
- case V4L2_PIX_FMT_MJPEG:
- memcpy(buf->mem, stream->imgdata, stream->imgsize);
- buf->bytesused = stream->imgsize;
- break;
- }
+ v4l2_queue_buffer(stream->uvc->vdev, &buf);
}
static void
@@ -139,16 +125,14 @@ uvc_video_process(void *d)
if (ret < 0)
return;
- uvc_video_fill_buffer(stream, &buf);
-
- v4l2_queue_buffer(stream->uvc->vdev, &buf);
+ v4l2_queue_buffer(stream->cap, &buf);
}
static int
uvc_video_stream(struct uvc_stream *stream, int enable)
{
+ struct v4l2_device *cap = stream->cap;
struct uvc_device *dev = stream->uvc;
- unsigned int i;
int ret;
if (!enable) {
@@ -161,43 +145,83 @@ uvc_video_stream(struct uvc_stream *stream, int enable)
printf("Starting video stream.\n");
- ret = v4l2_alloc_buffers(dev->vdev, V4L2_MEMORY_MMAP, 4);
+ ret = v4l2_alloc_buffers(dev->vdev, V4L2_MEMORY_DMABUF, 4);
if (ret < 0) {
- printf("Failed to allocate buffers.\n");
+ printf("Failed to allocate buffers: %s (%d)\n", strerror(-ret), -ret);
return ret;
}
- ret = v4l2_mmap_buffers(dev->vdev);
+ ret = v4l2_import_buffers(dev->vdev, dev->vdev->nbufs, cap->buffers);
if (ret < 0) {
- printf("Failed to mmap buffers.\n");
+ printf("Failed to import buffers: %s (%d)\n", strerror(-ret), -ret);
goto error;
}
- for (i = 0; i < dev->vdev->nbufs; ++i) {
- struct v4l2_video_buffer *buf = &dev->vdev->buffers[i];
+ v4l2_stream_on(dev->vdev);
+ events_watch_fd(stream->events, dev->vdev->fd, EVENT_WRITE,
+ uvc_video_process, stream);
+
+ return 0;
+
+error:
+ v4l2_free_buffers(dev->vdev);
+ return ret;
+}
+
+static int capture_video_stream(struct uvc_stream *stream, int enable)
+{
+ struct v4l2_device *cap = stream->cap;
+ unsigned int i;
+ int ret;
+
+ if (!enable) {
+ printf("Stopping video capture stream.\n");
+ events_unwatch_fd(stream->events, cap->fd, EVENT_READ);
+ v4l2_stream_off(cap);
+ v4l2_free_buffers(cap);
+ return 0;
+ }
+
+ printf("Starting video capture stream.\n");
- uvc_video_fill_buffer(stream, buf);
+ ret = v4l2_alloc_buffers(cap, V4L2_MEMORY_MMAP, 4);
+ if (ret < 0) {
+ printf("Failed to allocate capture buffers.\n");
+ return ret;
+ }
- ret = v4l2_queue_buffer(dev->vdev, buf);
+ ret = v4l2_export_buffers(cap);
+ if (ret < 0) {
+ printf("Failed to export buffers.\n");
+ goto error;
+ }
+
+ for (i = 0; i < cap->nbufs; ++i) {
+ struct v4l2_video_buffer *buf = &cap->buffers[i];
+
+ ret = v4l2_queue_buffer(cap, buf);
if (ret < 0)
goto error;
}
- v4l2_stream_on(dev->vdev);
- events_watch_fd(stream->events, dev->vdev->fd, EVENT_WRITE,
- uvc_video_process, stream);
+ v4l2_stream_on(cap);
+ events_watch_fd(stream->events, cap->fd, EVENT_READ,
+ capture_video_process, stream);
return 0;
error:
- v4l2_free_buffers(dev->vdev);
+ v4l2_free_buffers(cap);
return ret;
}
static int
-uvc_video_set_format(struct uvc_device *dev)
+uvc_video_set_format(struct uvc_stream *stream)
{
+ struct v4l2_device *cap = stream->cap;
+ struct uvc_device *dev = stream->uvc;
struct v4l2_pix_format fmt;
+ int ret;
printf("Setting format to 0x%08x %ux%u\n",
dev->fcc, dev->width, dev->height);
@@ -210,7 +234,11 @@ uvc_video_set_format(struct uvc_device *dev)
if (dev->fcc == V4L2_PIX_FMT_MJPEG)
fmt.sizeimage = dev->maxsize * 1.5;
- return v4l2_set_format(dev->vdev, &fmt);
+ ret = v4l2_set_format(dev->vdev, &fmt);
+ if (ret < 0)
+ return ret;
+
+ return v4l2_set_format(cap, &fmt);
}
static int
@@ -424,9 +452,10 @@ uvc_events_process_setup(struct uvc_device *dev,
}
static void
-uvc_events_process_data(struct uvc_device *dev,
+uvc_events_process_data(struct uvc_stream *stream,
const struct uvc_request_data *data)
{
+ struct uvc_device *dev = stream->uvc;
const struct uvc_streaming_control *ctrl =
(const struct uvc_streaming_control *)&data->data;
struct uvc_streaming_control *target;
@@ -461,7 +490,7 @@ uvc_events_process_data(struct uvc_device *dev,
dev->width = frame->width;
dev->height = frame->height;
- uvc_video_set_format(dev);
+ uvc_video_set_format(stream);
}
}
@@ -495,15 +524,17 @@ uvc_events_process(void *d)
break;
case UVC_EVENT_DATA:
- uvc_events_process_data(dev, &uvc_event->data);
+ uvc_events_process_data(stream, &uvc_event->data);
return;
case UVC_EVENT_STREAMON:
+ capture_video_stream(stream, 1);
uvc_video_stream(stream, 1);
return;
case UVC_EVENT_STREAMOFF:
uvc_video_stream(stream, 0);
+ capture_video_stream(stream, 0);
return;
}
@@ -539,7 +570,8 @@ uvc_events_init(struct uvc_device *dev)
* Stream handling
*/
-static struct uvc_stream *uvc_stream_new(const char *device)
+static struct uvc_stream *uvc_stream_new(const char *uvc_device,
+ const char *cap_device)
{
struct uvc_stream *stream;
@@ -549,13 +581,22 @@ static struct uvc_stream *uvc_stream_new(const char *device)
memset(stream, 0, sizeof(*stream));
- stream->uvc = uvc_open(device);
- if (stream->uvc == NULL) {
- free(stream);
- return NULL;
- }
+ stream->cap = v4l2_open(cap_device);
+ if (stream->cap == NULL)
+ goto error;
+
+ stream->uvc = uvc_open(uvc_device);
+ if (stream->uvc == NULL)
+ goto error;
return stream;
+
+error:
+ if (stream->cap)
+ v4l2_close(stream->cap);
+
+ free(stream);
+ return NULL;
}
static void uvc_stream_delete(struct uvc_stream *stream)
@@ -563,45 +604,18 @@ static void uvc_stream_delete(struct uvc_stream *stream)
if (stream == NULL)
return;
+ v4l2_close(stream->cap);
uvc_close(stream->uvc);
- free(stream->imgdata);
free(stream);
}
-static void uvc_stream_load_image(struct uvc_stream *stream, const char *img)
-{
- int fd = -1;
-
- if (img == NULL)
- return;
-
- fd = open(img, O_RDONLY);
- if (fd == -1) {
- printf("Unable to open MJPEG image '%s'\n", img);
- return;
- }
-
- stream->imgsize = lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
- stream->imgdata = malloc(stream->imgsize);
- if (stream->imgdata == NULL) {
- printf("Unable to allocate memory for MJPEG image\n");
- stream->imgsize = 0;
- return;
- }
-
- read(fd, stream->imgdata, stream->imgsize);
- close(fd);
-}
-
static void uvc_stream_init_uvc(struct uvc_stream *stream)
{
/*
* FIXME: The maximum size should be specified per format and frame.
- * For now just hardcode it to support MJPEG.
*/
- stream->uvc->maxsize = stream->imgsize;
+ stream->uvc->maxsize = 0;
uvc_events_init(stream->uvc);
uvc_video_init(stream->uvc);
@@ -624,6 +638,7 @@ static void usage(const char *argv0)
{
fprintf(stderr, "Usage: %s [options]\n", argv0);
fprintf(stderr, "Available options are\n");
+ fprintf(stderr, " -c device V4L2 source device\n");
fprintf(stderr, " -d device Video device\n");
fprintf(stderr, " -h Print this help screen and exit\n");
fprintf(stderr, " -i image MJPEG image\n");
@@ -640,27 +655,27 @@ static void sigint_handler(int signal __attribute__((__unused__)))
int main(int argc, char *argv[])
{
- char *device = "/dev/video0";
+ char *uvc_device = "/dev/video0";
+ char *cap_device = "/dev/video1";
struct uvc_stream *stream;
struct events events;
- char *mjpeg_image = NULL;
int ret = 0;
int opt;
- while ((opt = getopt(argc, argv, "d:hi:")) != -1) {
+ while ((opt = getopt(argc, argv, "c:d:h")) != -1) {
switch (opt) {
+ case 'c':
+ cap_device = optarg;
+ break;
+
case 'd':
- device = optarg;
+ uvc_device = optarg;
break;
case 'h':
usage(argv[0]);
return 0;
- case 'i':
- mjpeg_image = optarg;
- break;
-
default:
fprintf(stderr, "Invalid option '-%c'\n", opt);
usage(argv[0]);
@@ -679,13 +694,12 @@ int main(int argc, char *argv[])
signal(SIGINT, sigint_handler);
/* Create and initialise the stream. */
- stream = uvc_stream_new(device);
+ stream = uvc_stream_new(uvc_device, cap_device);
if (stream == NULL) {
ret = 1;
goto done;
}
- uvc_stream_load_image(stream, mjpeg_image);
uvc_stream_init_uvc(stream);
uvc_stream_set_event_handler(stream, &events);