diff mbox

[1/2] libv4l2: add implicit conversion from single- to multi-plane api

Message ID 1309944253-11703-1-git-send-email-t.stanislaws@samsung.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Tomasz Stanislawski July 6, 2011, 9:24 a.m. UTC
This patch add implicit conversion of single plane variant of ioctl to
multiplane variant. The conversion is performed only in case if a driver
implements only mplane api. The conversion is done by substituting SYS_IOCTL
with a wrapper that converts single plane call to their mplane analogs.
Function v4l2_fd_open was revised to work correctly with the wrapper.

Signed-off-by: Tomasz Stanislawski <t.stanislaws@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 lib/libv4l2/libv4l2.c                  |  227 ++++++++++++++++++++++++++++----
 lib/libv4lconvert/libv4lsyscall-priv.h |    5 +-
 2 files changed, 205 insertions(+), 27 deletions(-)

Comments

Hans de Goede July 17, 2011, 9:11 p.m. UTC | #1
Hi,

On 07/06/2011 11:24 AM, Tomasz Stanislawski wrote:
> This patch add implicit conversion of single plane variant of ioctl to
> multiplane variant. The conversion is performed only in case if a driver
> implements only mplane api. The conversion is done by substituting SYS_IOCTL
> with a wrapper that converts single plane call to their mplane analogs.
> Function v4l2_fd_open was revised to work correctly with the wrapper.
>
> Signed-off-by: Tomasz Stanislawski<t.stanislaws@samsung.com>
> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>

Thanks for the patch, I like the general idea, but I'm not completely
happy with the implementation.

I think overloading SYS_ioctl is not such a great idea, since this won't
work for calls made by libv4lconvert, unless we export it from libv4l2
and use it in libv4lconvert too, which is quite ugly from an ABI pov.

This is also problematic in the light of the upcoming plugin support
(which just landed in v4l-utils git). Notice how that has replaced
SYS_ioctl with dev_ops->ioctl, so that plugins can intercept ioctls.

Actually the plugni support should make doing this more easy wrt
libv4lconvert, since libv4lconvert now uses dev_ops->ioctl too.

I think this can and should be handled in the following way, with a
2 patch patch-set:

Patch1: Make the dev_ops member of v4l2_dev_info a struct rather
then a pointer to a struct (and adjust v4l_plugin_init accordingly).

Patch2: If one of the devices in question is detected the original
dev_ops->ioctl should be saved in v4l2_dev_info and be replaced with
the proposed wrapper, which then calls the saved original in cases
where it now calls SYS_ioctl.

Regards,

Hans




--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
index 4de382e..6af9f8c 100644
--- a/lib/libv4l2/libv4l2.c
+++ b/lib/libv4l2/libv4l2.c
@@ -57,6 +57,7 @@ 
  */
 #include <errno.h>
 #include <stdarg.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <fcntl.h>
@@ -79,6 +80,7 @@ 
 #define V4L2_STREAM_TOUCHED		0x2000
 #define V4L2_USE_READ_FOR_READ		0x4000
 #define V4L2_SUPPORTS_TIMEPERFRAME	0x8000
+#define V4L2_CONVERT2MPLANE		0x00010000
 
 #define V4L2_MMAP_OFFSET_MAGIC      0xABCDEF00u
 
@@ -93,6 +95,165 @@  static struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = {
 };
 static int devices_used;
 
+static int v4l2_get_index(int fd);
+
+#define SYS_DO_IOCTL(fd, cmd, arg) \
+	syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
+
+int v4l2_ioctl_mp(int fd, unsigned long cmd, void *arg)
+{
+	int ret;
+	int index = v4l2_get_index(fd);
+
+	/* skipping if no mplane convertion is needed */
+	if (index == -1 || !(devices[index].flags & V4L2_CONVERT2MPLANE))
+		return SYS_DO_IOCTL(fd, cmd, arg);
+
+	switch (cmd) {
+	case VIDIOC_QUERYCAP: {
+		struct v4l2_capability *cap = arg;
+		ret = SYS_DO_IOCTL(fd, cmd, cap);
+		if (ret)
+			return ret;
+		if (cap->capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
+			cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
+		return 0;
+	}
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_S_FMT: {
+		/* convert single planar structs to mplane structs */
+		struct v4l2_format fmt = {0};
+		struct v4l2_format *old = arg;
+
+		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		fmt.fmt.pix_mp.width = old->fmt.pix.width;
+		fmt.fmt.pix_mp.height = old->fmt.pix.height;
+		fmt.fmt.pix_mp.pixelformat = old->fmt.pix.pixelformat;
+		fmt.fmt.pix_mp.field = old->fmt.pix.field;
+		fmt.fmt.pix_mp.colorspace = old->fmt.pix.colorspace;
+		fmt.fmt.pix_mp.num_planes = 1;
+		fmt.fmt.pix_mp.plane_fmt[0].bytesperline = old->fmt.pix.bytesperline;
+		fmt.fmt.pix_mp.plane_fmt[0].sizeimage = old->fmt.pix.sizeimage;
+
+		ret = SYS_DO_IOCTL(fd, cmd, &fmt);
+		if (ret)
+			return ret;
+
+		old->fmt.pix.width = fmt.fmt.pix_mp.width;
+		old->fmt.pix.height = fmt.fmt.pix_mp.height;
+		old->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
+		old->fmt.pix.field = fmt.fmt.pix_mp.field;
+		old->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
+		old->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+		old->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+
+		return 0;
+	}
+	case VIDIOC_G_FMT: {
+		struct v4l2_format fmt = { 0 };
+		struct v4l2_format *old = arg;
+
+		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		ret = SYS_DO_IOCTL(fd, cmd, &fmt);
+		if (ret)
+			return ret;
+		/* cannot return multiplane format to singleplane app */
+		if (fmt.fmt.pix_mp.num_planes > 1) {
+			errno = -EBUSY;
+			return -1;
+		}
+		old->fmt.pix.width = fmt.fmt.pix_mp.width;
+		old->fmt.pix.height = fmt.fmt.pix_mp.height;
+		old->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
+		old->fmt.pix.field = fmt.fmt.pix_mp.field;
+		old->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
+		old->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+		old->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+
+		return 0;
+	}
+	case VIDIOC_ENUM_FMT: {
+		struct v4l2_fmtdesc *fmtdesc = arg;
+
+		fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		ret = SYS_DO_IOCTL(fd, cmd, fmtdesc);
+		fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+		return ret;
+	}
+	case VIDIOC_S_PARM:
+	case VIDIOC_G_PARM: {
+		struct v4l2_streamparm *parm = arg;
+
+		parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		ret = SYS_DO_IOCTL(fd, cmd, parm);
+		parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		return ret;
+	}
+	case VIDIOC_STREAMON:
+	case VIDIOC_STREAMOFF: {
+		int type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+		return SYS_DO_IOCTL(fd, cmd, &type);
+	}
+	case VIDIOC_CROPCAP: {
+		struct v4l2_cropcap *cropcap = arg;
+
+		cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		ret = SYS_DO_IOCTL(fd, cmd, cropcap);
+		cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		return ret;
+	}
+	case VIDIOC_S_CROP:
+	case VIDIOC_G_CROP: {
+		struct v4l2_crop *crop = arg;
+
+		crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		ret = SYS_DO_IOCTL(fd, cmd, crop);
+		crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		return ret;
+	}
+	case VIDIOC_REQBUFS: {
+		struct v4l2_requestbuffers *rq = arg;
+
+		rq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		ret = SYS_DO_IOCTL(fd, cmd, rq);
+		rq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		return ret;
+	}
+	case VIDIOC_QBUF:
+	case VIDIOC_DQBUF:
+	case VIDIOC_QUERYBUF: {
+		struct v4l2_buffer buf;
+		struct v4l2_buffer *old = arg;
+		struct v4l2_plane plane = { 0 };
+
+		memcpy(&buf, old, sizeof buf);
+		memcpy(&plane.m, &old->m, sizeof old->m);
+		plane.length = old->length;
+		plane.bytesused = old->bytesused;
+
+		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		buf.m.planes = &plane;
+		buf.length = 1;
+
+		ret = SYS_DO_IOCTL(fd, cmd, &buf);
+		if (ret)
+			return ret;
+
+		memcpy(old, &buf, sizeof buf);
+		old->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		memcpy(&old->m, &plane.m, sizeof plane.m);
+		old->length = plane.length;
+		old->bytesused = plane.bytesused;
+		return 0;
+	}
+
+	} /* switch brace */
+
+	/* handling not mplane ioctl */
+	return SYS_DO_IOCTL(fd, cmd, arg);
+}
 
 static int v4l2_request_read_buffers(int index)
 {
@@ -586,7 +747,7 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	}
 
 	/* check that this is an v4l2 device */
-	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) {
+	if (SYS_DO_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) {
 		int saved_err = errno;
 
 		V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
@@ -595,35 +756,21 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	}
 
 	/* we only add functionality for video capture devices */
-	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ||
-			!(cap.capabilities & (V4L2_CAP_STREAMING | V4L2_CAP_READWRITE)))
+	if (!(cap.capabilities &
+		(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)))
 		return fd;
 
-	/* Get current cam format */
-	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	if (SYS_IOCTL(fd, VIDIOC_G_FMT, &fmt)) {
-		int saved_err = errno;
-
-		V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
-		errno = saved_err;
-		return -1;
-	}
-
-	/* Check for framerate setting support */
-	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	if (SYS_IOCTL(fd, VIDIOC_G_PARM, &parm))
-		parm.type = 0;
-
-	/* init libv4lconvert */
-	convert = v4lconvert_create(fd);
-	if (!convert)
-		return -1;
+	/* we only add functionality for video streaming and read/write devices */
+	if (!(cap.capabilities & (V4L2_CAP_STREAMING | V4L2_CAP_READWRITE)))
+		return fd;
 
 	/* So we have a v4l2 capture device, register it in our devices array */
 	pthread_mutex_lock(&v4l2_open_mutex);
 	for (index = 0; index < V4L2_MAX_DEVICES; index++)
 		if (devices[index].fd == -1) {
 			devices[index].fd = fd;
+			if (index >= devices_used)
+				devices_used = index + 1;
 			break;
 		}
 	pthread_mutex_unlock(&v4l2_open_mutex);
@@ -636,6 +783,31 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	}
 
 	devices[index].flags = v4l2_flags;
+	/* enabling conversion for driver that supports only multiplanar api */
+	if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) &&
+		!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+		devices[index].flags |= V4L2_CONVERT2MPLANE;
+
+	/* Get current cam format */
+	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	if (SYS_IOCTL(fd, VIDIOC_G_FMT, &fmt)) {
+		int saved_err = errno;
+
+		V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
+		errno = saved_err;
+		goto fail;
+	}
+
+	/* Check for framerate setting support */
+	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	if (SYS_IOCTL(fd, VIDIOC_G_PARM, &parm))
+		parm.type = 0;
+
+	/* init libv4lconvert */
+	convert = v4lconvert_create(fd);
+	if (!convert)
+		goto fail;
+
 	if (cap.capabilities & V4L2_CAP_READWRITE)
 		devices[index].flags |= V4L2_SUPPORTS_READ;
 	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
@@ -677,9 +849,6 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	devices[index].readbuf = NULL;
 	devices[index].readbuf_size = 0;
 
-	if (index >= devices_used)
-		devices_used = index + 1;
-
 	/* Note we always tell v4lconvert to optimize src fmt selection for
 	   our default fps, the only exception is the app explictly selecting
 	   a framerate using the S_PARM ioctl after a S_FMT */
@@ -689,6 +858,14 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	V4L2_LOG("open: %d\n", fd);
 
 	return fd;
+
+fail:
+	/* unregister device */
+	pthread_mutex_lock(&v4l2_open_mutex);
+	devices[index].fd = -1;
+	pthread_mutex_unlock(&v4l2_open_mutex);
+
+	return -1;
 }
 
 /* Is this an fd for which we are emulating v4l1 ? */
diff --git a/lib/libv4lconvert/libv4lsyscall-priv.h b/lib/libv4lconvert/libv4lsyscall-priv.h
index 87028ef..9aaf20e 100644
--- a/lib/libv4lconvert/libv4lsyscall-priv.h
+++ b/lib/libv4lconvert/libv4lsyscall-priv.h
@@ -72,12 +72,13 @@  typedef off_t __off_t;
 
 #ifndef CONFIG_SYS_WRAPPER
 
+int v4l2_ioctl_mp(int fd, unsigned long cmd, void *arg) __attribute__ ((visibility("default")));
+
 #define SYS_OPEN(file, oflag, mode) \
 	syscall(SYS_open, (const char *)(file), (int)(oflag), (mode_t)(mode))
 #define SYS_CLOSE(fd) \
 	syscall(SYS_close, (int)(fd))
-#define SYS_IOCTL(fd, cmd, arg) \
-	syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
+#define SYS_IOCTL(fd, cmd, arg) v4l2_ioctl_mp(fd, cmd, arg)
 #define SYS_READ(fd, buf, len) \
 	syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len));
 #define SYS_WRITE(fd, buf, len) \