diff mbox

[v4,1/1] libv4l: Add plugin support to libv4l

Message ID 1678f1f41284ad9665de8717b7b8be117ddf9596.1304435825.git.ykamenov@mm-sol.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Yordan Kamenov May 3, 2011, 3:26 p.m. UTC
A libv4l2 plugin will sit in between libv4l2 itself and the actual
/dev/video device node a fd refers to. It will be called each time
libv4l2 wants to do an operation (read/write/ioctl) on the actual
/dev/video node in question.

Signed-off-by: Yordan Kamenov <ykamenov@mm-sol.com>
---
 lib/include/libv4l2-plugin.h                   |   36 ++++++
 lib/include/libv4lconvert.h                    |    5 +-
 lib/libv4l2/Makefile                           |    6 +-
 lib/libv4l2/libv4l2-priv.h                     |   10 ++
 lib/libv4l2/libv4l2.c                          |   88 ++++++++++----
 lib/libv4l2/v4l2-plugin.c                      |  158 ++++++++++++++++++++++++
 lib/libv4l2/v4l2convert.c                      |    9 --
 lib/libv4lconvert/control/libv4lcontrol-priv.h |    4 +
 lib/libv4lconvert/control/libv4lcontrol.c      |   35 ++++--
 lib/libv4lconvert/control/libv4lcontrol.h      |    5 +-
 lib/libv4lconvert/libv4lconvert-priv.h         |    2 +
 lib/libv4lconvert/libv4lconvert.c              |   34 ++++--
 utils/qv4l2/qv4l2.cpp                          |   17 +++-
 utils/qv4l2/qv4l2.h                            |    1 +
 14 files changed, 347 insertions(+), 63 deletions(-)
 create mode 100644 lib/include/libv4l2-plugin.h
 create mode 100644 lib/libv4l2/v4l2-plugin.c

Comments

Hans de Goede May 5, 2011, 2:04 p.m. UTC | #1
Hi,

Thanks, looks good now!

I'm going to keep this in my inbox before applying for a while though,
as this involves an abi change (of libv4lconvert, which is almost not
used directly but still).

I've a number of important fixes / changes planned which I hope to
be able to do soon, and then I want to do another 0.8.x release, once
that is out the door I'll merge this, and then do a 0.9.x release
pretty soon after that.

Regards,

Hans


On 05/03/2011 05:26 PM, Yordan Kamenov wrote:
> A libv4l2 plugin will sit in between libv4l2 itself and the actual
> /dev/video device node a fd refers to. It will be called each time
> libv4l2 wants to do an operation (read/write/ioctl) on the actual
> /dev/video node in question.
>
> Signed-off-by: Yordan Kamenov<ykamenov@mm-sol.com>
> ---
>   lib/include/libv4l2-plugin.h                   |   36 ++++++
>   lib/include/libv4lconvert.h                    |    5 +-
>   lib/libv4l2/Makefile                           |    6 +-
>   lib/libv4l2/libv4l2-priv.h                     |   10 ++
>   lib/libv4l2/libv4l2.c                          |   88 ++++++++++----
>   lib/libv4l2/v4l2-plugin.c                      |  158 ++++++++++++++++++++++++
>   lib/libv4l2/v4l2convert.c                      |    9 --
>   lib/libv4lconvert/control/libv4lcontrol-priv.h |    4 +
>   lib/libv4lconvert/control/libv4lcontrol.c      |   35 ++++--
>   lib/libv4lconvert/control/libv4lcontrol.h      |    5 +-
>   lib/libv4lconvert/libv4lconvert-priv.h         |    2 +
>   lib/libv4lconvert/libv4lconvert.c              |   34 ++++--
>   utils/qv4l2/qv4l2.cpp                          |   17 +++-
>   utils/qv4l2/qv4l2.h                            |    1 +
>   14 files changed, 347 insertions(+), 63 deletions(-)
>   create mode 100644 lib/include/libv4l2-plugin.h
>   create mode 100644 lib/libv4l2/v4l2-plugin.c
>
> diff --git a/lib/include/libv4l2-plugin.h b/lib/include/libv4l2-plugin.h
> new file mode 100644
> index 0000000..158c0c2
> --- /dev/null
> +++ b/lib/include/libv4l2-plugin.h
> @@ -0,0 +1,36 @@
> +/*
> +* Copyright (C) 2010 Nokia Corporation<multimedia@maemo.org>
> +
> +* This program is free software; you can redistribute it and/or modify
> +* it under the terms of the GNU Lesser General Public License as published by
> +* the Free Software Foundation; either version 2.1 of the License, or
> +* (at your option) any later version.
> +*
> +* This program is distributed in the hope that it will be useful,
> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +* Lesser General Public License for more details.
> +*
> +* You should have received a copy of the GNU Lesser General Public License
> +* along with this program; if not, write to the Free Software
> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> +*/
> +
> +#ifndef __LIBV4L2_PLUGIN_H
> +#define __LIBV4L2_PLUGIN_H
> +
> +#include<sys/types.h>
> +
> +/* Structure libv4l2_dev_ops holds the calls from libv4ls to video nodes.
> +   They can be normal open/close/ioctl etc. or any of them may be replaced
> +   with a callback by a loaded plugin.
> +*/
> +
> +struct libv4l2_dev_ops {
> +    void * (*init)(int fd);
> +    void (*close)(void *dev_ops_priv);
> +    int (*ioctl)(void *dev_ops_priv, int fd, unsigned long int request, void *arg);
> +    ssize_t (*read)(void *dev_ops_priv, int fd, void *buffer, size_t n);
> +};
> +
> +#endif
> diff --git a/lib/include/libv4lconvert.h b/lib/include/libv4lconvert.h
> index 0264290..351142e 100644
> --- a/lib/include/libv4lconvert.h
> +++ b/lib/include/libv4lconvert.h
> @@ -38,6 +38,8 @@
>
>   #include<linux/videodev2.h>
>
> +#include "libv4l2-plugin.h"
> +
>   #ifdef __cplusplus
>   extern "C" {
>   #endif /* __cplusplus */
> @@ -50,7 +52,8 @@ extern "C" {
>
>   struct v4lconvert_data;
>
> -LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd);
> +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd,
> +		void *dev_ops_priv, struct libv4l2_dev_ops *dev_ops);
>   LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data);
>
>   /* When doing flipping / rotating / video-processing, only supported
> diff --git a/lib/libv4l2/Makefile b/lib/libv4l2/Makefile
> index d78632f..f8b3714 100644
> --- a/lib/libv4l2/Makefile
> +++ b/lib/libv4l2/Makefile
> @@ -1,12 +1,12 @@
>   override CPPFLAGS += -I../include -fvisibility=hidden
>
> -LIBS_libv4l2  = -lpthread
> +LIBS_libv4l2  = -lpthread -ldl
>
> -V4L2_OBJS     = libv4l2.o log.o
> +V4L2_OBJS     = libv4l2.o v4l2-plugin.o log.o
>   V4L2CONVERT   = v4l2convert.so
>   V4L2CONVERT_O = v4l2convert.o libv4l2.so
>   TARGETS       = $(V4L2_LIB) libv4l2.pc
> -INCLUDES      = ../include/libv4l2.h
> +INCLUDES      = ../include/libv4l2.h ../include/libv4l2-plugin.h
>
>   ifeq ($(LINKTYPE),static)
>   V4L2_LIB      = libv4l2.a
> diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
> index 46d6103..d06a508 100644
> --- a/lib/libv4l2/libv4l2-priv.h
> +++ b/lib/libv4l2/libv4l2-priv.h
> @@ -85,8 +85,18 @@ struct v4l2_dev_info {
>   	/* buffer when doing conversion and using read() for read() */
>   	int readbuf_size;
>   	unsigned char *readbuf;
> +	struct libv4l2_dev_ops *dev_ops;
> +	/* plugin info */
> +	void *plugin_library;
> +	void *dev_ops_priv;
>   };
>
> +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret,
> +					struct libv4l2_dev_ops **dev_ops_ret);
> +void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
> +					struct libv4l2_dev_ops *dev_ops);
> +
> +
>   /* From log.c */
>   void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
>
> diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
> index ab85ea7..018350e 100644
> --- a/lib/libv4l2/libv4l2.c
> +++ b/lib/libv4l2/libv4l2.c
> @@ -67,6 +67,7 @@
>   #include<sys/stat.h>
>   #include "libv4l2.h"
>   #include "libv4l2-priv.h"
> +#include "libv4l2-plugin.h"
>
>   /* Note these flags are stored together with the flags passed to v4l2_fd_open()
>      in v4l2_dev_info's flags member, so care should be taken that the do not
> @@ -102,7 +103,8 @@ static int v4l2_request_read_buffers(int index)
>   		devices[index].nreadbuffers;
>   	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   	req.memory = V4L2_MEMORY_MMAP;
> -	result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS,&req);
> +	result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +			devices[index].fd, VIDIOC_REQBUFS,&req);
>   	if (result<  0) {
>   		int saved_err = errno;
>
> @@ -131,7 +133,8 @@ static void v4l2_unrequest_read_buffers(int index)
>   	req.count = 0;
>   	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   	req.memory = V4L2_MEMORY_MMAP;
> -	if (SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS,&req)<  0)
> +	if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +			devices[index].fd, VIDIOC_REQBUFS,&req)<  0)
>   		return;
>
>   	devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES);
> @@ -152,7 +155,8 @@ static int v4l2_map_buffers(int index)
>   		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   		buf.memory = V4L2_MEMORY_MMAP;
>   		buf.index = i;
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF,&buf);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QUERYBUF,&buf);
>   		if (result) {
>   			int saved_err = errno;
>
> @@ -202,7 +206,8 @@ static int v4l2_streamon(int index)
>   	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>
>   	if (!(devices[index].flags&  V4L2_STREAMON)) {
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMON,&type);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_STREAMON,&type);
>   		if (result) {
>   			int saved_err = errno;
>
> @@ -223,7 +228,8 @@ static int v4l2_streamoff(int index)
>   	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>
>   	if (devices[index].flags&  V4L2_STREAMON) {
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMOFF,&type);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_STREAMOFF,&type);
>   		if (result) {
>   			int saved_err = errno;
>
> @@ -252,7 +258,8 @@ static int v4l2_queue_read_buffer(int index, int buffer_index)
>   	buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   	buf.memory = V4L2_MEMORY_MMAP;
>   	buf.index  = buffer_index;
> -	result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF,&buf);
> +	result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +			devices[index].fd, VIDIOC_QBUF,&buf);
>   	if (result) {
>   		int saved_err = errno;
>
> @@ -277,7 +284,8 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
>   		return result;
>
>   	do {
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_DQBUF, buf);
>   		if (result) {
>   			if (errno != EAGAIN) {
>   				int saved_err = errno;
> @@ -356,7 +364,8 @@ static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size)
>   	}
>
>   	do {
> -		result = SYS_READ(devices[index].fd, devices[index].readbuf, buf_size);
> +		result = devices[index].dev_ops->read(devices[index].dev_ops_priv,
> +				devices[index].fd, devices[index].readbuf, buf_size);
>   		if (result<= 0) {
>   			if (result&&  errno != EAGAIN) {
>   				int saved_err = errno;
> @@ -496,7 +505,8 @@ static int v4l2_buffers_mapped(int index)
>   			buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   			buf.memory = V4L2_MEMORY_MMAP;
>   			buf.index = i;
> -			if (SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF,&buf)) {
> +			if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +					devices[index].fd, VIDIOC_QUERYBUF,&buf)) {
>   				int saved_err = errno;
>
>   				V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno));
> @@ -561,6 +571,11 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>   	struct v4l2_capability cap;
>   	struct v4l2_format fmt;
>   	struct v4lconvert_data *convert;
> +	struct libv4l2_dev_ops *dev_ops;
> +	void *plugin_library;
> +	void *plugin_data;
> +
> +	v4l2_plugin_init(fd,&plugin_library,&plugin_data,&dev_ops);
>
>   	/* If no log file was set by the app, see if one was specified through the
>   	   environment */
> @@ -571,9 +586,10 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>   	}
>
>   	/* check that this is an v4l2 device */
> -	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap)) {
> +	if (dev_ops->ioctl(plugin_data, fd, VIDIOC_QUERYCAP,&cap)) {
>   		int saved_err = errno;
>
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
>   		errno = saved_err;
>   		return -1;
> @@ -581,29 +597,38 @@ 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)))
> +			!(cap.capabilities&  (V4L2_CAP_STREAMING | V4L2_CAP_READWRITE))) {
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		return fd;
> +	}
>
>   	/* Get current cam format */
>   	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> -	if (SYS_IOCTL(fd, VIDIOC_G_FMT,&fmt)) {
> +	if (dev_ops->ioctl(plugin_data, fd, VIDIOC_G_FMT,&fmt)) {
>   		int saved_err = errno;
>
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
>   		errno = saved_err;
>   		return -1;
>   	}
>
>   	/* init libv4lconvert */
> -	convert = v4lconvert_create(fd);
> -	if (!convert)
> +	convert = v4lconvert_create(fd, plugin_data, dev_ops);
> +	if (!convert) {
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		return -1;
> +	}
>
>   	/* 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;
> +
> +			devices[index].plugin_library = plugin_library;
> +			devices[index].dev_ops_priv = plugin_data;
> +			devices[index].dev_ops = dev_ops;
>   			break;
>   		}
>   	pthread_mutex_unlock(&v4l2_open_mutex);
> @@ -611,6 +636,7 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>   	if (index == V4L2_MAX_DEVICES) {
>   		V4L2_LOG_ERR("attempting to open more then %d video devices\n",
>   				V4L2_MAX_DEVICES);
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		errno = EBUSY;
>   		return -1;
>   	}
> @@ -700,6 +726,9 @@ int v4l2_close(int fd)
>   	if (result)
>   		return 0;
>
> +	v4l2_plugin_cleanup(devices[index].dev_ops_priv,
> +			devices[index].plugin_library, devices[index].dev_ops);
> +
>   	/* Free resources */
>   	v4l2_unmap_buffers(index);
>   	if (devices[index].convert_mmap_buf != MAP_FAILED) {
> @@ -879,7 +908,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   	}
>
>   	if (!is_capture_request) {
> -		result = SYS_IOCTL(fd, request, arg);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				fd, request, arg);
>   		saved_err = errno;
>   		v4l2_log_ioctl(request, arg, result);
>   		errno = saved_err;
> @@ -927,7 +957,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   	case VIDIOC_QUERYCAP: {
>   		struct v4l2_capability *cap = arg;
>
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYCAP, cap);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QUERYCAP, cap);
>   		if (result == 0)
>   			/* We always support read() as we fake it using mmap mode */
>   			cap->capabilities |= V4L2_CAP_READWRITE;
> @@ -977,8 +1008,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		}
>
>   		if (devices[index].flags&  V4L2_DISABLE_CONVERSION) {
> -			result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT,
> -					dest_fmt);
> +			result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +					devices[index].fd, VIDIOC_TRY_FMT, dest_fmt);
>   			src_fmt = *dest_fmt;
>   		} else {
>   			result = v4lconvert_try_format(devices[index].convert, dest_fmt,
> @@ -1018,7 +1049,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   			break;
>
>   		req_pix_fmt = src_fmt.fmt.pix;
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT,&src_fmt);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_S_FMT,&src_fmt);
>   		if (result) {
>   			saved_err = errno;
>   			V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno));
> @@ -1067,7 +1099,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		if (req->count>  V4L2_MAX_NO_FRAMES)
>   			req->count = V4L2_MAX_NO_FRAMES;
>
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, req);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_REQBUFS, req);
>   		if (result<  0)
>   			break;
>   		result = 0; /* some drivers return the number of buffers on success */
> @@ -1088,7 +1121,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>
>   		/* Do a real query even when converting to let the driver fill in
>   		   things like buf->field */
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, buf);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QUERYBUF, buf);
>   		if (result || !v4l2_needs_conversion(index))
>   			break;
>
> @@ -1115,7 +1149,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   				break;
>   		}
>
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, arg);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QBUF, arg);
>   		break;
>
>   	case VIDIOC_DQBUF: {
> @@ -1128,7 +1163,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		}
>
>   		if (!v4l2_needs_conversion(index)) {
> -			result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
> +			result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +					devices[index].fd, VIDIOC_DQBUF, buf);
>   			if (result) {
>   				int saved_err = errno;
>
> @@ -1187,7 +1223,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		break;
>
>   	default:
> -		result = SYS_IOCTL(fd, request, arg);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				fd, request, arg);
>   		break;
>   	}
>
> @@ -1217,7 +1254,8 @@ ssize_t v4l2_read(int fd, void *dest, size_t n)
>   	   it */
>   	if ((devices[index].flags&  V4L2_SUPPORTS_READ)&&
>   			!v4l2_needs_conversion(index)) {
> -		result = SYS_READ(fd, dest, n);
> +		result = devices[index].dev_ops->read(devices[index].dev_ops_priv,
> +				fd, dest, n);
>   		goto leave;
>   	}
>
> diff --git a/lib/libv4l2/v4l2-plugin.c b/lib/libv4l2/v4l2-plugin.c
> new file mode 100644
> index 0000000..3735193
> --- /dev/null
> +++ b/lib/libv4l2/v4l2-plugin.c
> @@ -0,0 +1,158 @@
> +/*
> +* Copyright (C) 2010 Nokia Corporation<multimedia@maemo.org>
> +
> +* This program is free software; you can redistribute it and/or modify
> +* it under the terms of the GNU Lesser General Public License as published by
> +* the Free Software Foundation; either version 2.1 of the License, or
> +* (at your option) any later version.
> +*
> +* This program is distributed in the hope that it will be useful,
> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +* Lesser General Public License for more details.
> +*
> +* You should have received a copy of the GNU Lesser General Public License
> +* along with this program; if not, write to the Free Software
> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> +*/
> +
> +#include<stdarg.h>
> +#include<dlfcn.h>
> +#include<fcntl.h>
> +#include<glob.h>
> +#include<sys/stat.h>
> +#include<sys/mman.h>
> +#include "libv4l2.h"
> +#include "libv4l2-priv.h"
> +#include "libv4l2-plugin.h"
> +
> +/* libv4l plugin support:
> +   it is provided by functions v4l2_plugin_[open,close,etc].
> +
> +   When open() is called libv4l dlopens files in /usr/lib[64]/libv4l/plugins
> +   1 at a time and call open callback passing through the applications
> +   parameters unmodified.
> +
> +   If a plugin is relevant for the specified device node, it can indicate so
> +   by returning a value other then -1 (the actual file descriptor).
> +   As soon as a plugin returns another value then -1 plugin loading stops and
> +   information about it (fd and corresponding library handle) is stored. For
> +   each function v4l2_[ioctl,read,close,etc] is called corresponding
> +   v4l2_plugin_* function which looks if there is loaded plugin for that file
> +   and call it's callbacks.
> +
> +   v4l2_plugin_* function indicates by it's first argument if plugin was used,
> +   and if it was not then v4l2_* functions proceed with their usual behavior.
> +*/
> +
> +#define PLUGINS_PATTERN "/usr/lib/libv4l/plugins/*.so"
> +
> +static void * dev_init(int fd)
> +{
> +	return NULL;
> +}
> +
> +static void dev_close(void *dev_ops_priv)
> +{
> +}
> +
> +static int dev_ioctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg)
> +{
> +	return SYS_IOCTL(fd, cmd, arg);
> +}
> +
> +static ssize_t dev_read(void *dev_ops_priv, int fd, void *buf, size_t len)
> +{
> +	return SYS_READ(fd, buf, len);
> +}
> +
> +
> +struct libv4l2_dev_ops default_operations = {
> +	.init =&dev_init,
> +	.close =&dev_close,
> +	.ioctl =&dev_ioctl,
> +	.read =&dev_read
> +};
> +
> +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret,
> +					struct libv4l2_dev_ops **dev_ops_ret)
> +{
> +	char *error;
> +	int glob_ret, plugin_num;
> +	void *plugin_library = NULL;
> +	struct libv4l2_dev_ops *libv4l2_plugin = NULL;
> +	glob_t globbuf;
> +
> +	*dev_ops_ret =&default_operations;
> +
> +	glob_ret = glob(PLUGINS_PATTERN, 0, NULL,&globbuf);
> +
> +	if (glob_ret == GLOB_NOSPACE) {
> +		*plugin_lib_ret = NULL;
> +		*plugin_priv_ret = NULL;
> +		return;
> +	}
> +
> +	if (glob_ret == GLOB_ABORTED || glob_ret == GLOB_NOMATCH) {
> +		*plugin_lib_ret = NULL;
> +		*plugin_priv_ret = NULL;
> +		goto leave;
> +	}
> +
> +	for (plugin_num = 0; plugin_num<  globbuf.gl_pathc; plugin_num++) {
> +
> +		V4L2_LOG("PLUGIN: dlopen(%s);\n", globbuf.gl_pathv[plugin_num]);
> +
> +		plugin_library = dlopen(globbuf.gl_pathv[plugin_num],
> +							RTLD_LAZY);
> +
> +		if (!plugin_library)
> +			continue;
> +
> +		dlerror();    /* Clear any existing error */
> +		libv4l2_plugin = (struct libv4l2_dev_ops *)
> +					dlsym(plugin_library, "libv4l2_plugin");
> +
> +		error = dlerror();
> +		if (error != NULL)  {
> +			V4L2_LOG_ERR("PLUGIN: dlsym failed: %s\n", error);
> +			dlclose(plugin_library);
> +
> +			continue;
> +		}
> +
> +		*plugin_priv_ret = NULL;
> +		if (libv4l2_plugin->init)
> +			*plugin_priv_ret = libv4l2_plugin->init(fd);
> +
> +		if (!*plugin_priv_ret) {
> +			V4L2_LOG("PLUGIN: plugin open() returned NULL\n");
> +			dlclose(plugin_library);
> +			*plugin_lib_ret = NULL;
> +			continue;
> +		}
> +
> +		(*dev_ops_ret)->init = libv4l2_plugin->init;
> +		if (libv4l2_plugin->close != NULL)
> +			(*dev_ops_ret)->close = libv4l2_plugin->close;
> +		if (libv4l2_plugin->ioctl != NULL)
> +			(*dev_ops_ret)->ioctl = libv4l2_plugin->ioctl;
> +		if (libv4l2_plugin->read != NULL)
> +			(*dev_ops_ret)->read = libv4l2_plugin->read;
> +
> +		break;
> +
> +	}
> +
> +leave:
> +	globfree(&globbuf);
> +}
> +
> +void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
> +					struct libv4l2_dev_ops *dev_ops)
> +{
> +	if (plugin_priv) {
> +		dev_ops->close(plugin_priv);
> +		dlclose(plugin_lib);
> +	}
> +}
> diff --git a/lib/libv4l2/v4l2convert.c b/lib/libv4l2/v4l2convert.c
> index e251085..03f34ad 100644
> --- a/lib/libv4l2/v4l2convert.c
> +++ b/lib/libv4l2/v4l2convert.c
> @@ -46,7 +46,6 @@
>   LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
>   {
>   	int fd;
> -	struct v4l2_capability cap;
>   	int v4l_device = 0;
>
>   	/* check if we're opening a video4linux2 device */
> @@ -76,14 +75,6 @@ LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
>   	if (fd == -1 || !v4l_device)
>   		return fd;
>
> -	/* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */
> -	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap))
> -		return fd;
> -
> -	/* libv4l2 only adds functionality to capture capable devices */
> -	if (!(cap.capabilities&  V4L2_CAP_VIDEO_CAPTURE))
> -		return fd;
> -
>   	/* Try to Register with libv4l2 (in case of failure pass the fd to the
>   	   application as is) */
>   	v4l2_fd_open(fd, 0);
> diff --git a/lib/libv4lconvert/control/libv4lcontrol-priv.h b/lib/libv4lconvert/control/libv4lcontrol-priv.h
> index 22cdf34..1259e6b 100644
> --- a/lib/libv4lconvert/control/libv4lcontrol-priv.h
> +++ b/lib/libv4lconvert/control/libv4lcontrol-priv.h
> @@ -22,6 +22,8 @@
>   #ifndef __LIBV4LCONTROL_PRIV_H
>   #define __LIBV4LCONTROL_PRIV_H
>
> +#include "libv4l2-plugin.h"
> +
>   #define V4LCONTROL_SHM_SIZE 4096
>
>   #define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01
> @@ -37,6 +39,8 @@ struct v4lcontrol_data {
>   	unsigned int *shm_values; /* shared memory control value store */
>   	unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */
>   	const struct v4lcontrol_flags_info *flags_info;
> +	struct libv4l2_dev_ops *dev_ops;
> +	void *dev_ops_priv;
>   };
>
>   struct v4lcontrol_flags_info {
> diff --git a/lib/libv4lconvert/control/libv4lcontrol.c b/lib/libv4lconvert/control/libv4lcontrol.c
> index 116bef5..24a5600 100644
> --- a/lib/libv4lconvert/control/libv4lcontrol.c
> +++ b/lib/libv4lconvert/control/libv4lcontrol.c
> @@ -747,7 +747,8 @@ static void v4lcontrol_get_flags_from_db(struct v4lcontrol_data *data,
>   		}
>   }
>
> -struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
> +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
> +		struct libv4l2_dev_ops *dev_ops, int always_needs_conversion)
>   {
>   	int shm_fd;
>   	int i, rc, got_usb_ids, init = 0;
> @@ -767,10 +768,12 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	}
>
>   	data->fd = fd;
> +	data->dev_ops = dev_ops;
> +	data->dev_ops_priv = dev_ops_priv;
>
>   	/* Check if the driver has indicated some form of flipping is needed */
> -	if ((SYS_IOCTL(data->fd, VIDIOC_G_INPUT,&input.index) == 0)&&
> -			(SYS_IOCTL(data->fd, VIDIOC_ENUMINPUT,&input) == 0)) {
> +	if ((data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_G_INPUT,&input.index) == 0)&&
> +			(data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_ENUMINPUT,&input) == 0)) {
>   		if (input.status&  V4L2_IN_ST_HFLIP)
>   			data->flags |= V4LCONTROL_HFLIPPED;
>   		if (input.status&  V4L2_IN_ST_VFLIP)
> @@ -787,7 +790,8 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   		data->flags = strtol(s, NULL, 0);
>
>   	ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
> -	if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl) == 0)
> +	if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_QUERYCTRL,&ctrl) == 0)
>   		data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL;
>
>   	/* If the device always needs conversion, we can add fake controls at no cost
> @@ -795,7 +799,8 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	if (always_needs_conversion || v4lcontrol_needs_conversion(data)) {
>   		for (i = 0; i<  V4LCONTROL_AUTO_ENABLE_COUNT; i++) {
>   			ctrl.id = fake_controls[i].id;
> -			rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +			rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +					VIDIOC_QUERYCTRL,&ctrl);
>   			if (rc == -1 || (rc == 0&&  (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED)))
>   				data->controls |= 1<<  i;
>   		}
> @@ -807,17 +812,20 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	   different sensors with / without autogain or the necessary controls. */
>   	while (data->flags&  V4LCONTROL_WANTS_AUTOGAIN) {
>   		ctrl.id = V4L2_CID_AUTOGAIN;
> -		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_QUERYCTRL,&ctrl);
>   		if (rc == 0&&  !(ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>   			break;
>
>   		ctrl.id = V4L2_CID_EXPOSURE;
> -		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_QUERYCTRL,&ctrl);
>   		if (rc != 0 || (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>   			break;
>
>   		ctrl.id = V4L2_CID_GAIN;
> -		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_QUERYCTRL,&ctrl);
>   		if (rc != 0 || (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>   			break;
>
> @@ -834,7 +842,7 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	if (data->controls == 0)
>   		return data; /* No need to create a shared memory segment */
>
> -	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap)) {
> +	if (data->dev_ops->ioctl(data->dev_ops_priv, fd, VIDIOC_QUERYCAP,&cap)) {
>   		perror("libv4lcontrol: error querying device capabilities");
>   		goto error;
>   	}
> @@ -1020,7 +1028,8 @@ int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
>   		}
>
>   	/* find out what the kernel driver would respond. */
> -	retval = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, arg);
> +	retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_QUERYCTRL, arg);
>
>   	if ((data->priv_flags&  V4LCONTROL_SUPPORTS_NEXT_CTRL)&&
>   			(orig_id&  V4L2_CTRL_FLAG_NEXT_CTRL)) {
> @@ -1057,7 +1066,8 @@ int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg)
>   			return 0;
>   		}
>
> -	return SYS_IOCTL(data->fd, VIDIOC_G_CTRL, arg);
> +	return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_G_CTRL, arg);
>   }
>
>   int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
> @@ -1078,7 +1088,8 @@ int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
>   			return 0;
>   		}
>
> -	return SYS_IOCTL(data->fd, VIDIOC_S_CTRL, arg);
> +	return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_S_CTRL, arg);
>   }
>
>   int v4lcontrol_get_flags(struct v4lcontrol_data *data)
> diff --git a/lib/libv4lconvert/control/libv4lcontrol.h b/lib/libv4lconvert/control/libv4lcontrol.h
> index 974e97a..1c09546 100644
> --- a/lib/libv4lconvert/control/libv4lcontrol.h
> +++ b/lib/libv4lconvert/control/libv4lcontrol.h
> @@ -22,6 +22,8 @@
>   #ifndef __LIBV4LCONTROL_H
>   #define __LIBV4LCONTROL_H
>
> +#include "libv4l2-plugin.h"
> +
>   /* Flags */
>   #define V4LCONTROL_HFLIPPED              0x01
>   #define V4LCONTROL_VFLIPPED              0x02
> @@ -47,7 +49,8 @@ enum {
>
>   struct v4lcontrol_data;
>
> -struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion);
> +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
> +		struct libv4l2_dev_ops *dev_ops, int always_needs_conversion);
>   void v4lcontrol_destroy(struct v4lcontrol_data *data);
>
>   /* Functions used by v4lprocessing to get the control state */
> diff --git a/lib/libv4lconvert/libv4lconvert-priv.h b/lib/libv4lconvert/libv4lconvert-priv.h
> index 84c706e..9cb6e1c 100644
> --- a/lib/libv4lconvert/libv4lconvert-priv.h
> +++ b/lib/libv4lconvert/libv4lconvert-priv.h
> @@ -65,6 +65,8 @@ struct v4lconvert_data {
>   	unsigned char *convert_pixfmt_buf;
>   	struct v4lcontrol_data *control;
>   	struct v4lprocessing_data *processing;
> +	struct libv4l2_dev_ops *dev_ops;
> +	void *dev_ops_priv;
>
>   	/* Data for external decompression helpers code */
>   	pid_t decompress_pid;
> diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c
> index e4863fd..6861986 100644
> --- a/lib/libv4lconvert/libv4lconvert.c
> +++ b/lib/libv4lconvert/libv4lconvert.c
> @@ -92,7 +92,8 @@ static const int v4lconvert_crop_res[][2] = {
>   	{ 176, 144 },
>   };
>
> -struct v4lconvert_data *v4lconvert_create(int fd)
> +struct v4lconvert_data *v4lconvert_create(int fd, void *dev_ops_priv,
> +		struct libv4l2_dev_ops *dev_ops)
>   {
>   	int i, j;
>   	struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data));
> @@ -108,6 +109,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>   	}
>
>   	data->fd = fd;
> +	data->dev_ops = dev_ops;
> +	data->dev_ops_priv = dev_ops_priv;
>   	data->decompress_pid = -1;
>
>   	/* Check supported formats */
> @@ -116,7 +119,7 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>
>   		fmt.index = i;
>
> -		if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT,&fmt))
> +		if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_ENUM_FMT,&fmt))
>   			break;
>
>   		for (j = 0; j<  ARRAY_SIZE(supported_src_pixfmts); j++)
> @@ -135,7 +138,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>   	data->no_formats = i;
>
>   	/* Check if this cam has any special flags */
> -	if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP,&cap) == 0) {
> +	if (data->dev_ops->ioctl(data->dev_ops_priv,
> +					data->fd, VIDIOC_QUERYCAP,&cap) == 0) {
>   		if (!strcmp((char *)cap.driver, "uvcvideo"))
>   			data->flags |= V4LCONVERT_IS_UVC;
>
> @@ -143,7 +147,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>   			always_needs_conversion = 0;
>   	}
>
> -	data->control = v4lcontrol_create(fd, always_needs_conversion);
> +	data->control = v4lcontrol_create(fd, dev_ops_priv, dev_ops,
> +						always_needs_conversion);
>   	if (!data->control) {
>   		free(data);
>   		return NULL;
> @@ -206,7 +211,8 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
>   	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>   			(!v4lconvert_supported_dst_fmt_only(data)&&
>   			fmt->index<  data->no_formats))
> -		return SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, fmt);
> +		return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +						VIDIOC_ENUM_FMT, fmt);
>
>   	for (i = 0; i<  ARRAY_SIZE(supported_dst_pixfmts); i++)
>   		if (v4lconvert_supported_dst_fmt_only(data) ||
> @@ -314,7 +320,8 @@ static int v4lconvert_do_try_format(struct v4lconvert_data *data,
>   		try_fmt = *dest_fmt;
>   		try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;
>
> -		if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT,&try_fmt)) {
> +		if (!data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +						VIDIOC_TRY_FMT,&try_fmt)) {
>   			if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) {
>   				int size_x_diff = abs((int)try_fmt.fmt.pix.width -
>   						(int)dest_fmt->fmt.pix.width);
> @@ -382,7 +389,8 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
>   	if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
>   			dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>   			v4lconvert_do_try_format(data,&try_dest,&try_src)) {
> -		result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt);
> +		result = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +							VIDIOC_TRY_FMT, dest_fmt);
>   		if (src_fmt)
>   			*src_fmt = *dest_fmt;
>   		return result;
> @@ -1241,7 +1249,8 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
>
>   	for (i = 0; ; i++) {
>   		frmsize.index = i;
> -		if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES,&frmsize))
> +		if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +					VIDIOC_ENUM_FRAMESIZES,&frmsize))
>   			break;
>
>   		/* We got a framesize, check we don't have the same one already */
> @@ -1301,7 +1310,8 @@ int v4lconvert_enum_framesizes(struct v4lconvert_data *data,
>   			errno = EINVAL;
>   			return -1;
>   		}
> -		return SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize);
> +		return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +						VIDIOC_ENUM_FRAMESIZES, frmsize);
>   	}
>
>   	if (frmsize->index>= data->no_framesizes) {
> @@ -1337,7 +1347,8 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
>   			errno = EINVAL;
>   			return -1;
>   		}
> -		res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
> +		res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>   		if (res)
>   			V4LCONVERT_ERR("%s\n", strerror(errno));
>   		return res;
> @@ -1382,7 +1393,8 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
>   	frmival->pixel_format = src_fmt.fmt.pix.pixelformat;
>   	frmival->width = src_fmt.fmt.pix.width;
>   	frmival->height = src_fmt.fmt.pix.height;
> -	res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
> +	res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>   	if (res) {
>   		int dest_pixfmt = dest_fmt.fmt.pix.pixelformat;
>   		int src_pixfmt  = src_fmt.fmt.pix.pixelformat;
> diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
> index 232a318..349d1ca 100644
> --- a/utils/qv4l2/qv4l2.cpp
> +++ b/utils/qv4l2/qv4l2.cpp
> @@ -47,6 +47,17 @@
>   #include<sys/mman.h>
>   #include<errno.h>
>   #include<dirent.h>
> +#include<syscall.h>
> +
> +int defIoctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg)
> +{
> +	return syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd),
> +					(void *)(arg));
> +}
> +ssize_t defRead(void *dev_ops_priv, int fd, void *buf, size_t len)
> +{
> +	return syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len));
> +}
>
>   ApplicationWindow::ApplicationWindow() :
>   	m_capture(NULL),
> @@ -59,6 +70,10 @@ ApplicationWindow::ApplicationWindow() :
>   	m_frameData = NULL;
>   	m_nbuffers = 0;
>   	m_buffers = NULL;
> +	m_defauldDevOps.init = NULL;
> +	m_defauldDevOps.close = NULL;
> +	m_defauldDevOps.ioctl =&defIoctl;
> +	m_defauldDevOps.read =&defRead;
>
>   	QAction *openAct = new QAction(QIcon(":/fileopen.png"), "&Open device", this);
>   	openAct->setStatusTip("Open a v4l device, use libv4l2 wrapper if possible");
> @@ -144,7 +159,7 @@ void ApplicationWindow::setDevice(const QString&device, bool rawOpen)
>   	}
>   	m_tabs->show();
>   	m_tabs->setFocus();
> -	m_convertData = v4lconvert_create(fd());
> +	m_convertData = v4lconvert_create(fd(), NULL,&m_defauldDevOps);
>   	m_capStartAct->setEnabled(fd()>= 0);
>   }
>
> diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
> index 0a3e5ef..d847fe6 100644
> --- a/utils/qv4l2/qv4l2.h
> +++ b/utils/qv4l2/qv4l2.h
> @@ -91,6 +91,7 @@ private:
>   	unsigned char *m_frameData;
>   	unsigned m_nbuffers;
>   	struct v4lconvert_data *m_convertData;
> +	struct libv4l2_dev_ops m_defauldDevOps;
>   	CapMethod m_capMethod;
>
>   private slots:
--
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
Hans de Goede July 17, 2011, 9 p.m. UTC | #2
Hi,

Sorry it took so long, but I've just merged the plugin
support into v4l-utils git. I did make some minor mods /
bugfixes before merging, see the commit message in git.

Regards,

Hans

p.s.

I think we should expand the plugin support with support
for a output devices, iow add a write() dev_op. If you
guys agree I can easily do so myself, we should do this
asap before people start depending on the ABI
(although there is no ABI stability promise until I
release 0.10.x, see my message to the list wrt
the start of the 0.9.x cycle).

While on the subject of keeping the plugin ABI, I suggest
that we add a number of extra dev_ops named reserved1 --
reserved8 for future use and a comment that these should
all be set to NULL. Then we can easily add another op
on the future while keeping compatibility with older
plugins.


On 05/03/2011 05:26 PM, Yordan Kamenov wrote:
> A libv4l2 plugin will sit in between libv4l2 itself and the actual
> /dev/video device node a fd refers to. It will be called each time
> libv4l2 wants to do an operation (read/write/ioctl) on the actual
> /dev/video node in question.
>
> Signed-off-by: Yordan Kamenov<ykamenov@mm-sol.com>
> ---
>   lib/include/libv4l2-plugin.h                   |   36 ++++++
>   lib/include/libv4lconvert.h                    |    5 +-
>   lib/libv4l2/Makefile                           |    6 +-
>   lib/libv4l2/libv4l2-priv.h                     |   10 ++
>   lib/libv4l2/libv4l2.c                          |   88 ++++++++++----
>   lib/libv4l2/v4l2-plugin.c                      |  158 ++++++++++++++++++++++++
>   lib/libv4l2/v4l2convert.c                      |    9 --
>   lib/libv4lconvert/control/libv4lcontrol-priv.h |    4 +
>   lib/libv4lconvert/control/libv4lcontrol.c      |   35 ++++--
>   lib/libv4lconvert/control/libv4lcontrol.h      |    5 +-
>   lib/libv4lconvert/libv4lconvert-priv.h         |    2 +
>   lib/libv4lconvert/libv4lconvert.c              |   34 ++++--
>   utils/qv4l2/qv4l2.cpp                          |   17 +++-
>   utils/qv4l2/qv4l2.h                            |    1 +
>   14 files changed, 347 insertions(+), 63 deletions(-)
>   create mode 100644 lib/include/libv4l2-plugin.h
>   create mode 100644 lib/libv4l2/v4l2-plugin.c
>
> diff --git a/lib/include/libv4l2-plugin.h b/lib/include/libv4l2-plugin.h
> new file mode 100644
> index 0000000..158c0c2
> --- /dev/null
> +++ b/lib/include/libv4l2-plugin.h
> @@ -0,0 +1,36 @@
> +/*
> +* Copyright (C) 2010 Nokia Corporation<multimedia@maemo.org>
> +
> +* This program is free software; you can redistribute it and/or modify
> +* it under the terms of the GNU Lesser General Public License as published by
> +* the Free Software Foundation; either version 2.1 of the License, or
> +* (at your option) any later version.
> +*
> +* This program is distributed in the hope that it will be useful,
> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +* Lesser General Public License for more details.
> +*
> +* You should have received a copy of the GNU Lesser General Public License
> +* along with this program; if not, write to the Free Software
> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> +*/
> +
> +#ifndef __LIBV4L2_PLUGIN_H
> +#define __LIBV4L2_PLUGIN_H
> +
> +#include<sys/types.h>
> +
> +/* Structure libv4l2_dev_ops holds the calls from libv4ls to video nodes.
> +   They can be normal open/close/ioctl etc. or any of them may be replaced
> +   with a callback by a loaded plugin.
> +*/
> +
> +struct libv4l2_dev_ops {
> +    void * (*init)(int fd);
> +    void (*close)(void *dev_ops_priv);
> +    int (*ioctl)(void *dev_ops_priv, int fd, unsigned long int request, void *arg);
> +    ssize_t (*read)(void *dev_ops_priv, int fd, void *buffer, size_t n);
> +};
> +
> +#endif
> diff --git a/lib/include/libv4lconvert.h b/lib/include/libv4lconvert.h
> index 0264290..351142e 100644
> --- a/lib/include/libv4lconvert.h
> +++ b/lib/include/libv4lconvert.h
> @@ -38,6 +38,8 @@
>
>   #include<linux/videodev2.h>
>
> +#include "libv4l2-plugin.h"
> +
>   #ifdef __cplusplus
>   extern "C" {
>   #endif /* __cplusplus */
> @@ -50,7 +52,8 @@ extern "C" {
>
>   struct v4lconvert_data;
>
> -LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd);
> +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd,
> +		void *dev_ops_priv, struct libv4l2_dev_ops *dev_ops);
>   LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data);
>
>   /* When doing flipping / rotating / video-processing, only supported
> diff --git a/lib/libv4l2/Makefile b/lib/libv4l2/Makefile
> index d78632f..f8b3714 100644
> --- a/lib/libv4l2/Makefile
> +++ b/lib/libv4l2/Makefile
> @@ -1,12 +1,12 @@
>   override CPPFLAGS += -I../include -fvisibility=hidden
>
> -LIBS_libv4l2  = -lpthread
> +LIBS_libv4l2  = -lpthread -ldl
>
> -V4L2_OBJS     = libv4l2.o log.o
> +V4L2_OBJS     = libv4l2.o v4l2-plugin.o log.o
>   V4L2CONVERT   = v4l2convert.so
>   V4L2CONVERT_O = v4l2convert.o libv4l2.so
>   TARGETS       = $(V4L2_LIB) libv4l2.pc
> -INCLUDES      = ../include/libv4l2.h
> +INCLUDES      = ../include/libv4l2.h ../include/libv4l2-plugin.h
>
>   ifeq ($(LINKTYPE),static)
>   V4L2_LIB      = libv4l2.a
> diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
> index 46d6103..d06a508 100644
> --- a/lib/libv4l2/libv4l2-priv.h
> +++ b/lib/libv4l2/libv4l2-priv.h
> @@ -85,8 +85,18 @@ struct v4l2_dev_info {
>   	/* buffer when doing conversion and using read() for read() */
>   	int readbuf_size;
>   	unsigned char *readbuf;
> +	struct libv4l2_dev_ops *dev_ops;
> +	/* plugin info */
> +	void *plugin_library;
> +	void *dev_ops_priv;
>   };
>
> +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret,
> +					struct libv4l2_dev_ops **dev_ops_ret);
> +void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
> +					struct libv4l2_dev_ops *dev_ops);
> +
> +
>   /* From log.c */
>   void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
>
> diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
> index ab85ea7..018350e 100644
> --- a/lib/libv4l2/libv4l2.c
> +++ b/lib/libv4l2/libv4l2.c
> @@ -67,6 +67,7 @@
>   #include<sys/stat.h>
>   #include "libv4l2.h"
>   #include "libv4l2-priv.h"
> +#include "libv4l2-plugin.h"
>
>   /* Note these flags are stored together with the flags passed to v4l2_fd_open()
>      in v4l2_dev_info's flags member, so care should be taken that the do not
> @@ -102,7 +103,8 @@ static int v4l2_request_read_buffers(int index)
>   		devices[index].nreadbuffers;
>   	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   	req.memory = V4L2_MEMORY_MMAP;
> -	result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS,&req);
> +	result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +			devices[index].fd, VIDIOC_REQBUFS,&req);
>   	if (result<  0) {
>   		int saved_err = errno;
>
> @@ -131,7 +133,8 @@ static void v4l2_unrequest_read_buffers(int index)
>   	req.count = 0;
>   	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   	req.memory = V4L2_MEMORY_MMAP;
> -	if (SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS,&req)<  0)
> +	if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +			devices[index].fd, VIDIOC_REQBUFS,&req)<  0)
>   		return;
>
>   	devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES);
> @@ -152,7 +155,8 @@ static int v4l2_map_buffers(int index)
>   		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   		buf.memory = V4L2_MEMORY_MMAP;
>   		buf.index = i;
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF,&buf);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QUERYBUF,&buf);
>   		if (result) {
>   			int saved_err = errno;
>
> @@ -202,7 +206,8 @@ static int v4l2_streamon(int index)
>   	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>
>   	if (!(devices[index].flags&  V4L2_STREAMON)) {
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMON,&type);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_STREAMON,&type);
>   		if (result) {
>   			int saved_err = errno;
>
> @@ -223,7 +228,8 @@ static int v4l2_streamoff(int index)
>   	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>
>   	if (devices[index].flags&  V4L2_STREAMON) {
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMOFF,&type);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_STREAMOFF,&type);
>   		if (result) {
>   			int saved_err = errno;
>
> @@ -252,7 +258,8 @@ static int v4l2_queue_read_buffer(int index, int buffer_index)
>   	buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   	buf.memory = V4L2_MEMORY_MMAP;
>   	buf.index  = buffer_index;
> -	result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF,&buf);
> +	result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +			devices[index].fd, VIDIOC_QBUF,&buf);
>   	if (result) {
>   		int saved_err = errno;
>
> @@ -277,7 +284,8 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
>   		return result;
>
>   	do {
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_DQBUF, buf);
>   		if (result) {
>   			if (errno != EAGAIN) {
>   				int saved_err = errno;
> @@ -356,7 +364,8 @@ static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size)
>   	}
>
>   	do {
> -		result = SYS_READ(devices[index].fd, devices[index].readbuf, buf_size);
> +		result = devices[index].dev_ops->read(devices[index].dev_ops_priv,
> +				devices[index].fd, devices[index].readbuf, buf_size);
>   		if (result<= 0) {
>   			if (result&&  errno != EAGAIN) {
>   				int saved_err = errno;
> @@ -496,7 +505,8 @@ static int v4l2_buffers_mapped(int index)
>   			buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>   			buf.memory = V4L2_MEMORY_MMAP;
>   			buf.index = i;
> -			if (SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF,&buf)) {
> +			if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +					devices[index].fd, VIDIOC_QUERYBUF,&buf)) {
>   				int saved_err = errno;
>
>   				V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno));
> @@ -561,6 +571,11 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>   	struct v4l2_capability cap;
>   	struct v4l2_format fmt;
>   	struct v4lconvert_data *convert;
> +	struct libv4l2_dev_ops *dev_ops;
> +	void *plugin_library;
> +	void *plugin_data;
> +
> +	v4l2_plugin_init(fd,&plugin_library,&plugin_data,&dev_ops);
>
>   	/* If no log file was set by the app, see if one was specified through the
>   	   environment */
> @@ -571,9 +586,10 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>   	}
>
>   	/* check that this is an v4l2 device */
> -	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap)) {
> +	if (dev_ops->ioctl(plugin_data, fd, VIDIOC_QUERYCAP,&cap)) {
>   		int saved_err = errno;
>
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
>   		errno = saved_err;
>   		return -1;
> @@ -581,29 +597,38 @@ 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)))
> +			!(cap.capabilities&  (V4L2_CAP_STREAMING | V4L2_CAP_READWRITE))) {
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		return fd;
> +	}
>
>   	/* Get current cam format */
>   	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> -	if (SYS_IOCTL(fd, VIDIOC_G_FMT,&fmt)) {
> +	if (dev_ops->ioctl(plugin_data, fd, VIDIOC_G_FMT,&fmt)) {
>   		int saved_err = errno;
>
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
>   		errno = saved_err;
>   		return -1;
>   	}
>
>   	/* init libv4lconvert */
> -	convert = v4lconvert_create(fd);
> -	if (!convert)
> +	convert = v4lconvert_create(fd, plugin_data, dev_ops);
> +	if (!convert) {
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		return -1;
> +	}
>
>   	/* 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;
> +
> +			devices[index].plugin_library = plugin_library;
> +			devices[index].dev_ops_priv = plugin_data;
> +			devices[index].dev_ops = dev_ops;
>   			break;
>   		}
>   	pthread_mutex_unlock(&v4l2_open_mutex);
> @@ -611,6 +636,7 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>   	if (index == V4L2_MAX_DEVICES) {
>   		V4L2_LOG_ERR("attempting to open more then %d video devices\n",
>   				V4L2_MAX_DEVICES);
> +		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>   		errno = EBUSY;
>   		return -1;
>   	}
> @@ -700,6 +726,9 @@ int v4l2_close(int fd)
>   	if (result)
>   		return 0;
>
> +	v4l2_plugin_cleanup(devices[index].dev_ops_priv,
> +			devices[index].plugin_library, devices[index].dev_ops);
> +
>   	/* Free resources */
>   	v4l2_unmap_buffers(index);
>   	if (devices[index].convert_mmap_buf != MAP_FAILED) {
> @@ -879,7 +908,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   	}
>
>   	if (!is_capture_request) {
> -		result = SYS_IOCTL(fd, request, arg);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				fd, request, arg);
>   		saved_err = errno;
>   		v4l2_log_ioctl(request, arg, result);
>   		errno = saved_err;
> @@ -927,7 +957,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   	case VIDIOC_QUERYCAP: {
>   		struct v4l2_capability *cap = arg;
>
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYCAP, cap);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QUERYCAP, cap);
>   		if (result == 0)
>   			/* We always support read() as we fake it using mmap mode */
>   			cap->capabilities |= V4L2_CAP_READWRITE;
> @@ -977,8 +1008,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		}
>
>   		if (devices[index].flags&  V4L2_DISABLE_CONVERSION) {
> -			result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT,
> -					dest_fmt);
> +			result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +					devices[index].fd, VIDIOC_TRY_FMT, dest_fmt);
>   			src_fmt = *dest_fmt;
>   		} else {
>   			result = v4lconvert_try_format(devices[index].convert, dest_fmt,
> @@ -1018,7 +1049,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   			break;
>
>   		req_pix_fmt = src_fmt.fmt.pix;
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT,&src_fmt);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_S_FMT,&src_fmt);
>   		if (result) {
>   			saved_err = errno;
>   			V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno));
> @@ -1067,7 +1099,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		if (req->count>  V4L2_MAX_NO_FRAMES)
>   			req->count = V4L2_MAX_NO_FRAMES;
>
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, req);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_REQBUFS, req);
>   		if (result<  0)
>   			break;
>   		result = 0; /* some drivers return the number of buffers on success */
> @@ -1088,7 +1121,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>
>   		/* Do a real query even when converting to let the driver fill in
>   		   things like buf->field */
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, buf);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QUERYBUF, buf);
>   		if (result || !v4l2_needs_conversion(index))
>   			break;
>
> @@ -1115,7 +1149,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   				break;
>   		}
>
> -		result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, arg);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				devices[index].fd, VIDIOC_QBUF, arg);
>   		break;
>
>   	case VIDIOC_DQBUF: {
> @@ -1128,7 +1163,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		}
>
>   		if (!v4l2_needs_conversion(index)) {
> -			result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
> +			result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +					devices[index].fd, VIDIOC_DQBUF, buf);
>   			if (result) {
>   				int saved_err = errno;
>
> @@ -1187,7 +1223,8 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
>   		break;
>
>   	default:
> -		result = SYS_IOCTL(fd, request, arg);
> +		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
> +				fd, request, arg);
>   		break;
>   	}
>
> @@ -1217,7 +1254,8 @@ ssize_t v4l2_read(int fd, void *dest, size_t n)
>   	   it */
>   	if ((devices[index].flags&  V4L2_SUPPORTS_READ)&&
>   			!v4l2_needs_conversion(index)) {
> -		result = SYS_READ(fd, dest, n);
> +		result = devices[index].dev_ops->read(devices[index].dev_ops_priv,
> +				fd, dest, n);
>   		goto leave;
>   	}
>
> diff --git a/lib/libv4l2/v4l2-plugin.c b/lib/libv4l2/v4l2-plugin.c
> new file mode 100644
> index 0000000..3735193
> --- /dev/null
> +++ b/lib/libv4l2/v4l2-plugin.c
> @@ -0,0 +1,158 @@
> +/*
> +* Copyright (C) 2010 Nokia Corporation<multimedia@maemo.org>
> +
> +* This program is free software; you can redistribute it and/or modify
> +* it under the terms of the GNU Lesser General Public License as published by
> +* the Free Software Foundation; either version 2.1 of the License, or
> +* (at your option) any later version.
> +*
> +* This program is distributed in the hope that it will be useful,
> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +* Lesser General Public License for more details.
> +*
> +* You should have received a copy of the GNU Lesser General Public License
> +* along with this program; if not, write to the Free Software
> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> +*/
> +
> +#include<stdarg.h>
> +#include<dlfcn.h>
> +#include<fcntl.h>
> +#include<glob.h>
> +#include<sys/stat.h>
> +#include<sys/mman.h>
> +#include "libv4l2.h"
> +#include "libv4l2-priv.h"
> +#include "libv4l2-plugin.h"
> +
> +/* libv4l plugin support:
> +   it is provided by functions v4l2_plugin_[open,close,etc].
> +
> +   When open() is called libv4l dlopens files in /usr/lib[64]/libv4l/plugins
> +   1 at a time and call open callback passing through the applications
> +   parameters unmodified.
> +
> +   If a plugin is relevant for the specified device node, it can indicate so
> +   by returning a value other then -1 (the actual file descriptor).
> +   As soon as a plugin returns another value then -1 plugin loading stops and
> +   information about it (fd and corresponding library handle) is stored. For
> +   each function v4l2_[ioctl,read,close,etc] is called corresponding
> +   v4l2_plugin_* function which looks if there is loaded plugin for that file
> +   and call it's callbacks.
> +
> +   v4l2_plugin_* function indicates by it's first argument if plugin was used,
> +   and if it was not then v4l2_* functions proceed with their usual behavior.
> +*/
> +
> +#define PLUGINS_PATTERN "/usr/lib/libv4l/plugins/*.so"
> +
> +static void * dev_init(int fd)
> +{
> +	return NULL;
> +}
> +
> +static void dev_close(void *dev_ops_priv)
> +{
> +}
> +
> +static int dev_ioctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg)
> +{
> +	return SYS_IOCTL(fd, cmd, arg);
> +}
> +
> +static ssize_t dev_read(void *dev_ops_priv, int fd, void *buf, size_t len)
> +{
> +	return SYS_READ(fd, buf, len);
> +}
> +
> +
> +struct libv4l2_dev_ops default_operations = {
> +	.init =&dev_init,
> +	.close =&dev_close,
> +	.ioctl =&dev_ioctl,
> +	.read =&dev_read
> +};
> +
> +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret,
> +					struct libv4l2_dev_ops **dev_ops_ret)
> +{
> +	char *error;
> +	int glob_ret, plugin_num;
> +	void *plugin_library = NULL;
> +	struct libv4l2_dev_ops *libv4l2_plugin = NULL;
> +	glob_t globbuf;
> +
> +	*dev_ops_ret =&default_operations;
> +
> +	glob_ret = glob(PLUGINS_PATTERN, 0, NULL,&globbuf);
> +
> +	if (glob_ret == GLOB_NOSPACE) {
> +		*plugin_lib_ret = NULL;
> +		*plugin_priv_ret = NULL;
> +		return;
> +	}
> +
> +	if (glob_ret == GLOB_ABORTED || glob_ret == GLOB_NOMATCH) {
> +		*plugin_lib_ret = NULL;
> +		*plugin_priv_ret = NULL;
> +		goto leave;
> +	}
> +
> +	for (plugin_num = 0; plugin_num<  globbuf.gl_pathc; plugin_num++) {
> +
> +		V4L2_LOG("PLUGIN: dlopen(%s);\n", globbuf.gl_pathv[plugin_num]);
> +
> +		plugin_library = dlopen(globbuf.gl_pathv[plugin_num],
> +							RTLD_LAZY);
> +
> +		if (!plugin_library)
> +			continue;
> +
> +		dlerror();    /* Clear any existing error */
> +		libv4l2_plugin = (struct libv4l2_dev_ops *)
> +					dlsym(plugin_library, "libv4l2_plugin");
> +
> +		error = dlerror();
> +		if (error != NULL)  {
> +			V4L2_LOG_ERR("PLUGIN: dlsym failed: %s\n", error);
> +			dlclose(plugin_library);
> +
> +			continue;
> +		}
> +
> +		*plugin_priv_ret = NULL;
> +		if (libv4l2_plugin->init)
> +			*plugin_priv_ret = libv4l2_plugin->init(fd);
> +
> +		if (!*plugin_priv_ret) {
> +			V4L2_LOG("PLUGIN: plugin open() returned NULL\n");
> +			dlclose(plugin_library);
> +			*plugin_lib_ret = NULL;
> +			continue;
> +		}
> +
> +		(*dev_ops_ret)->init = libv4l2_plugin->init;
> +		if (libv4l2_plugin->close != NULL)
> +			(*dev_ops_ret)->close = libv4l2_plugin->close;
> +		if (libv4l2_plugin->ioctl != NULL)
> +			(*dev_ops_ret)->ioctl = libv4l2_plugin->ioctl;
> +		if (libv4l2_plugin->read != NULL)
> +			(*dev_ops_ret)->read = libv4l2_plugin->read;
> +
> +		break;
> +
> +	}
> +
> +leave:
> +	globfree(&globbuf);
> +}
> +
> +void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
> +					struct libv4l2_dev_ops *dev_ops)
> +{
> +	if (plugin_priv) {
> +		dev_ops->close(plugin_priv);
> +		dlclose(plugin_lib);
> +	}
> +}
> diff --git a/lib/libv4l2/v4l2convert.c b/lib/libv4l2/v4l2convert.c
> index e251085..03f34ad 100644
> --- a/lib/libv4l2/v4l2convert.c
> +++ b/lib/libv4l2/v4l2convert.c
> @@ -46,7 +46,6 @@
>   LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
>   {
>   	int fd;
> -	struct v4l2_capability cap;
>   	int v4l_device = 0;
>
>   	/* check if we're opening a video4linux2 device */
> @@ -76,14 +75,6 @@ LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
>   	if (fd == -1 || !v4l_device)
>   		return fd;
>
> -	/* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */
> -	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap))
> -		return fd;
> -
> -	/* libv4l2 only adds functionality to capture capable devices */
> -	if (!(cap.capabilities&  V4L2_CAP_VIDEO_CAPTURE))
> -		return fd;
> -
>   	/* Try to Register with libv4l2 (in case of failure pass the fd to the
>   	   application as is) */
>   	v4l2_fd_open(fd, 0);
> diff --git a/lib/libv4lconvert/control/libv4lcontrol-priv.h b/lib/libv4lconvert/control/libv4lcontrol-priv.h
> index 22cdf34..1259e6b 100644
> --- a/lib/libv4lconvert/control/libv4lcontrol-priv.h
> +++ b/lib/libv4lconvert/control/libv4lcontrol-priv.h
> @@ -22,6 +22,8 @@
>   #ifndef __LIBV4LCONTROL_PRIV_H
>   #define __LIBV4LCONTROL_PRIV_H
>
> +#include "libv4l2-plugin.h"
> +
>   #define V4LCONTROL_SHM_SIZE 4096
>
>   #define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01
> @@ -37,6 +39,8 @@ struct v4lcontrol_data {
>   	unsigned int *shm_values; /* shared memory control value store */
>   	unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */
>   	const struct v4lcontrol_flags_info *flags_info;
> +	struct libv4l2_dev_ops *dev_ops;
> +	void *dev_ops_priv;
>   };
>
>   struct v4lcontrol_flags_info {
> diff --git a/lib/libv4lconvert/control/libv4lcontrol.c b/lib/libv4lconvert/control/libv4lcontrol.c
> index 116bef5..24a5600 100644
> --- a/lib/libv4lconvert/control/libv4lcontrol.c
> +++ b/lib/libv4lconvert/control/libv4lcontrol.c
> @@ -747,7 +747,8 @@ static void v4lcontrol_get_flags_from_db(struct v4lcontrol_data *data,
>   		}
>   }
>
> -struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
> +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
> +		struct libv4l2_dev_ops *dev_ops, int always_needs_conversion)
>   {
>   	int shm_fd;
>   	int i, rc, got_usb_ids, init = 0;
> @@ -767,10 +768,12 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	}
>
>   	data->fd = fd;
> +	data->dev_ops = dev_ops;
> +	data->dev_ops_priv = dev_ops_priv;
>
>   	/* Check if the driver has indicated some form of flipping is needed */
> -	if ((SYS_IOCTL(data->fd, VIDIOC_G_INPUT,&input.index) == 0)&&
> -			(SYS_IOCTL(data->fd, VIDIOC_ENUMINPUT,&input) == 0)) {
> +	if ((data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_G_INPUT,&input.index) == 0)&&
> +			(data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_ENUMINPUT,&input) == 0)) {
>   		if (input.status&  V4L2_IN_ST_HFLIP)
>   			data->flags |= V4LCONTROL_HFLIPPED;
>   		if (input.status&  V4L2_IN_ST_VFLIP)
> @@ -787,7 +790,8 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   		data->flags = strtol(s, NULL, 0);
>
>   	ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
> -	if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl) == 0)
> +	if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_QUERYCTRL,&ctrl) == 0)
>   		data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL;
>
>   	/* If the device always needs conversion, we can add fake controls at no cost
> @@ -795,7 +799,8 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	if (always_needs_conversion || v4lcontrol_needs_conversion(data)) {
>   		for (i = 0; i<  V4LCONTROL_AUTO_ENABLE_COUNT; i++) {
>   			ctrl.id = fake_controls[i].id;
> -			rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +			rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +					VIDIOC_QUERYCTRL,&ctrl);
>   			if (rc == -1 || (rc == 0&&  (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED)))
>   				data->controls |= 1<<  i;
>   		}
> @@ -807,17 +812,20 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	   different sensors with / without autogain or the necessary controls. */
>   	while (data->flags&  V4LCONTROL_WANTS_AUTOGAIN) {
>   		ctrl.id = V4L2_CID_AUTOGAIN;
> -		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_QUERYCTRL,&ctrl);
>   		if (rc == 0&&  !(ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>   			break;
>
>   		ctrl.id = V4L2_CID_EXPOSURE;
> -		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_QUERYCTRL,&ctrl);
>   		if (rc != 0 || (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>   			break;
>
>   		ctrl.id = V4L2_CID_GAIN;
> -		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
> +		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_QUERYCTRL,&ctrl);
>   		if (rc != 0 || (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>   			break;
>
> @@ -834,7 +842,7 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
>   	if (data->controls == 0)
>   		return data; /* No need to create a shared memory segment */
>
> -	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap)) {
> +	if (data->dev_ops->ioctl(data->dev_ops_priv, fd, VIDIOC_QUERYCAP,&cap)) {
>   		perror("libv4lcontrol: error querying device capabilities");
>   		goto error;
>   	}
> @@ -1020,7 +1028,8 @@ int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
>   		}
>
>   	/* find out what the kernel driver would respond. */
> -	retval = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, arg);
> +	retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_QUERYCTRL, arg);
>
>   	if ((data->priv_flags&  V4LCONTROL_SUPPORTS_NEXT_CTRL)&&
>   			(orig_id&  V4L2_CTRL_FLAG_NEXT_CTRL)) {
> @@ -1057,7 +1066,8 @@ int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg)
>   			return 0;
>   		}
>
> -	return SYS_IOCTL(data->fd, VIDIOC_G_CTRL, arg);
> +	return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_G_CTRL, arg);
>   }
>
>   int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
> @@ -1078,7 +1088,8 @@ int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
>   			return 0;
>   		}
>
> -	return SYS_IOCTL(data->fd, VIDIOC_S_CTRL, arg);
> +	return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_S_CTRL, arg);
>   }
>
>   int v4lcontrol_get_flags(struct v4lcontrol_data *data)
> diff --git a/lib/libv4lconvert/control/libv4lcontrol.h b/lib/libv4lconvert/control/libv4lcontrol.h
> index 974e97a..1c09546 100644
> --- a/lib/libv4lconvert/control/libv4lcontrol.h
> +++ b/lib/libv4lconvert/control/libv4lcontrol.h
> @@ -22,6 +22,8 @@
>   #ifndef __LIBV4LCONTROL_H
>   #define __LIBV4LCONTROL_H
>
> +#include "libv4l2-plugin.h"
> +
>   /* Flags */
>   #define V4LCONTROL_HFLIPPED              0x01
>   #define V4LCONTROL_VFLIPPED              0x02
> @@ -47,7 +49,8 @@ enum {
>
>   struct v4lcontrol_data;
>
> -struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion);
> +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
> +		struct libv4l2_dev_ops *dev_ops, int always_needs_conversion);
>   void v4lcontrol_destroy(struct v4lcontrol_data *data);
>
>   /* Functions used by v4lprocessing to get the control state */
> diff --git a/lib/libv4lconvert/libv4lconvert-priv.h b/lib/libv4lconvert/libv4lconvert-priv.h
> index 84c706e..9cb6e1c 100644
> --- a/lib/libv4lconvert/libv4lconvert-priv.h
> +++ b/lib/libv4lconvert/libv4lconvert-priv.h
> @@ -65,6 +65,8 @@ struct v4lconvert_data {
>   	unsigned char *convert_pixfmt_buf;
>   	struct v4lcontrol_data *control;
>   	struct v4lprocessing_data *processing;
> +	struct libv4l2_dev_ops *dev_ops;
> +	void *dev_ops_priv;
>
>   	/* Data for external decompression helpers code */
>   	pid_t decompress_pid;
> diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c
> index e4863fd..6861986 100644
> --- a/lib/libv4lconvert/libv4lconvert.c
> +++ b/lib/libv4lconvert/libv4lconvert.c
> @@ -92,7 +92,8 @@ static const int v4lconvert_crop_res[][2] = {
>   	{ 176, 144 },
>   };
>
> -struct v4lconvert_data *v4lconvert_create(int fd)
> +struct v4lconvert_data *v4lconvert_create(int fd, void *dev_ops_priv,
> +		struct libv4l2_dev_ops *dev_ops)
>   {
>   	int i, j;
>   	struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data));
> @@ -108,6 +109,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>   	}
>
>   	data->fd = fd;
> +	data->dev_ops = dev_ops;
> +	data->dev_ops_priv = dev_ops_priv;
>   	data->decompress_pid = -1;
>
>   	/* Check supported formats */
> @@ -116,7 +119,7 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>
>   		fmt.index = i;
>
> -		if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT,&fmt))
> +		if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_ENUM_FMT,&fmt))
>   			break;
>
>   		for (j = 0; j<  ARRAY_SIZE(supported_src_pixfmts); j++)
> @@ -135,7 +138,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>   	data->no_formats = i;
>
>   	/* Check if this cam has any special flags */
> -	if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP,&cap) == 0) {
> +	if (data->dev_ops->ioctl(data->dev_ops_priv,
> +					data->fd, VIDIOC_QUERYCAP,&cap) == 0) {
>   		if (!strcmp((char *)cap.driver, "uvcvideo"))
>   			data->flags |= V4LCONVERT_IS_UVC;
>
> @@ -143,7 +147,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>   			always_needs_conversion = 0;
>   	}
>
> -	data->control = v4lcontrol_create(fd, always_needs_conversion);
> +	data->control = v4lcontrol_create(fd, dev_ops_priv, dev_ops,
> +						always_needs_conversion);
>   	if (!data->control) {
>   		free(data);
>   		return NULL;
> @@ -206,7 +211,8 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
>   	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>   			(!v4lconvert_supported_dst_fmt_only(data)&&
>   			fmt->index<  data->no_formats))
> -		return SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, fmt);
> +		return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +						VIDIOC_ENUM_FMT, fmt);
>
>   	for (i = 0; i<  ARRAY_SIZE(supported_dst_pixfmts); i++)
>   		if (v4lconvert_supported_dst_fmt_only(data) ||
> @@ -314,7 +320,8 @@ static int v4lconvert_do_try_format(struct v4lconvert_data *data,
>   		try_fmt = *dest_fmt;
>   		try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;
>
> -		if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT,&try_fmt)) {
> +		if (!data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +						VIDIOC_TRY_FMT,&try_fmt)) {
>   			if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) {
>   				int size_x_diff = abs((int)try_fmt.fmt.pix.width -
>   						(int)dest_fmt->fmt.pix.width);
> @@ -382,7 +389,8 @@ int v4lconvert_try_format(struct v4lconvert_data *data,
>   	if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
>   			dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>   			v4lconvert_do_try_format(data,&try_dest,&try_src)) {
> -		result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt);
> +		result = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +							VIDIOC_TRY_FMT, dest_fmt);
>   		if (src_fmt)
>   			*src_fmt = *dest_fmt;
>   		return result;
> @@ -1241,7 +1249,8 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
>
>   	for (i = 0; ; i++) {
>   		frmsize.index = i;
> -		if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES,&frmsize))
> +		if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +					VIDIOC_ENUM_FRAMESIZES,&frmsize))
>   			break;
>
>   		/* We got a framesize, check we don't have the same one already */
> @@ -1301,7 +1310,8 @@ int v4lconvert_enum_framesizes(struct v4lconvert_data *data,
>   			errno = EINVAL;
>   			return -1;
>   		}
> -		return SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize);
> +		return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +						VIDIOC_ENUM_FRAMESIZES, frmsize);
>   	}
>
>   	if (frmsize->index>= data->no_framesizes) {
> @@ -1337,7 +1347,8 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
>   			errno = EINVAL;
>   			return -1;
>   		}
> -		res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
> +		res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +				VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>   		if (res)
>   			V4LCONVERT_ERR("%s\n", strerror(errno));
>   		return res;
> @@ -1382,7 +1393,8 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
>   	frmival->pixel_format = src_fmt.fmt.pix.pixelformat;
>   	frmival->width = src_fmt.fmt.pix.width;
>   	frmival->height = src_fmt.fmt.pix.height;
> -	res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
> +	res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
> +			VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>   	if (res) {
>   		int dest_pixfmt = dest_fmt.fmt.pix.pixelformat;
>   		int src_pixfmt  = src_fmt.fmt.pix.pixelformat;
> diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
> index 232a318..349d1ca 100644
> --- a/utils/qv4l2/qv4l2.cpp
> +++ b/utils/qv4l2/qv4l2.cpp
> @@ -47,6 +47,17 @@
>   #include<sys/mman.h>
>   #include<errno.h>
>   #include<dirent.h>
> +#include<syscall.h>
> +
> +int defIoctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg)
> +{
> +	return syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd),
> +					(void *)(arg));
> +}
> +ssize_t defRead(void *dev_ops_priv, int fd, void *buf, size_t len)
> +{
> +	return syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len));
> +}
>
>   ApplicationWindow::ApplicationWindow() :
>   	m_capture(NULL),
> @@ -59,6 +70,10 @@ ApplicationWindow::ApplicationWindow() :
>   	m_frameData = NULL;
>   	m_nbuffers = 0;
>   	m_buffers = NULL;
> +	m_defauldDevOps.init = NULL;
> +	m_defauldDevOps.close = NULL;
> +	m_defauldDevOps.ioctl =&defIoctl;
> +	m_defauldDevOps.read =&defRead;
>
>   	QAction *openAct = new QAction(QIcon(":/fileopen.png"), "&Open device", this);
>   	openAct->setStatusTip("Open a v4l device, use libv4l2 wrapper if possible");
> @@ -144,7 +159,7 @@ void ApplicationWindow::setDevice(const QString&device, bool rawOpen)
>   	}
>   	m_tabs->show();
>   	m_tabs->setFocus();
> -	m_convertData = v4lconvert_create(fd());
> +	m_convertData = v4lconvert_create(fd(), NULL,&m_defauldDevOps);
>   	m_capStartAct->setEnabled(fd()>= 0);
>   }
>
> diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
> index 0a3e5ef..d847fe6 100644
> --- a/utils/qv4l2/qv4l2.h
> +++ b/utils/qv4l2/qv4l2.h
> @@ -91,6 +91,7 @@ private:
>   	unsigned char *m_frameData;
>   	unsigned m_nbuffers;
>   	struct v4lconvert_data *m_convertData;
> +	struct libv4l2_dev_ops m_defauldDevOps;
>   	CapMethod m_capMethod;
>
>   private slots:
--
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
Yordan Kamenov July 22, 2011, 3:39 p.m. UTC | #3
Hi Hans,

Hans de Goede wrote:
> Hi,
>
> Sorry it took so long, but I've just merged the plugin
> support into v4l-utils git. I did make some minor mods /
> bugfixes before merging, see the commit message in git.
>
> Regards,
>
> Hans
>
> p.s.
>
> I think we should expand the plugin support with support
> for a output devices, iow add a write() dev_op. If you
> guys agree I can easily do so myself, we should do this
> asap before people start depending on the ABI
> (although there is no ABI stability promise until I
> release 0.10.x, see my message to the list wrt
> the start of the 0.9.x cycle).
>

I think that it is a good point, you can add write() and
reserved dev_ops.

Thanks
Yordan

> While on the subject of keeping the plugin ABI, I suggest
> that we add a number of extra dev_ops named reserved1 --
> reserved8 for future use and a comment that these should
> all be set to NULL. Then we can easily add another op
> on the future while keeping compatibility with older
> plugins.
>
>
> On 05/03/2011 05:26 PM, Yordan Kamenov wrote:
>> A libv4l2 plugin will sit in between libv4l2 itself and the actual
>> /dev/video device node a fd refers to. It will be called each time
>> libv4l2 wants to do an operation (read/write/ioctl) on the actual
>> /dev/video node in question.
>>
>> Signed-off-by: Yordan Kamenov<ykamenov@mm-sol.com>
>> ---
>>   lib/include/libv4l2-plugin.h                   |   36 ++++++
>>   lib/include/libv4lconvert.h                    |    5 +-
>>   lib/libv4l2/Makefile                           |    6 +-
>>   lib/libv4l2/libv4l2-priv.h                     |   10 ++
>>   lib/libv4l2/libv4l2.c                          |   88 ++++++++++----
>>   lib/libv4l2/v4l2-plugin.c                      |  158 
>> ++++++++++++++++++++++++
>>   lib/libv4l2/v4l2convert.c                      |    9 --
>>   lib/libv4lconvert/control/libv4lcontrol-priv.h |    4 +
>>   lib/libv4lconvert/control/libv4lcontrol.c      |   35 ++++--
>>   lib/libv4lconvert/control/libv4lcontrol.h      |    5 +-
>>   lib/libv4lconvert/libv4lconvert-priv.h         |    2 +
>>   lib/libv4lconvert/libv4lconvert.c              |   34 ++++--
>>   utils/qv4l2/qv4l2.cpp                          |   17 +++-
>>   utils/qv4l2/qv4l2.h                            |    1 +
>>   14 files changed, 347 insertions(+), 63 deletions(-)
>>   create mode 100644 lib/include/libv4l2-plugin.h
>>   create mode 100644 lib/libv4l2/v4l2-plugin.c
>>
>> diff --git a/lib/include/libv4l2-plugin.h b/lib/include/libv4l2-plugin.h
>> new file mode 100644
>> index 0000000..158c0c2
>> --- /dev/null
>> +++ b/lib/include/libv4l2-plugin.h
>> @@ -0,0 +1,36 @@
>> +/*
>> +* Copyright (C) 2010 Nokia Corporation<multimedia@maemo.org>
>> +
>> +* This program is free software; you can redistribute it and/or modify
>> +* it under the terms of the GNU Lesser General Public License as 
>> published by
>> +* the Free Software Foundation; either version 2.1 of the License, or
>> +* (at your option) any later version.
>> +*
>> +* This program is distributed in the hope that it will be useful,
>> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +* Lesser General Public License for more details.
>> +*
>> +* You should have received a copy of the GNU Lesser General Public 
>> License
>> +* along with this program; if not, write to the Free Software
>> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  
>> 02111-1307  USA
>> +*/
>> +
>> +#ifndef __LIBV4L2_PLUGIN_H
>> +#define __LIBV4L2_PLUGIN_H
>> +
>> +#include<sys/types.h>
>> +
>> +/* Structure libv4l2_dev_ops holds the calls from libv4ls to video 
>> nodes.
>> +   They can be normal open/close/ioctl etc. or any of them may be 
>> replaced
>> +   with a callback by a loaded plugin.
>> +*/
>> +
>> +struct libv4l2_dev_ops {
>> +    void * (*init)(int fd);
>> +    void (*close)(void *dev_ops_priv);
>> +    int (*ioctl)(void *dev_ops_priv, int fd, unsigned long int 
>> request, void *arg);
>> +    ssize_t (*read)(void *dev_ops_priv, int fd, void *buffer, size_t 
>> n);
>> +};
>> +
>> +#endif
>> diff --git a/lib/include/libv4lconvert.h b/lib/include/libv4lconvert.h
>> index 0264290..351142e 100644
>> --- a/lib/include/libv4lconvert.h
>> +++ b/lib/include/libv4lconvert.h
>> @@ -38,6 +38,8 @@
>>
>>   #include<linux/videodev2.h>
>>
>> +#include "libv4l2-plugin.h"
>> +
>>   #ifdef __cplusplus
>>   extern "C" {
>>   #endif /* __cplusplus */
>> @@ -50,7 +52,8 @@ extern "C" {
>>
>>   struct v4lconvert_data;
>>
>> -LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd);
>> +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd,
>> +        void *dev_ops_priv, struct libv4l2_dev_ops *dev_ops);
>>   LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data);
>>
>>   /* When doing flipping / rotating / video-processing, only supported
>> diff --git a/lib/libv4l2/Makefile b/lib/libv4l2/Makefile
>> index d78632f..f8b3714 100644
>> --- a/lib/libv4l2/Makefile
>> +++ b/lib/libv4l2/Makefile
>> @@ -1,12 +1,12 @@
>>   override CPPFLAGS += -I../include -fvisibility=hidden
>>
>> -LIBS_libv4l2  = -lpthread
>> +LIBS_libv4l2  = -lpthread -ldl
>>
>> -V4L2_OBJS     = libv4l2.o log.o
>> +V4L2_OBJS     = libv4l2.o v4l2-plugin.o log.o
>>   V4L2CONVERT   = v4l2convert.so
>>   V4L2CONVERT_O = v4l2convert.o libv4l2.so
>>   TARGETS       = $(V4L2_LIB) libv4l2.pc
>> -INCLUDES      = ../include/libv4l2.h
>> +INCLUDES      = ../include/libv4l2.h ../include/libv4l2-plugin.h
>>
>>   ifeq ($(LINKTYPE),static)
>>   V4L2_LIB      = libv4l2.a
>> diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
>> index 46d6103..d06a508 100644
>> --- a/lib/libv4l2/libv4l2-priv.h
>> +++ b/lib/libv4l2/libv4l2-priv.h
>> @@ -85,8 +85,18 @@ struct v4l2_dev_info {
>>       /* buffer when doing conversion and using read() for read() */
>>       int readbuf_size;
>>       unsigned char *readbuf;
>> +    struct libv4l2_dev_ops *dev_ops;
>> +    /* plugin info */
>> +    void *plugin_library;
>> +    void *dev_ops_priv;
>>   };
>>
>> +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void 
>> **plugin_priv_ret,
>> +                    struct libv4l2_dev_ops **dev_ops_ret);
>> +void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
>> +                    struct libv4l2_dev_ops *dev_ops);
>> +
>> +
>>   /* From log.c */
>>   void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
>>
>> diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
>> index ab85ea7..018350e 100644
>> --- a/lib/libv4l2/libv4l2.c
>> +++ b/lib/libv4l2/libv4l2.c
>> @@ -67,6 +67,7 @@
>>   #include<sys/stat.h>
>>   #include "libv4l2.h"
>>   #include "libv4l2-priv.h"
>> +#include "libv4l2-plugin.h"
>>
>>   /* Note these flags are stored together with the flags passed to 
>> v4l2_fd_open()
>>      in v4l2_dev_info's flags member, so care should be taken that 
>> the do not
>> @@ -102,7 +103,8 @@ static int v4l2_request_read_buffers(int index)
>>           devices[index].nreadbuffers;
>>       req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>       req.memory = V4L2_MEMORY_MMAP;
>> -    result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS,&req);
>> +    result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +            devices[index].fd, VIDIOC_REQBUFS,&req);
>>       if (result<  0) {
>>           int saved_err = errno;
>>
>> @@ -131,7 +133,8 @@ static void v4l2_unrequest_read_buffers(int index)
>>       req.count = 0;
>>       req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>       req.memory = V4L2_MEMORY_MMAP;
>> -    if (SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS,&req)<  0)
>> +    if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +            devices[index].fd, VIDIOC_REQBUFS,&req)<  0)
>>           return;
>>
>>       devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES);
>> @@ -152,7 +155,8 @@ static int v4l2_map_buffers(int index)
>>           buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>           buf.memory = V4L2_MEMORY_MMAP;
>>           buf.index = i;
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF,&buf);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_QUERYBUF,&buf);
>>           if (result) {
>>               int saved_err = errno;
>>
>> @@ -202,7 +206,8 @@ static int v4l2_streamon(int index)
>>       enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>
>>       if (!(devices[index].flags&  V4L2_STREAMON)) {
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMON,&type);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_STREAMON,&type);
>>           if (result) {
>>               int saved_err = errno;
>>
>> @@ -223,7 +228,8 @@ static int v4l2_streamoff(int index)
>>       enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>
>>       if (devices[index].flags&  V4L2_STREAMON) {
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMOFF,&type);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_STREAMOFF,&type);
>>           if (result) {
>>               int saved_err = errno;
>>
>> @@ -252,7 +258,8 @@ static int v4l2_queue_read_buffer(int index, int 
>> buffer_index)
>>       buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>       buf.memory = V4L2_MEMORY_MMAP;
>>       buf.index  = buffer_index;
>> -    result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF,&buf);
>> +    result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +            devices[index].fd, VIDIOC_QBUF,&buf);
>>       if (result) {
>>           int saved_err = errno;
>>
>> @@ -277,7 +284,8 @@ static int v4l2_dequeue_and_convert(int index, 
>> struct v4l2_buffer *buf,
>>           return result;
>>
>>       do {
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_DQBUF, buf);
>>           if (result) {
>>               if (errno != EAGAIN) {
>>                   int saved_err = errno;
>> @@ -356,7 +364,8 @@ static int v4l2_read_and_convert(int index, 
>> unsigned char *dest, int dest_size)
>>       }
>>
>>       do {
>> -        result = SYS_READ(devices[index].fd, devices[index].readbuf, 
>> buf_size);
>> +        result = 
>> devices[index].dev_ops->read(devices[index].dev_ops_priv,
>> +                devices[index].fd, devices[index].readbuf, buf_size);
>>           if (result<= 0) {
>>               if (result&&  errno != EAGAIN) {
>>                   int saved_err = errno;
>> @@ -496,7 +505,8 @@ static int v4l2_buffers_mapped(int index)
>>               buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>               buf.memory = V4L2_MEMORY_MMAP;
>>               buf.index = i;
>> -            if (SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF,&buf)) {
>> +            if 
>> (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                    devices[index].fd, VIDIOC_QUERYBUF,&buf)) {
>>                   int saved_err = errno;
>>
>>                   V4L2_LOG_ERR("querying buffer %u: %s\n", i, 
>> strerror(errno));
>> @@ -561,6 +571,11 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>>       struct v4l2_capability cap;
>>       struct v4l2_format fmt;
>>       struct v4lconvert_data *convert;
>> +    struct libv4l2_dev_ops *dev_ops;
>> +    void *plugin_library;
>> +    void *plugin_data;
>> +
>> +    v4l2_plugin_init(fd,&plugin_library,&plugin_data,&dev_ops);
>>
>>       /* If no log file was set by the app, see if one was specified 
>> through the
>>          environment */
>> @@ -571,9 +586,10 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>>       }
>>
>>       /* check that this is an v4l2 device */
>> -    if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap)) {
>> +    if (dev_ops->ioctl(plugin_data, fd, VIDIOC_QUERYCAP,&cap)) {
>>           int saved_err = errno;
>>
>> +        v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>>           V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
>>           errno = saved_err;
>>           return -1;
>> @@ -581,29 +597,38 @@ 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)))
>> +            !(cap.capabilities&  (V4L2_CAP_STREAMING | 
>> V4L2_CAP_READWRITE))) {
>> +        v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>>           return fd;
>> +    }
>>
>>       /* Get current cam format */
>>       fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> -    if (SYS_IOCTL(fd, VIDIOC_G_FMT,&fmt)) {
>> +    if (dev_ops->ioctl(plugin_data, fd, VIDIOC_G_FMT,&fmt)) {
>>           int saved_err = errno;
>>
>> +        v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>>           V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
>>           errno = saved_err;
>>           return -1;
>>       }
>>
>>       /* init libv4lconvert */
>> -    convert = v4lconvert_create(fd);
>> -    if (!convert)
>> +    convert = v4lconvert_create(fd, plugin_data, dev_ops);
>> +    if (!convert) {
>> +        v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>>           return -1;
>> +    }
>>
>>       /* 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;
>> +
>> +            devices[index].plugin_library = plugin_library;
>> +            devices[index].dev_ops_priv = plugin_data;
>> +            devices[index].dev_ops = dev_ops;
>>               break;
>>           }
>>       pthread_mutex_unlock(&v4l2_open_mutex);
>> @@ -611,6 +636,7 @@ int v4l2_fd_open(int fd, int v4l2_flags)
>>       if (index == V4L2_MAX_DEVICES) {
>>           V4L2_LOG_ERR("attempting to open more then %d video 
>> devices\n",
>>                   V4L2_MAX_DEVICES);
>> +        v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
>>           errno = EBUSY;
>>           return -1;
>>       }
>> @@ -700,6 +726,9 @@ int v4l2_close(int fd)
>>       if (result)
>>           return 0;
>>
>> +    v4l2_plugin_cleanup(devices[index].dev_ops_priv,
>> +            devices[index].plugin_library, devices[index].dev_ops);
>> +
>>       /* Free resources */
>>       v4l2_unmap_buffers(index);
>>       if (devices[index].convert_mmap_buf != MAP_FAILED) {
>> @@ -879,7 +908,8 @@ int v4l2_ioctl(int fd, unsigned long int request, 
>> ...)
>>       }
>>
>>       if (!is_capture_request) {
>> -        result = SYS_IOCTL(fd, request, arg);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                fd, request, arg);
>>           saved_err = errno;
>>           v4l2_log_ioctl(request, arg, result);
>>           errno = saved_err;
>> @@ -927,7 +957,8 @@ int v4l2_ioctl(int fd, unsigned long int request, 
>> ...)
>>       case VIDIOC_QUERYCAP: {
>>           struct v4l2_capability *cap = arg;
>>
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYCAP, cap);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_QUERYCAP, cap);
>>           if (result == 0)
>>               /* We always support read() as we fake it using mmap 
>> mode */
>>               cap->capabilities |= V4L2_CAP_READWRITE;
>> @@ -977,8 +1008,8 @@ int v4l2_ioctl(int fd, unsigned long int 
>> request, ...)
>>           }
>>
>>           if (devices[index].flags&  V4L2_DISABLE_CONVERSION) {
>> -            result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT,
>> -                    dest_fmt);
>> +            result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                    devices[index].fd, VIDIOC_TRY_FMT, dest_fmt);
>>               src_fmt = *dest_fmt;
>>           } else {
>>               result = v4lconvert_try_format(devices[index].convert, 
>> dest_fmt,
>> @@ -1018,7 +1049,8 @@ int v4l2_ioctl(int fd, unsigned long int 
>> request, ...)
>>               break;
>>
>>           req_pix_fmt = src_fmt.fmt.pix;
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT,&src_fmt);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_S_FMT,&src_fmt);
>>           if (result) {
>>               saved_err = errno;
>>               V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno));
>> @@ -1067,7 +1099,8 @@ int v4l2_ioctl(int fd, unsigned long int 
>> request, ...)
>>           if (req->count>  V4L2_MAX_NO_FRAMES)
>>               req->count = V4L2_MAX_NO_FRAMES;
>>
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, req);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_REQBUFS, req);
>>           if (result<  0)
>>               break;
>>           result = 0; /* some drivers return the number of buffers on 
>> success */
>> @@ -1088,7 +1121,8 @@ int v4l2_ioctl(int fd, unsigned long int 
>> request, ...)
>>
>>           /* Do a real query even when converting to let the driver 
>> fill in
>>              things like buf->field */
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, buf);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_QUERYBUF, buf);
>>           if (result || !v4l2_needs_conversion(index))
>>               break;
>>
>> @@ -1115,7 +1149,8 @@ int v4l2_ioctl(int fd, unsigned long int 
>> request, ...)
>>                   break;
>>           }
>>
>> -        result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, arg);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                devices[index].fd, VIDIOC_QBUF, arg);
>>           break;
>>
>>       case VIDIOC_DQBUF: {
>> @@ -1128,7 +1163,8 @@ int v4l2_ioctl(int fd, unsigned long int 
>> request, ...)
>>           }
>>
>>           if (!v4l2_needs_conversion(index)) {
>> -            result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
>> +            result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                    devices[index].fd, VIDIOC_DQBUF, buf);
>>               if (result) {
>>                   int saved_err = errno;
>>
>> @@ -1187,7 +1223,8 @@ int v4l2_ioctl(int fd, unsigned long int 
>> request, ...)
>>           break;
>>
>>       default:
>> -        result = SYS_IOCTL(fd, request, arg);
>> +        result = 
>> devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
>> +                fd, request, arg);
>>           break;
>>       }
>>
>> @@ -1217,7 +1254,8 @@ ssize_t v4l2_read(int fd, void *dest, size_t n)
>>          it */
>>       if ((devices[index].flags&  V4L2_SUPPORTS_READ)&&
>>               !v4l2_needs_conversion(index)) {
>> -        result = SYS_READ(fd, dest, n);
>> +        result = 
>> devices[index].dev_ops->read(devices[index].dev_ops_priv,
>> +                fd, dest, n);
>>           goto leave;
>>       }
>>
>> diff --git a/lib/libv4l2/v4l2-plugin.c b/lib/libv4l2/v4l2-plugin.c
>> new file mode 100644
>> index 0000000..3735193
>> --- /dev/null
>> +++ b/lib/libv4l2/v4l2-plugin.c
>> @@ -0,0 +1,158 @@
>> +/*
>> +* Copyright (C) 2010 Nokia Corporation<multimedia@maemo.org>
>> +
>> +* This program is free software; you can redistribute it and/or modify
>> +* it under the terms of the GNU Lesser General Public License as 
>> published by
>> +* the Free Software Foundation; either version 2.1 of the License, or
>> +* (at your option) any later version.
>> +*
>> +* This program is distributed in the hope that it will be useful,
>> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +* Lesser General Public License for more details.
>> +*
>> +* You should have received a copy of the GNU Lesser General Public 
>> License
>> +* along with this program; if not, write to the Free Software
>> +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  
>> 02111-1307  USA
>> +*/
>> +
>> +#include<stdarg.h>
>> +#include<dlfcn.h>
>> +#include<fcntl.h>
>> +#include<glob.h>
>> +#include<sys/stat.h>
>> +#include<sys/mman.h>
>> +#include "libv4l2.h"
>> +#include "libv4l2-priv.h"
>> +#include "libv4l2-plugin.h"
>> +
>> +/* libv4l plugin support:
>> +   it is provided by functions v4l2_plugin_[open,close,etc].
>> +
>> +   When open() is called libv4l dlopens files in 
>> /usr/lib[64]/libv4l/plugins
>> +   1 at a time and call open callback passing through the applications
>> +   parameters unmodified.
>> +
>> +   If a plugin is relevant for the specified device node, it can 
>> indicate so
>> +   by returning a value other then -1 (the actual file descriptor).
>> +   As soon as a plugin returns another value then -1 plugin loading 
>> stops and
>> +   information about it (fd and corresponding library handle) is 
>> stored. For
>> +   each function v4l2_[ioctl,read,close,etc] is called corresponding
>> +   v4l2_plugin_* function which looks if there is loaded plugin for 
>> that file
>> +   and call it's callbacks.
>> +
>> +   v4l2_plugin_* function indicates by it's first argument if plugin 
>> was used,
>> +   and if it was not then v4l2_* functions proceed with their usual 
>> behavior.
>> +*/
>> +
>> +#define PLUGINS_PATTERN "/usr/lib/libv4l/plugins/*.so"
>> +
>> +static void * dev_init(int fd)
>> +{
>> +    return NULL;
>> +}
>> +
>> +static void dev_close(void *dev_ops_priv)
>> +{
>> +}
>> +
>> +static int dev_ioctl(void *dev_ops_priv, int fd, unsigned long cmd, 
>> void *arg)
>> +{
>> +    return SYS_IOCTL(fd, cmd, arg);
>> +}
>> +
>> +static ssize_t dev_read(void *dev_ops_priv, int fd, void *buf, 
>> size_t len)
>> +{
>> +    return SYS_READ(fd, buf, len);
>> +}
>> +
>> +
>> +struct libv4l2_dev_ops default_operations = {
>> +    .init =&dev_init,
>> +    .close =&dev_close,
>> +    .ioctl =&dev_ioctl,
>> +    .read =&dev_read
>> +};
>> +
>> +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void 
>> **plugin_priv_ret,
>> +                    struct libv4l2_dev_ops **dev_ops_ret)
>> +{
>> +    char *error;
>> +    int glob_ret, plugin_num;
>> +    void *plugin_library = NULL;
>> +    struct libv4l2_dev_ops *libv4l2_plugin = NULL;
>> +    glob_t globbuf;
>> +
>> +    *dev_ops_ret =&default_operations;
>> +
>> +    glob_ret = glob(PLUGINS_PATTERN, 0, NULL,&globbuf);
>> +
>> +    if (glob_ret == GLOB_NOSPACE) {
>> +        *plugin_lib_ret = NULL;
>> +        *plugin_priv_ret = NULL;
>> +        return;
>> +    }
>> +
>> +    if (glob_ret == GLOB_ABORTED || glob_ret == GLOB_NOMATCH) {
>> +        *plugin_lib_ret = NULL;
>> +        *plugin_priv_ret = NULL;
>> +        goto leave;
>> +    }
>> +
>> +    for (plugin_num = 0; plugin_num<  globbuf.gl_pathc; plugin_num++) {
>> +
>> +        V4L2_LOG("PLUGIN: dlopen(%s);\n", 
>> globbuf.gl_pathv[plugin_num]);
>> +
>> +        plugin_library = dlopen(globbuf.gl_pathv[plugin_num],
>> +                            RTLD_LAZY);
>> +
>> +        if (!plugin_library)
>> +            continue;
>> +
>> +        dlerror();    /* Clear any existing error */
>> +        libv4l2_plugin = (struct libv4l2_dev_ops *)
>> +                    dlsym(plugin_library, "libv4l2_plugin");
>> +
>> +        error = dlerror();
>> +        if (error != NULL)  {
>> +            V4L2_LOG_ERR("PLUGIN: dlsym failed: %s\n", error);
>> +            dlclose(plugin_library);
>> +
>> +            continue;
>> +        }
>> +
>> +        *plugin_priv_ret = NULL;
>> +        if (libv4l2_plugin->init)
>> +            *plugin_priv_ret = libv4l2_plugin->init(fd);
>> +
>> +        if (!*plugin_priv_ret) {
>> +            V4L2_LOG("PLUGIN: plugin open() returned NULL\n");
>> +            dlclose(plugin_library);
>> +            *plugin_lib_ret = NULL;
>> +            continue;
>> +        }
>> +
>> +        (*dev_ops_ret)->init = libv4l2_plugin->init;
>> +        if (libv4l2_plugin->close != NULL)
>> +            (*dev_ops_ret)->close = libv4l2_plugin->close;
>> +        if (libv4l2_plugin->ioctl != NULL)
>> +            (*dev_ops_ret)->ioctl = libv4l2_plugin->ioctl;
>> +        if (libv4l2_plugin->read != NULL)
>> +            (*dev_ops_ret)->read = libv4l2_plugin->read;
>> +
>> +        break;
>> +
>> +    }
>> +
>> +leave:
>> +    globfree(&globbuf);
>> +}
>> +
>> +void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
>> +                    struct libv4l2_dev_ops *dev_ops)
>> +{
>> +    if (plugin_priv) {
>> +        dev_ops->close(plugin_priv);
>> +        dlclose(plugin_lib);
>> +    }
>> +}
>> diff --git a/lib/libv4l2/v4l2convert.c b/lib/libv4l2/v4l2convert.c
>> index e251085..03f34ad 100644
>> --- a/lib/libv4l2/v4l2convert.c
>> +++ b/lib/libv4l2/v4l2convert.c
>> @@ -46,7 +46,6 @@
>>   LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
>>   {
>>       int fd;
>> -    struct v4l2_capability cap;
>>       int v4l_device = 0;
>>
>>       /* check if we're opening a video4linux2 device */
>> @@ -76,14 +75,6 @@ LIBV4L_PUBLIC int open(const char *file, int 
>> oflag, ...)
>>       if (fd == -1 || !v4l_device)
>>           return fd;
>>
>> -    /* check that this is an v4l2 device, libv4l2 only supports v4l2 
>> devices */
>> -    if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap))
>> -        return fd;
>> -
>> -    /* libv4l2 only adds functionality to capture capable devices */
>> -    if (!(cap.capabilities&  V4L2_CAP_VIDEO_CAPTURE))
>> -        return fd;
>> -
>>       /* Try to Register with libv4l2 (in case of failure pass the fd 
>> to the
>>          application as is) */
>>       v4l2_fd_open(fd, 0);
>> diff --git a/lib/libv4lconvert/control/libv4lcontrol-priv.h 
>> b/lib/libv4lconvert/control/libv4lcontrol-priv.h
>> index 22cdf34..1259e6b 100644
>> --- a/lib/libv4lconvert/control/libv4lcontrol-priv.h
>> +++ b/lib/libv4lconvert/control/libv4lcontrol-priv.h
>> @@ -22,6 +22,8 @@
>>   #ifndef __LIBV4LCONTROL_PRIV_H
>>   #define __LIBV4LCONTROL_PRIV_H
>>
>> +#include "libv4l2-plugin.h"
>> +
>>   #define V4LCONTROL_SHM_SIZE 4096
>>
>>   #define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01
>> @@ -37,6 +39,8 @@ struct v4lcontrol_data {
>>       unsigned int *shm_values; /* shared memory control value store */
>>       unsigned int old_values[V4LCONTROL_COUNT]; /* for 
>> controls_changed() */
>>       const struct v4lcontrol_flags_info *flags_info;
>> +    struct libv4l2_dev_ops *dev_ops;
>> +    void *dev_ops_priv;
>>   };
>>
>>   struct v4lcontrol_flags_info {
>> diff --git a/lib/libv4lconvert/control/libv4lcontrol.c 
>> b/lib/libv4lconvert/control/libv4lcontrol.c
>> index 116bef5..24a5600 100644
>> --- a/lib/libv4lconvert/control/libv4lcontrol.c
>> +++ b/lib/libv4lconvert/control/libv4lcontrol.c
>> @@ -747,7 +747,8 @@ static void v4lcontrol_get_flags_from_db(struct 
>> v4lcontrol_data *data,
>>           }
>>   }
>>
>> -struct v4lcontrol_data *v4lcontrol_create(int fd, int 
>> always_needs_conversion)
>> +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
>> +        struct libv4l2_dev_ops *dev_ops, int always_needs_conversion)
>>   {
>>       int shm_fd;
>>       int i, rc, got_usb_ids, init = 0;
>> @@ -767,10 +768,12 @@ struct v4lcontrol_data *v4lcontrol_create(int 
>> fd, int always_needs_conversion)
>>       }
>>
>>       data->fd = fd;
>> +    data->dev_ops = dev_ops;
>> +    data->dev_ops_priv = dev_ops_priv;
>>
>>       /* Check if the driver has indicated some form of flipping is 
>> needed */
>> -    if ((SYS_IOCTL(data->fd, VIDIOC_G_INPUT,&input.index) == 0)&&
>> -            (SYS_IOCTL(data->fd, VIDIOC_ENUMINPUT,&input) == 0)) {
>> +    if ((data->dev_ops->ioctl(data->dev_ops_priv, data->fd, 
>> VIDIOC_G_INPUT,&input.index) == 0)&&
>> +            (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, 
>> VIDIOC_ENUMINPUT,&input) == 0)) {
>>           if (input.status&  V4L2_IN_ST_HFLIP)
>>               data->flags |= V4LCONTROL_HFLIPPED;
>>           if (input.status&  V4L2_IN_ST_VFLIP)
>> @@ -787,7 +790,8 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, 
>> int always_needs_conversion)
>>           data->flags = strtol(s, NULL, 0);
>>
>>       ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
>> -    if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl) == 0)
>> +    if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +            VIDIOC_QUERYCTRL,&ctrl) == 0)
>>           data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL;
>>
>>       /* If the device always needs conversion, we can add fake 
>> controls at no cost
>> @@ -795,7 +799,8 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, 
>> int always_needs_conversion)
>>       if (always_needs_conversion || 
>> v4lcontrol_needs_conversion(data)) {
>>           for (i = 0; i<  V4LCONTROL_AUTO_ENABLE_COUNT; i++) {
>>               ctrl.id = fake_controls[i].id;
>> -            rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
>> +            rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                    VIDIOC_QUERYCTRL,&ctrl);
>>               if (rc == -1 || (rc == 0&&  (ctrl.flags&  
>> V4L2_CTRL_FLAG_DISABLED)))
>>                   data->controls |= 1<<  i;
>>           }
>> @@ -807,17 +812,20 @@ struct v4lcontrol_data *v4lcontrol_create(int 
>> fd, int always_needs_conversion)
>>          different sensors with / without autogain or the necessary 
>> controls. */
>>       while (data->flags&  V4LCONTROL_WANTS_AUTOGAIN) {
>>           ctrl.id = V4L2_CID_AUTOGAIN;
>> -        rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
>> +        rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                VIDIOC_QUERYCTRL,&ctrl);
>>           if (rc == 0&&  !(ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>>               break;
>>
>>           ctrl.id = V4L2_CID_EXPOSURE;
>> -        rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
>> +        rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                VIDIOC_QUERYCTRL,&ctrl);
>>           if (rc != 0 || (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>>               break;
>>
>>           ctrl.id = V4L2_CID_GAIN;
>> -        rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL,&ctrl);
>> +        rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                VIDIOC_QUERYCTRL,&ctrl);
>>           if (rc != 0 || (ctrl.flags&  V4L2_CTRL_FLAG_DISABLED))
>>               break;
>>
>> @@ -834,7 +842,7 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, 
>> int always_needs_conversion)
>>       if (data->controls == 0)
>>           return data; /* No need to create a shared memory segment */
>>
>> -    if (SYS_IOCTL(fd, VIDIOC_QUERYCAP,&cap)) {
>> +    if (data->dev_ops->ioctl(data->dev_ops_priv, fd, 
>> VIDIOC_QUERYCAP,&cap)) {
>>           perror("libv4lcontrol: error querying device capabilities");
>>           goto error;
>>       }
>> @@ -1020,7 +1028,8 @@ int v4lcontrol_vidioc_queryctrl(struct 
>> v4lcontrol_data *data, void *arg)
>>           }
>>
>>       /* find out what the kernel driver would respond. */
>> -    retval = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, arg);
>> +    retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +            VIDIOC_QUERYCTRL, arg);
>>
>>       if ((data->priv_flags&  V4LCONTROL_SUPPORTS_NEXT_CTRL)&&
>>               (orig_id&  V4L2_CTRL_FLAG_NEXT_CTRL)) {
>> @@ -1057,7 +1066,8 @@ int v4lcontrol_vidioc_g_ctrl(struct 
>> v4lcontrol_data *data, void *arg)
>>               return 0;
>>           }
>>
>> -    return SYS_IOCTL(data->fd, VIDIOC_G_CTRL, arg);
>> +    return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +            VIDIOC_G_CTRL, arg);
>>   }
>>
>>   int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
>> @@ -1078,7 +1088,8 @@ int v4lcontrol_vidioc_s_ctrl(struct 
>> v4lcontrol_data *data, void *arg)
>>               return 0;
>>           }
>>
>> -    return SYS_IOCTL(data->fd, VIDIOC_S_CTRL, arg);
>> +    return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +            VIDIOC_S_CTRL, arg);
>>   }
>>
>>   int v4lcontrol_get_flags(struct v4lcontrol_data *data)
>> diff --git a/lib/libv4lconvert/control/libv4lcontrol.h 
>> b/lib/libv4lconvert/control/libv4lcontrol.h
>> index 974e97a..1c09546 100644
>> --- a/lib/libv4lconvert/control/libv4lcontrol.h
>> +++ b/lib/libv4lconvert/control/libv4lcontrol.h
>> @@ -22,6 +22,8 @@
>>   #ifndef __LIBV4LCONTROL_H
>>   #define __LIBV4LCONTROL_H
>>
>> +#include "libv4l2-plugin.h"
>> +
>>   /* Flags */
>>   #define V4LCONTROL_HFLIPPED              0x01
>>   #define V4LCONTROL_VFLIPPED              0x02
>> @@ -47,7 +49,8 @@ enum {
>>
>>   struct v4lcontrol_data;
>>
>> -struct v4lcontrol_data *v4lcontrol_create(int fd, int 
>> always_needs_conversion);
>> +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
>> +        struct libv4l2_dev_ops *dev_ops, int always_needs_conversion);
>>   void v4lcontrol_destroy(struct v4lcontrol_data *data);
>>
>>   /* Functions used by v4lprocessing to get the control state */
>> diff --git a/lib/libv4lconvert/libv4lconvert-priv.h 
>> b/lib/libv4lconvert/libv4lconvert-priv.h
>> index 84c706e..9cb6e1c 100644
>> --- a/lib/libv4lconvert/libv4lconvert-priv.h
>> +++ b/lib/libv4lconvert/libv4lconvert-priv.h
>> @@ -65,6 +65,8 @@ struct v4lconvert_data {
>>       unsigned char *convert_pixfmt_buf;
>>       struct v4lcontrol_data *control;
>>       struct v4lprocessing_data *processing;
>> +    struct libv4l2_dev_ops *dev_ops;
>> +    void *dev_ops_priv;
>>
>>       /* Data for external decompression helpers code */
>>       pid_t decompress_pid;
>> diff --git a/lib/libv4lconvert/libv4lconvert.c 
>> b/lib/libv4lconvert/libv4lconvert.c
>> index e4863fd..6861986 100644
>> --- a/lib/libv4lconvert/libv4lconvert.c
>> +++ b/lib/libv4lconvert/libv4lconvert.c
>> @@ -92,7 +92,8 @@ static const int v4lconvert_crop_res[][2] = {
>>       { 176, 144 },
>>   };
>>
>> -struct v4lconvert_data *v4lconvert_create(int fd)
>> +struct v4lconvert_data *v4lconvert_create(int fd, void *dev_ops_priv,
>> +        struct libv4l2_dev_ops *dev_ops)
>>   {
>>       int i, j;
>>       struct v4lconvert_data *data = calloc(1, sizeof(struct 
>> v4lconvert_data));
>> @@ -108,6 +109,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>>       }
>>
>>       data->fd = fd;
>> +    data->dev_ops = dev_ops;
>> +    data->dev_ops_priv = dev_ops_priv;
>>       data->decompress_pid = -1;
>>
>>       /* Check supported formats */
>> @@ -116,7 +119,7 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>>
>>           fmt.index = i;
>>
>> -        if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT,&fmt))
>> +        if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, 
>> VIDIOC_ENUM_FMT,&fmt))
>>               break;
>>
>>           for (j = 0; j<  ARRAY_SIZE(supported_src_pixfmts); j++)
>> @@ -135,7 +138,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>>       data->no_formats = i;
>>
>>       /* Check if this cam has any special flags */
>> -    if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP,&cap) == 0) {
>> +    if (data->dev_ops->ioctl(data->dev_ops_priv,
>> +                    data->fd, VIDIOC_QUERYCAP,&cap) == 0) {
>>           if (!strcmp((char *)cap.driver, "uvcvideo"))
>>               data->flags |= V4LCONVERT_IS_UVC;
>>
>> @@ -143,7 +147,8 @@ struct v4lconvert_data *v4lconvert_create(int fd)
>>               always_needs_conversion = 0;
>>       }
>>
>> -    data->control = v4lcontrol_create(fd, always_needs_conversion);
>> +    data->control = v4lcontrol_create(fd, dev_ops_priv, dev_ops,
>> +                        always_needs_conversion);
>>       if (!data->control) {
>>           free(data);
>>           return NULL;
>> @@ -206,7 +211,8 @@ int v4lconvert_enum_fmt(struct v4lconvert_data 
>> *data, struct v4l2_fmtdesc *fmt)
>>       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>>               (!v4lconvert_supported_dst_fmt_only(data)&&
>>               fmt->index<  data->no_formats))
>> -        return SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, fmt);
>> +        return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                        VIDIOC_ENUM_FMT, fmt);
>>
>>       for (i = 0; i<  ARRAY_SIZE(supported_dst_pixfmts); i++)
>>           if (v4lconvert_supported_dst_fmt_only(data) ||
>> @@ -314,7 +320,8 @@ static int v4lconvert_do_try_format(struct 
>> v4lconvert_data *data,
>>           try_fmt = *dest_fmt;
>>           try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;
>>
>> -        if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT,&try_fmt)) {
>> +        if (!data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                        VIDIOC_TRY_FMT,&try_fmt)) {
>>               if (try_fmt.fmt.pix.pixelformat == 
>> supported_src_pixfmts[i].fmt) {
>>                   int size_x_diff = abs((int)try_fmt.fmt.pix.width -
>>                           (int)dest_fmt->fmt.pix.width);
>> @@ -382,7 +389,8 @@ int v4lconvert_try_format(struct v4lconvert_data 
>> *data,
>>       if 
>> (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
>>               dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>>               v4lconvert_do_try_format(data,&try_dest,&try_src)) {
>> -        result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt);
>> +        result = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                            VIDIOC_TRY_FMT, dest_fmt);
>>           if (src_fmt)
>>               *src_fmt = *dest_fmt;
>>           return result;
>> @@ -1241,7 +1249,8 @@ static void v4lconvert_get_framesizes(struct 
>> v4lconvert_data *data,
>>
>>       for (i = 0; ; i++) {
>>           frmsize.index = i;
>> -        if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES,&frmsize))
>> +        if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                    VIDIOC_ENUM_FRAMESIZES,&frmsize))
>>               break;
>>
>>           /* We got a framesize, check we don't have the same one 
>> already */
>> @@ -1301,7 +1310,8 @@ int v4lconvert_enum_framesizes(struct 
>> v4lconvert_data *data,
>>               errno = EINVAL;
>>               return -1;
>>           }
>> -        return SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize);
>> +        return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                        VIDIOC_ENUM_FRAMESIZES, frmsize);
>>       }
>>
>>       if (frmsize->index>= data->no_framesizes) {
>> @@ -1337,7 +1347,8 @@ int v4lconvert_enum_frameintervals(struct 
>> v4lconvert_data *data,
>>               errno = EINVAL;
>>               return -1;
>>           }
>> -        res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>> +        res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +                VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>>           if (res)
>>               V4LCONVERT_ERR("%s\n", strerror(errno));
>>           return res;
>> @@ -1382,7 +1393,8 @@ int v4lconvert_enum_frameintervals(struct 
>> v4lconvert_data *data,
>>       frmival->pixel_format = src_fmt.fmt.pix.pixelformat;
>>       frmival->width = src_fmt.fmt.pix.width;
>>       frmival->height = src_fmt.fmt.pix.height;
>> -    res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>> +    res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
>> +            VIDIOC_ENUM_FRAMEINTERVALS, frmival);
>>       if (res) {
>>           int dest_pixfmt = dest_fmt.fmt.pix.pixelformat;
>>           int src_pixfmt  = src_fmt.fmt.pix.pixelformat;
>> diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
>> index 232a318..349d1ca 100644
>> --- a/utils/qv4l2/qv4l2.cpp
>> +++ b/utils/qv4l2/qv4l2.cpp
>> @@ -47,6 +47,17 @@
>>   #include<sys/mman.h>
>>   #include<errno.h>
>>   #include<dirent.h>
>> +#include<syscall.h>
>> +
>> +int defIoctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg)
>> +{
>> +    return syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd),
>> +                    (void *)(arg));
>> +}
>> +ssize_t defRead(void *dev_ops_priv, int fd, void *buf, size_t len)
>> +{
>> +    return syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len));
>> +}
>>
>>   ApplicationWindow::ApplicationWindow() :
>>       m_capture(NULL),
>> @@ -59,6 +70,10 @@ ApplicationWindow::ApplicationWindow() :
>>       m_frameData = NULL;
>>       m_nbuffers = 0;
>>       m_buffers = NULL;
>> +    m_defauldDevOps.init = NULL;
>> +    m_defauldDevOps.close = NULL;
>> +    m_defauldDevOps.ioctl =&defIoctl;
>> +    m_defauldDevOps.read =&defRead;
>>
>>       QAction *openAct = new QAction(QIcon(":/fileopen.png"), "&Open 
>> device", this);
>>       openAct->setStatusTip("Open a v4l device, use libv4l2 wrapper 
>> if possible");
>> @@ -144,7 +159,7 @@ void ApplicationWindow::setDevice(const 
>> QString&device, bool rawOpen)
>>       }
>>       m_tabs->show();
>>       m_tabs->setFocus();
>> -    m_convertData = v4lconvert_create(fd());
>> +    m_convertData = v4lconvert_create(fd(), NULL,&m_defauldDevOps);
>>       m_capStartAct->setEnabled(fd()>= 0);
>>   }
>>
>> diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
>> index 0a3e5ef..d847fe6 100644
>> --- a/utils/qv4l2/qv4l2.h
>> +++ b/utils/qv4l2/qv4l2.h
>> @@ -91,6 +91,7 @@ private:
>>       unsigned char *m_frameData;
>>       unsigned m_nbuffers;
>>       struct v4lconvert_data *m_convertData;
>> +    struct libv4l2_dev_ops m_defauldDevOps;
>>       CapMethod m_capMethod;
>>
>>   private slots:

--
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
Hans de Goede July 24, 2011, 5:36 p.m. UTC | #4
Hi,

On 07/22/2011 05:39 PM, Yordan Kamenov wrote:
> Hi Hans,
>
> Hans de Goede wrote:
>> Hi,
>>
>> Sorry it took so long, but I've just merged the plugin
>> support into v4l-utils git. I did make some minor mods /
>> bugfixes before merging, see the commit message in git.
>>
>> Regards,
>>
>> Hans
>>
>> p.s.
>>
>> I think we should expand the plugin support with support
>> for a output devices, iow add a write() dev_op. If you
>> guys agree I can easily do so myself, we should do this
>> asap before people start depending on the ABI
>> (although there is no ABI stability promise until I
>> release 0.10.x, see my message to the list wrt
>> the start of the 0.9.x cycle).
>>
>
> I think that it is a good point, you can add write() and
> reserved dev_ops.

Ok, done, this is in v4l-utils git master now.

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
Sakari Ailus July 26, 2011, 7:05 p.m. UTC | #5
On Sun, Jul 24, 2011 at 07:36:38PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 07/22/2011 05:39 PM, Yordan Kamenov wrote:
> >Hi Hans,
> >
> >Hans de Goede wrote:
> >>Hi,
> >>
> >>Sorry it took so long, but I've just merged the plugin
> >>support into v4l-utils git. I did make some minor mods /
> >>bugfixes before merging, see the commit message in git.
> >>
> >>Regards,
> >>
> >>Hans
> >>
> >>p.s.
> >>
> >>I think we should expand the plugin support with support
> >>for a output devices, iow add a write() dev_op. If you
> >>guys agree I can easily do so myself, we should do this
> >>asap before people start depending on the ABI
> >>(although there is no ABI stability promise until I
> >>release 0.10.x, see my message to the list wrt
> >>the start of the 0.9.x cycle).
> >>
> >
> >I think that it is a good point, you can add write() and
> >reserved dev_ops.
> 
> Ok, done, this is in v4l-utils git master now.

Wow! Thanks, Hans and Yordan! :-)
diff mbox

Patch

diff --git a/lib/include/libv4l2-plugin.h b/lib/include/libv4l2-plugin.h
new file mode 100644
index 0000000..158c0c2
--- /dev/null
+++ b/lib/include/libv4l2-plugin.h
@@ -0,0 +1,36 @@ 
+/*
+* Copyright (C) 2010 Nokia Corporation <multimedia@maemo.org>
+
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 2.1 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef __LIBV4L2_PLUGIN_H
+#define __LIBV4L2_PLUGIN_H
+
+#include <sys/types.h>
+
+/* Structure libv4l2_dev_ops holds the calls from libv4ls to video nodes.
+   They can be normal open/close/ioctl etc. or any of them may be replaced
+   with a callback by a loaded plugin.
+*/
+
+struct libv4l2_dev_ops {
+    void * (*init)(int fd);
+    void (*close)(void *dev_ops_priv);
+    int (*ioctl)(void *dev_ops_priv, int fd, unsigned long int request, void *arg);
+    ssize_t (*read)(void *dev_ops_priv, int fd, void *buffer, size_t n);
+};
+
+#endif
diff --git a/lib/include/libv4lconvert.h b/lib/include/libv4lconvert.h
index 0264290..351142e 100644
--- a/lib/include/libv4lconvert.h
+++ b/lib/include/libv4lconvert.h
@@ -38,6 +38,8 @@ 
 
 #include <linux/videodev2.h>
 
+#include "libv4l2-plugin.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
@@ -50,7 +52,8 @@  extern "C" {
 
 struct v4lconvert_data;
 
-LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd);
+LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd,
+		void *dev_ops_priv, struct libv4l2_dev_ops *dev_ops);
 LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data);
 
 /* When doing flipping / rotating / video-processing, only supported
diff --git a/lib/libv4l2/Makefile b/lib/libv4l2/Makefile
index d78632f..f8b3714 100644
--- a/lib/libv4l2/Makefile
+++ b/lib/libv4l2/Makefile
@@ -1,12 +1,12 @@ 
 override CPPFLAGS += -I../include -fvisibility=hidden
 
-LIBS_libv4l2  = -lpthread
+LIBS_libv4l2  = -lpthread -ldl
 
-V4L2_OBJS     = libv4l2.o log.o
+V4L2_OBJS     = libv4l2.o v4l2-plugin.o log.o
 V4L2CONVERT   = v4l2convert.so
 V4L2CONVERT_O = v4l2convert.o libv4l2.so
 TARGETS       = $(V4L2_LIB) libv4l2.pc
-INCLUDES      = ../include/libv4l2.h
+INCLUDES      = ../include/libv4l2.h ../include/libv4l2-plugin.h
 
 ifeq ($(LINKTYPE),static)
 V4L2_LIB      = libv4l2.a
diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
index 46d6103..d06a508 100644
--- a/lib/libv4l2/libv4l2-priv.h
+++ b/lib/libv4l2/libv4l2-priv.h
@@ -85,8 +85,18 @@  struct v4l2_dev_info {
 	/* buffer when doing conversion and using read() for read() */
 	int readbuf_size;
 	unsigned char *readbuf;
+	struct libv4l2_dev_ops *dev_ops;
+	/* plugin info */
+	void *plugin_library;
+	void *dev_ops_priv;
 };
 
+void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret,
+					struct libv4l2_dev_ops **dev_ops_ret);
+void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
+					struct libv4l2_dev_ops *dev_ops);
+
+
 /* From log.c */
 void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
 
diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
index ab85ea7..018350e 100644
--- a/lib/libv4l2/libv4l2.c
+++ b/lib/libv4l2/libv4l2.c
@@ -67,6 +67,7 @@ 
 #include <sys/stat.h>
 #include "libv4l2.h"
 #include "libv4l2-priv.h"
+#include "libv4l2-plugin.h"
 
 /* Note these flags are stored together with the flags passed to v4l2_fd_open()
    in v4l2_dev_info's flags member, so care should be taken that the do not
@@ -102,7 +103,8 @@  static int v4l2_request_read_buffers(int index)
 		devices[index].nreadbuffers;
 	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	req.memory = V4L2_MEMORY_MMAP;
-	result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req);
+	result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+			devices[index].fd, VIDIOC_REQBUFS, &req);
 	if (result < 0) {
 		int saved_err = errno;
 
@@ -131,7 +133,8 @@  static void v4l2_unrequest_read_buffers(int index)
 	req.count = 0;
 	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	req.memory = V4L2_MEMORY_MMAP;
-	if (SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req) < 0)
+	if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+			devices[index].fd, VIDIOC_REQBUFS, &req) < 0)
 		return;
 
 	devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES);
@@ -152,7 +155,8 @@  static int v4l2_map_buffers(int index)
 		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 		buf.memory = V4L2_MEMORY_MMAP;
 		buf.index = i;
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_QUERYBUF, &buf);
 		if (result) {
 			int saved_err = errno;
 
@@ -202,7 +206,8 @@  static int v4l2_streamon(int index)
 	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
 	if (!(devices[index].flags & V4L2_STREAMON)) {
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMON, &type);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_STREAMON, &type);
 		if (result) {
 			int saved_err = errno;
 
@@ -223,7 +228,8 @@  static int v4l2_streamoff(int index)
 	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
 	if (devices[index].flags & V4L2_STREAMON) {
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMOFF, &type);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_STREAMOFF, &type);
 		if (result) {
 			int saved_err = errno;
 
@@ -252,7 +258,8 @@  static int v4l2_queue_read_buffer(int index, int buffer_index)
 	buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	buf.memory = V4L2_MEMORY_MMAP;
 	buf.index  = buffer_index;
-	result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, &buf);
+	result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+			devices[index].fd, VIDIOC_QBUF, &buf);
 	if (result) {
 		int saved_err = errno;
 
@@ -277,7 +284,8 @@  static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
 		return result;
 
 	do {
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_DQBUF, buf);
 		if (result) {
 			if (errno != EAGAIN) {
 				int saved_err = errno;
@@ -356,7 +364,8 @@  static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size)
 	}
 
 	do {
-		result = SYS_READ(devices[index].fd, devices[index].readbuf, buf_size);
+		result = devices[index].dev_ops->read(devices[index].dev_ops_priv,
+				devices[index].fd, devices[index].readbuf, buf_size);
 		if (result <= 0) {
 			if (result && errno != EAGAIN) {
 				int saved_err = errno;
@@ -496,7 +505,8 @@  static int v4l2_buffers_mapped(int index)
 			buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 			buf.memory = V4L2_MEMORY_MMAP;
 			buf.index = i;
-			if (SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf)) {
+			if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+					devices[index].fd, VIDIOC_QUERYBUF, &buf)) {
 				int saved_err = errno;
 
 				V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno));
@@ -561,6 +571,11 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	struct v4l2_capability cap;
 	struct v4l2_format fmt;
 	struct v4lconvert_data *convert;
+	struct libv4l2_dev_ops *dev_ops;
+	void *plugin_library;
+	void *plugin_data;
+
+	v4l2_plugin_init(fd, &plugin_library, &plugin_data, &dev_ops);
 
 	/* If no log file was set by the app, see if one was specified through the
 	   environment */
@@ -571,9 +586,10 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	}
 
 	/* check that this is an v4l2 device */
-	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) {
+	if (dev_ops->ioctl(plugin_data, fd, VIDIOC_QUERYCAP, &cap)) {
 		int saved_err = errno;
 
+		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
 		V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
 		errno = saved_err;
 		return -1;
@@ -581,29 +597,38 @@  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)))
+			!(cap.capabilities & (V4L2_CAP_STREAMING | V4L2_CAP_READWRITE))) {
+		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
 		return fd;
+	}
 
 	/* Get current cam format */
 	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	if (SYS_IOCTL(fd, VIDIOC_G_FMT, &fmt)) {
+	if (dev_ops->ioctl(plugin_data, fd, VIDIOC_G_FMT, &fmt)) {
 		int saved_err = errno;
 
+		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
 		V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
 		errno = saved_err;
 		return -1;
 	}
 
 	/* init libv4lconvert */
-	convert = v4lconvert_create(fd);
-	if (!convert)
+	convert = v4lconvert_create(fd, plugin_data, dev_ops);
+	if (!convert) {
+		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
 		return -1;
+	}
 
 	/* 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;
+
+			devices[index].plugin_library = plugin_library;
+			devices[index].dev_ops_priv = plugin_data;
+			devices[index].dev_ops = dev_ops;
 			break;
 		}
 	pthread_mutex_unlock(&v4l2_open_mutex);
@@ -611,6 +636,7 @@  int v4l2_fd_open(int fd, int v4l2_flags)
 	if (index == V4L2_MAX_DEVICES) {
 		V4L2_LOG_ERR("attempting to open more then %d video devices\n",
 				V4L2_MAX_DEVICES);
+		v4l2_plugin_cleanup(plugin_data, plugin_library, dev_ops);
 		errno = EBUSY;
 		return -1;
 	}
@@ -700,6 +726,9 @@  int v4l2_close(int fd)
 	if (result)
 		return 0;
 
+	v4l2_plugin_cleanup(devices[index].dev_ops_priv,
+			devices[index].plugin_library, devices[index].dev_ops);
+
 	/* Free resources */
 	v4l2_unmap_buffers(index);
 	if (devices[index].convert_mmap_buf != MAP_FAILED) {
@@ -879,7 +908,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 	}
 
 	if (!is_capture_request) {
-		result = SYS_IOCTL(fd, request, arg);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				fd, request, arg);
 		saved_err = errno;
 		v4l2_log_ioctl(request, arg, result);
 		errno = saved_err;
@@ -927,7 +957,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 	case VIDIOC_QUERYCAP: {
 		struct v4l2_capability *cap = arg;
 
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYCAP, cap);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_QUERYCAP, cap);
 		if (result == 0)
 			/* We always support read() as we fake it using mmap mode */
 			cap->capabilities |= V4L2_CAP_READWRITE;
@@ -977,8 +1008,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 		}
 
 		if (devices[index].flags & V4L2_DISABLE_CONVERSION) {
-			result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT,
-					dest_fmt);
+			result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+					devices[index].fd, VIDIOC_TRY_FMT, dest_fmt);
 			src_fmt = *dest_fmt;
 		} else {
 			result = v4lconvert_try_format(devices[index].convert, dest_fmt,
@@ -1018,7 +1049,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 			break;
 
 		req_pix_fmt = src_fmt.fmt.pix;
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT, &src_fmt);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_S_FMT, &src_fmt);
 		if (result) {
 			saved_err = errno;
 			V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno));
@@ -1067,7 +1099,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 		if (req->count > V4L2_MAX_NO_FRAMES)
 			req->count = V4L2_MAX_NO_FRAMES;
 
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, req);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_REQBUFS, req);
 		if (result < 0)
 			break;
 		result = 0; /* some drivers return the number of buffers on success */
@@ -1088,7 +1121,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 
 		/* Do a real query even when converting to let the driver fill in
 		   things like buf->field */
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, buf);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_QUERYBUF, buf);
 		if (result || !v4l2_needs_conversion(index))
 			break;
 
@@ -1115,7 +1149,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 				break;
 		}
 
-		result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, arg);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				devices[index].fd, VIDIOC_QBUF, arg);
 		break;
 
 	case VIDIOC_DQBUF: {
@@ -1128,7 +1163,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 		}
 
 		if (!v4l2_needs_conversion(index)) {
-			result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf);
+			result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+					devices[index].fd, VIDIOC_DQBUF, buf);
 			if (result) {
 				int saved_err = errno;
 
@@ -1187,7 +1223,8 @@  int v4l2_ioctl(int fd, unsigned long int request, ...)
 		break;
 
 	default:
-		result = SYS_IOCTL(fd, request, arg);
+		result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+				fd, request, arg);
 		break;
 	}
 
@@ -1217,7 +1254,8 @@  ssize_t v4l2_read(int fd, void *dest, size_t n)
 	   it */
 	if ((devices[index].flags & V4L2_SUPPORTS_READ) &&
 			!v4l2_needs_conversion(index)) {
-		result = SYS_READ(fd, dest, n);
+		result = devices[index].dev_ops->read(devices[index].dev_ops_priv,
+				fd, dest, n);
 		goto leave;
 	}
 
diff --git a/lib/libv4l2/v4l2-plugin.c b/lib/libv4l2/v4l2-plugin.c
new file mode 100644
index 0000000..3735193
--- /dev/null
+++ b/lib/libv4l2/v4l2-plugin.c
@@ -0,0 +1,158 @@ 
+/*
+* Copyright (C) 2010 Nokia Corporation <multimedia@maemo.org>
+
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 2.1 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdarg.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include "libv4l2.h"
+#include "libv4l2-priv.h"
+#include "libv4l2-plugin.h"
+
+/* libv4l plugin support:
+   it is provided by functions v4l2_plugin_[open,close,etc].
+
+   When open() is called libv4l dlopens files in /usr/lib[64]/libv4l/plugins
+   1 at a time and call open callback passing through the applications
+   parameters unmodified.
+
+   If a plugin is relevant for the specified device node, it can indicate so
+   by returning a value other then -1 (the actual file descriptor).
+   As soon as a plugin returns another value then -1 plugin loading stops and
+   information about it (fd and corresponding library handle) is stored. For
+   each function v4l2_[ioctl,read,close,etc] is called corresponding
+   v4l2_plugin_* function which looks if there is loaded plugin for that file
+   and call it's callbacks.
+
+   v4l2_plugin_* function indicates by it's first argument if plugin was used,
+   and if it was not then v4l2_* functions proceed with their usual behavior.
+*/
+
+#define PLUGINS_PATTERN "/usr/lib/libv4l/plugins/*.so"
+
+static void * dev_init(int fd)
+{
+	return NULL;
+}
+
+static void dev_close(void *dev_ops_priv)
+{
+}
+
+static int dev_ioctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg)
+{
+	return SYS_IOCTL(fd, cmd, arg);
+}
+
+static ssize_t dev_read(void *dev_ops_priv, int fd, void *buf, size_t len)
+{
+	return SYS_READ(fd, buf, len);
+}
+
+
+struct libv4l2_dev_ops default_operations = {
+	.init = &dev_init,
+	.close = &dev_close,
+	.ioctl = &dev_ioctl,
+	.read = &dev_read
+};
+
+void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret,
+					struct libv4l2_dev_ops **dev_ops_ret)
+{
+	char *error;
+	int glob_ret, plugin_num;
+	void *plugin_library = NULL;
+	struct libv4l2_dev_ops *libv4l2_plugin = NULL;
+	glob_t globbuf;
+
+	*dev_ops_ret = &default_operations;
+
+	glob_ret = glob(PLUGINS_PATTERN, 0, NULL, &globbuf);
+
+	if (glob_ret == GLOB_NOSPACE) {
+		*plugin_lib_ret = NULL;
+		*plugin_priv_ret = NULL;
+		return;
+	}
+
+	if (glob_ret == GLOB_ABORTED || glob_ret == GLOB_NOMATCH) {
+		*plugin_lib_ret = NULL;
+		*plugin_priv_ret = NULL;
+		goto leave;
+	}
+
+	for (plugin_num = 0; plugin_num < globbuf.gl_pathc; plugin_num++) {
+
+		V4L2_LOG("PLUGIN: dlopen(%s);\n", globbuf.gl_pathv[plugin_num]);
+
+		plugin_library = dlopen(globbuf.gl_pathv[plugin_num],
+							RTLD_LAZY);
+
+		if (!plugin_library)
+			continue;
+
+		dlerror();    /* Clear any existing error */
+		libv4l2_plugin = (struct libv4l2_dev_ops *)
+					dlsym(plugin_library, "libv4l2_plugin");
+
+		error = dlerror();
+		if (error != NULL)  {
+			V4L2_LOG_ERR("PLUGIN: dlsym failed: %s\n", error);
+			dlclose(plugin_library);
+
+			continue;
+		}
+
+		*plugin_priv_ret = NULL;
+		if (libv4l2_plugin->init)
+			*plugin_priv_ret = libv4l2_plugin->init(fd);
+
+		if (!*plugin_priv_ret) {
+			V4L2_LOG("PLUGIN: plugin open() returned NULL\n");
+			dlclose(plugin_library);
+			*plugin_lib_ret = NULL;
+			continue;
+		}
+
+		(*dev_ops_ret)->init = libv4l2_plugin->init;
+		if (libv4l2_plugin->close != NULL)
+			(*dev_ops_ret)->close = libv4l2_plugin->close;
+		if (libv4l2_plugin->ioctl != NULL)
+			(*dev_ops_ret)->ioctl = libv4l2_plugin->ioctl;
+		if (libv4l2_plugin->read != NULL)
+			(*dev_ops_ret)->read = libv4l2_plugin->read;
+
+		break;
+
+	}
+
+leave:
+	globfree(&globbuf);
+}
+
+void v4l2_plugin_cleanup(void *plugin_priv, void *plugin_lib,
+					struct libv4l2_dev_ops *dev_ops)
+{
+	if (plugin_priv) {
+		dev_ops->close(plugin_priv);
+		dlclose(plugin_lib);
+	}
+}
diff --git a/lib/libv4l2/v4l2convert.c b/lib/libv4l2/v4l2convert.c
index e251085..03f34ad 100644
--- a/lib/libv4l2/v4l2convert.c
+++ b/lib/libv4l2/v4l2convert.c
@@ -46,7 +46,6 @@ 
 LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
 {
 	int fd;
-	struct v4l2_capability cap;
 	int v4l_device = 0;
 
 	/* check if we're opening a video4linux2 device */
@@ -76,14 +75,6 @@  LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
 	if (fd == -1 || !v4l_device)
 		return fd;
 
-	/* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */
-	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap))
-		return fd;
-
-	/* libv4l2 only adds functionality to capture capable devices */
-	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
-		return fd;
-
 	/* Try to Register with libv4l2 (in case of failure pass the fd to the
 	   application as is) */
 	v4l2_fd_open(fd, 0);
diff --git a/lib/libv4lconvert/control/libv4lcontrol-priv.h b/lib/libv4lconvert/control/libv4lcontrol-priv.h
index 22cdf34..1259e6b 100644
--- a/lib/libv4lconvert/control/libv4lcontrol-priv.h
+++ b/lib/libv4lconvert/control/libv4lcontrol-priv.h
@@ -22,6 +22,8 @@ 
 #ifndef __LIBV4LCONTROL_PRIV_H
 #define __LIBV4LCONTROL_PRIV_H
 
+#include "libv4l2-plugin.h"
+
 #define V4LCONTROL_SHM_SIZE 4096
 
 #define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01
@@ -37,6 +39,8 @@  struct v4lcontrol_data {
 	unsigned int *shm_values; /* shared memory control value store */
 	unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */
 	const struct v4lcontrol_flags_info *flags_info;
+	struct libv4l2_dev_ops *dev_ops;
+	void *dev_ops_priv;
 };
 
 struct v4lcontrol_flags_info {
diff --git a/lib/libv4lconvert/control/libv4lcontrol.c b/lib/libv4lconvert/control/libv4lcontrol.c
index 116bef5..24a5600 100644
--- a/lib/libv4lconvert/control/libv4lcontrol.c
+++ b/lib/libv4lconvert/control/libv4lcontrol.c
@@ -747,7 +747,8 @@  static void v4lcontrol_get_flags_from_db(struct v4lcontrol_data *data,
 		}
 }
 
-struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
+struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
+		struct libv4l2_dev_ops *dev_ops, int always_needs_conversion)
 {
 	int shm_fd;
 	int i, rc, got_usb_ids, init = 0;
@@ -767,10 +768,12 @@  struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
 	}
 
 	data->fd = fd;
+	data->dev_ops = dev_ops;
+	data->dev_ops_priv = dev_ops_priv;
 
 	/* Check if the driver has indicated some form of flipping is needed */
-	if ((SYS_IOCTL(data->fd, VIDIOC_G_INPUT, &input.index) == 0) &&
-			(SYS_IOCTL(data->fd, VIDIOC_ENUMINPUT, &input) == 0)) {
+	if ((data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_G_INPUT, &input.index) == 0) &&
+			(data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_ENUMINPUT, &input) == 0)) {
 		if (input.status & V4L2_IN_ST_HFLIP)
 			data->flags |= V4LCONTROL_HFLIPPED;
 		if (input.status & V4L2_IN_ST_VFLIP)
@@ -787,7 +790,8 @@  struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
 		data->flags = strtol(s, NULL, 0);
 
 	ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
-	if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl) == 0)
+	if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+			VIDIOC_QUERYCTRL, &ctrl) == 0)
 		data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL;
 
 	/* If the device always needs conversion, we can add fake controls at no cost
@@ -795,7 +799,8 @@  struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
 	if (always_needs_conversion || v4lcontrol_needs_conversion(data)) {
 		for (i = 0; i < V4LCONTROL_AUTO_ENABLE_COUNT; i++) {
 			ctrl.id = fake_controls[i].id;
-			rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+			rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+					VIDIOC_QUERYCTRL, &ctrl);
 			if (rc == -1 || (rc == 0 && (ctrl.flags & V4L2_CTRL_FLAG_DISABLED)))
 				data->controls |= 1 << i;
 		}
@@ -807,17 +812,20 @@  struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
 	   different sensors with / without autogain or the necessary controls. */
 	while (data->flags & V4LCONTROL_WANTS_AUTOGAIN) {
 		ctrl.id = V4L2_CID_AUTOGAIN;
-		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+				VIDIOC_QUERYCTRL, &ctrl);
 		if (rc == 0 && !(ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
 			break;
 
 		ctrl.id = V4L2_CID_EXPOSURE;
-		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+				VIDIOC_QUERYCTRL, &ctrl);
 		if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
 			break;
 
 		ctrl.id = V4L2_CID_GAIN;
-		rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl);
+		rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+				VIDIOC_QUERYCTRL, &ctrl);
 		if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
 			break;
 
@@ -834,7 +842,7 @@  struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion)
 	if (data->controls == 0)
 		return data; /* No need to create a shared memory segment */
 
-	if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) {
+	if (data->dev_ops->ioctl(data->dev_ops_priv, fd, VIDIOC_QUERYCAP, &cap)) {
 		perror("libv4lcontrol: error querying device capabilities");
 		goto error;
 	}
@@ -1020,7 +1028,8 @@  int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
 		}
 
 	/* find out what the kernel driver would respond. */
-	retval = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, arg);
+	retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+			VIDIOC_QUERYCTRL, arg);
 
 	if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) &&
 			(orig_id & V4L2_CTRL_FLAG_NEXT_CTRL)) {
@@ -1057,7 +1066,8 @@  int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg)
 			return 0;
 		}
 
-	return SYS_IOCTL(data->fd, VIDIOC_G_CTRL, arg);
+	return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+			VIDIOC_G_CTRL, arg);
 }
 
 int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
@@ -1078,7 +1088,8 @@  int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
 			return 0;
 		}
 
-	return SYS_IOCTL(data->fd, VIDIOC_S_CTRL, arg);
+	return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+			VIDIOC_S_CTRL, arg);
 }
 
 int v4lcontrol_get_flags(struct v4lcontrol_data *data)
diff --git a/lib/libv4lconvert/control/libv4lcontrol.h b/lib/libv4lconvert/control/libv4lcontrol.h
index 974e97a..1c09546 100644
--- a/lib/libv4lconvert/control/libv4lcontrol.h
+++ b/lib/libv4lconvert/control/libv4lcontrol.h
@@ -22,6 +22,8 @@ 
 #ifndef __LIBV4LCONTROL_H
 #define __LIBV4LCONTROL_H
 
+#include "libv4l2-plugin.h"
+
 /* Flags */
 #define V4LCONTROL_HFLIPPED              0x01
 #define V4LCONTROL_VFLIPPED              0x02
@@ -47,7 +49,8 @@  enum {
 
 struct v4lcontrol_data;
 
-struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion);
+struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
+		struct libv4l2_dev_ops *dev_ops, int always_needs_conversion);
 void v4lcontrol_destroy(struct v4lcontrol_data *data);
 
 /* Functions used by v4lprocessing to get the control state */
diff --git a/lib/libv4lconvert/libv4lconvert-priv.h b/lib/libv4lconvert/libv4lconvert-priv.h
index 84c706e..9cb6e1c 100644
--- a/lib/libv4lconvert/libv4lconvert-priv.h
+++ b/lib/libv4lconvert/libv4lconvert-priv.h
@@ -65,6 +65,8 @@  struct v4lconvert_data {
 	unsigned char *convert_pixfmt_buf;
 	struct v4lcontrol_data *control;
 	struct v4lprocessing_data *processing;
+	struct libv4l2_dev_ops *dev_ops;
+	void *dev_ops_priv;
 
 	/* Data for external decompression helpers code */
 	pid_t decompress_pid;
diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c
index e4863fd..6861986 100644
--- a/lib/libv4lconvert/libv4lconvert.c
+++ b/lib/libv4lconvert/libv4lconvert.c
@@ -92,7 +92,8 @@  static const int v4lconvert_crop_res[][2] = {
 	{ 176, 144 },
 };
 
-struct v4lconvert_data *v4lconvert_create(int fd)
+struct v4lconvert_data *v4lconvert_create(int fd, void *dev_ops_priv,
+		struct libv4l2_dev_ops *dev_ops)
 {
 	int i, j;
 	struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data));
@@ -108,6 +109,8 @@  struct v4lconvert_data *v4lconvert_create(int fd)
 	}
 
 	data->fd = fd;
+	data->dev_ops = dev_ops;
+	data->dev_ops_priv = dev_ops_priv;
 	data->decompress_pid = -1;
 
 	/* Check supported formats */
@@ -116,7 +119,7 @@  struct v4lconvert_data *v4lconvert_create(int fd)
 
 		fmt.index = i;
 
-		if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, &fmt))
+		if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, VIDIOC_ENUM_FMT, &fmt))
 			break;
 
 		for (j = 0; j < ARRAY_SIZE(supported_src_pixfmts); j++)
@@ -135,7 +138,8 @@  struct v4lconvert_data *v4lconvert_create(int fd)
 	data->no_formats = i;
 
 	/* Check if this cam has any special flags */
-	if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP, &cap) == 0) {
+	if (data->dev_ops->ioctl(data->dev_ops_priv,
+					data->fd, VIDIOC_QUERYCAP, &cap) == 0) {
 		if (!strcmp((char *)cap.driver, "uvcvideo"))
 			data->flags |= V4LCONVERT_IS_UVC;
 
@@ -143,7 +147,8 @@  struct v4lconvert_data *v4lconvert_create(int fd)
 			always_needs_conversion = 0;
 	}
 
-	data->control = v4lcontrol_create(fd, always_needs_conversion);
+	data->control = v4lcontrol_create(fd, dev_ops_priv, dev_ops,
+						always_needs_conversion);
 	if (!data->control) {
 		free(data);
 		return NULL;
@@ -206,7 +211,8 @@  int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt)
 	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
 			(!v4lconvert_supported_dst_fmt_only(data) &&
 			 fmt->index < data->no_formats))
-		return SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, fmt);
+		return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+						VIDIOC_ENUM_FMT, fmt);
 
 	for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++)
 		if (v4lconvert_supported_dst_fmt_only(data) ||
@@ -314,7 +320,8 @@  static int v4lconvert_do_try_format(struct v4lconvert_data *data,
 		try_fmt = *dest_fmt;
 		try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;
 
-		if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, &try_fmt)) {
+		if (!data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+						VIDIOC_TRY_FMT, &try_fmt)) {
 			if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) {
 				int size_x_diff = abs((int)try_fmt.fmt.pix.width -
 						(int)dest_fmt->fmt.pix.width);
@@ -382,7 +389,8 @@  int v4lconvert_try_format(struct v4lconvert_data *data,
 	if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
 			dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
 			v4lconvert_do_try_format(data, &try_dest, &try_src)) {
-		result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt);
+		result = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+							VIDIOC_TRY_FMT, dest_fmt);
 		if (src_fmt)
 			*src_fmt = *dest_fmt;
 		return result;
@@ -1241,7 +1249,8 @@  static void v4lconvert_get_framesizes(struct v4lconvert_data *data,
 
 	for (i = 0; ; i++) {
 		frmsize.index = i;
-		if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize))
+		if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+					VIDIOC_ENUM_FRAMESIZES, &frmsize))
 			break;
 
 		/* We got a framesize, check we don't have the same one already */
@@ -1301,7 +1310,8 @@  int v4lconvert_enum_framesizes(struct v4lconvert_data *data,
 			errno = EINVAL;
 			return -1;
 		}
-		return SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize);
+		return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+						VIDIOC_ENUM_FRAMESIZES, frmsize);
 	}
 
 	if (frmsize->index >= data->no_framesizes) {
@@ -1337,7 +1347,8 @@  int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
 			errno = EINVAL;
 			return -1;
 		}
-		res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+		res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+				VIDIOC_ENUM_FRAMEINTERVALS, frmival);
 		if (res)
 			V4LCONVERT_ERR("%s\n", strerror(errno));
 		return res;
@@ -1382,7 +1393,8 @@  int v4lconvert_enum_frameintervals(struct v4lconvert_data *data,
 	frmival->pixel_format = src_fmt.fmt.pix.pixelformat;
 	frmival->width = src_fmt.fmt.pix.width;
 	frmival->height = src_fmt.fmt.pix.height;
-	res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival);
+	res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+			VIDIOC_ENUM_FRAMEINTERVALS, frmival);
 	if (res) {
 		int dest_pixfmt = dest_fmt.fmt.pix.pixelformat;
 		int src_pixfmt  = src_fmt.fmt.pix.pixelformat;
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index 232a318..349d1ca 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -47,6 +47,17 @@ 
 #include <sys/mman.h>
 #include <errno.h>
 #include <dirent.h>
+#include <syscall.h>
+
+int defIoctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg)
+{
+	return syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd),
+					(void *)(arg));
+}
+ssize_t defRead(void *dev_ops_priv, int fd, void *buf, size_t len)
+{
+	return syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len));
+}
 
 ApplicationWindow::ApplicationWindow() :
 	m_capture(NULL),
@@ -59,6 +70,10 @@  ApplicationWindow::ApplicationWindow() :
 	m_frameData = NULL;
 	m_nbuffers = 0;
 	m_buffers = NULL;
+	m_defauldDevOps.init = NULL;
+	m_defauldDevOps.close = NULL;
+	m_defauldDevOps.ioctl = &defIoctl;
+	m_defauldDevOps.read = &defRead;
 
 	QAction *openAct = new QAction(QIcon(":/fileopen.png"), "&Open device", this);
 	openAct->setStatusTip("Open a v4l device, use libv4l2 wrapper if possible");
@@ -144,7 +159,7 @@  void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
 	}
 	m_tabs->show();
 	m_tabs->setFocus();
-	m_convertData = v4lconvert_create(fd());
+	m_convertData = v4lconvert_create(fd(), NULL, &m_defauldDevOps);
 	m_capStartAct->setEnabled(fd() >= 0);
 }
 
diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
index 0a3e5ef..d847fe6 100644
--- a/utils/qv4l2/qv4l2.h
+++ b/utils/qv4l2/qv4l2.h
@@ -91,6 +91,7 @@  private:
 	unsigned char *m_frameData;
 	unsigned m_nbuffers;
 	struct v4lconvert_data *m_convertData;
+	struct libv4l2_dev_ops m_defauldDevOps;
 	CapMethod m_capMethod;
 
 private slots: