summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--v4l2-source.c180
-rw-r--r--v4l2-source.h20
3 files changed, 201 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index c69bc94..f3d7208 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ OBJS := \
stream.o \
uvc.o \
v4l2.o \
+ v4l2-source.o \
video-buffers.o \
video-source.o
diff --git a/v4l2-source.c b/v4l2-source.c
new file mode 100644
index 0000000..7eced6a
--- /dev/null
+++ b/v4l2-source.c
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * V4L2 video source
+ *
+ * Copyright (C) 2018 Laurent Pinchart
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "events.h"
+#include "tools.h"
+#include "v4l2.h"
+#include "v4l2-source.h"
+#include "video-buffers.h"
+
+struct v4l2_source {
+ struct video_source src;
+
+ struct v4l2_device *vdev;
+};
+
+#define to_v4l2_source(s) container_of(s, struct v4l2_source, src)
+
+static void v4l2_source_video_process(void *d)
+{
+ struct v4l2_source *src = d;
+ struct video_buffer buf;
+ int ret;
+
+ ret = v4l2_dequeue_buffer(src->vdev, &buf);
+ if (ret < 0)
+ return;
+
+ src->src.handler(src->src.handler_data, &src->src, &buf);
+}
+
+static void v4l2_source_destroy(struct video_source *s)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+
+ v4l2_close(src->vdev);
+ free(src);
+}
+
+static int v4l2_source_set_format(struct video_source *s,
+ struct v4l2_pix_format *fmt)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+
+ return v4l2_set_format(src->vdev, fmt);
+}
+
+static int v4l2_source_alloc_buffers(struct video_source *s, unsigned int nbufs)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+
+ return v4l2_alloc_buffers(src->vdev, V4L2_MEMORY_MMAP, nbufs);
+}
+
+static int v4l2_source_export_buffers(struct video_source *s,
+ struct video_buffer_set **bufs)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+ struct video_buffer_set *buffers;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_export_buffers(src->vdev);
+ if (ret < 0)
+ return ret;
+
+ buffers = video_buffer_set_new(src->vdev->buffers.nbufs);
+ if (!buffers)
+ return -ENOMEM;
+
+ for (i = 0; i < src->vdev->buffers.nbufs; ++i) {
+ struct video_buffer *buffer = &src->vdev->buffers.buffers[i];
+
+ buffers->buffers[i].size = buffer->size;
+ buffers->buffers[i].dmabuf = buffer->dmabuf;
+ }
+
+ *bufs = buffers;
+ return 0;
+}
+
+static int v4l2_source_free_buffers(struct video_source *s)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+
+ return v4l2_free_buffers(src->vdev);
+}
+
+static int v4l2_source_stream_on(struct video_source *s)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+ unsigned int i;
+ int ret;
+
+ /* Queue all buffers. */
+ for (i = 0; i < src->vdev->buffers.nbufs; ++i) {
+ struct video_buffer buf = {
+ .index = i,
+ .size = src->vdev->buffers.buffers[i].size,
+ .dmabuf = src->vdev->buffers.buffers[i].dmabuf,
+ };
+
+ ret = v4l2_queue_buffer(src->vdev, &buf);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = v4l2_stream_on(src->vdev);
+ if (ret < 0)
+ return ret;
+
+ events_watch_fd(src->src.events, src->vdev->fd, EVENT_READ,
+ v4l2_source_video_process, src);
+
+ return 0;
+}
+
+static int v4l2_source_stream_off(struct video_source *s)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+
+ events_unwatch_fd(src->src.events, src->vdev->fd, EVENT_READ);
+
+ return v4l2_stream_off(src->vdev);
+}
+
+static int v4l2_source_queue_buffer(struct video_source *s,
+ struct video_buffer *buf)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+
+ return v4l2_queue_buffer(src->vdev, buf);
+}
+
+static const struct video_source_ops v4l2_source_ops = {
+ .destroy = v4l2_source_destroy,
+ .set_format = v4l2_source_set_format,
+ .alloc_buffers = v4l2_source_alloc_buffers,
+ .export_buffers = v4l2_source_export_buffers,
+ .free_buffers = v4l2_source_free_buffers,
+ .stream_on = v4l2_source_stream_on,
+ .stream_off = v4l2_source_stream_off,
+ .queue_buffer = v4l2_source_queue_buffer,
+};
+
+struct video_source *v4l2_video_source_create(const char *devname)
+{
+ struct v4l2_source *src;
+
+ src = malloc(sizeof *src);
+ if (!src)
+ return NULL;
+
+ memset(src, 0, sizeof *src);
+ src->src.ops = &v4l2_source_ops;
+
+ src->vdev = v4l2_open(devname);
+ if (!src->vdev) {
+ free(src);
+ return NULL;
+ }
+
+ return &src->src;
+}
+
+void v4l2_video_source_init(struct video_source *s, struct events *events)
+{
+ struct v4l2_source *src = to_v4l2_source(s);
+
+ src->src.events = events;
+}
diff --git a/v4l2-source.h b/v4l2-source.h
new file mode 100644
index 0000000..53e28d6
--- /dev/null
+++ b/v4l2-source.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * V4L2 video source
+ *
+ * Copyright (C) 2018 Laurent Pinchart
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+#ifndef __V4L2_VIDEO_SOURCE_H__
+#define __V4L2_VIDEO_SOURCE_H__
+
+#include "video-source.h"
+
+struct events;
+struct video_source;
+
+struct video_source *v4l2_video_source_create(const char *devname);
+void v4l2_video_source_init(struct video_source *src, struct events *events);
+
+#endif /* __VIDEO_SOURCE_H__ */