libv4l2: Add plugin support to libv4l
authorYordan Kamenov <ykamenov@mm-sol.com>
Thu, 19 May 2011 12:35:59 +0000 (15:35 +0300)
committerHans de Goede <hdegoede@redhat.com>
Sun, 17 Jul 2011 20:43:57 +0000 (22:43 +0200)
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.

Changes before merge by Hans de Goede:
-export libv4l2_default_dev_ops for apps which directly use libv4lconvert
-use libv4l2_default_dev_ops in qv4l2 rather then defining our own there
-swap 1st 2 arguments to v4l2_plugin_cleanup to match v4l2_plugin_init order
-v4l2_plugin_init: return the address of the plugins devops, rather then
 always returning the address of the default dev_ops and overriding the
 individual default dev_ops with those of the plugin. The latter is a bad idea
 when multiple /dev/video devices are opened in the same process space as
 they may use different plugins
-use $LIBDIR/libv4l/plugins as pluginsdir instead of hardcoded
 /usr/lib/libv4l/plugins

Signed-off-by: Yordan Kamenov <ykamenov@mm-sol.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
14 files changed:
lib/include/libv4l2-plugin.h [new file with mode: 0644]
lib/include/libv4l2.h
lib/include/libv4lconvert.h
lib/libv4l2/Makefile
lib/libv4l2/libv4l2-priv.h
lib/libv4l2/libv4l2.c
lib/libv4l2/v4l2-plugin.c [new file with mode: 0644]
lib/libv4l2/v4l2convert.c
lib/libv4lconvert/control/libv4lcontrol-priv.h
lib/libv4lconvert/control/libv4lcontrol.c
lib/libv4lconvert/control/libv4lcontrol.h
lib/libv4lconvert/libv4lconvert-priv.h
lib/libv4lconvert/libv4lconvert.c
utils/qv4l2/qv4l2.cpp

diff --git a/lib/include/libv4l2-plugin.h b/lib/include/libv4l2-plugin.h
new file mode 100644 (file)
index 0000000..158c0c2
--- /dev/null
@@ -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
index cb39792..1985e08 100644 (file)
@@ -37,6 +37,11 @@ extern "C" {
    status messages to a file, when NULL errors will get send to stderr */
 LIBV4L_PUBLIC extern FILE *v4l2_log_file;
 
+/* For apps which want to directly use libv4lconvert, but don't want to
+   define their own device ops. Apps which do this call v4lconvert_create 
+   with NULL as dev_ops_priv and this as dev_ops parameter */
+LIBV4L_PUBLIC extern const struct libv4l2_dev_ops libv4l2_default_dev_ops;
+
 /* Just like your regular open/close/etc, except that format conversion is
    done if necessary when capturing. That is if you (try to) set a capture
    format which is not supported by the cam, but is supported by libv4lconvert,
index 075fd86..579bd58 100644 (file)
@@ -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, const struct libv4l2_dev_ops *dev_ops);
 LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data);
 
 /* When doing flipping / rotating / video-processing, only supported
index 8428991..479990b 100644 (file)
@@ -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
@@ -18,6 +18,8 @@ TARGETS      += $(V4L2CONVERT)
 override CPPFLAGS += -fPIC
 endif
 
+override CPPFLAGS += -DLIBDIR='"$(LIBDIR)"'
+
 # This is the soname version
 LIB_RELEASE = 0
 
index a0a0f9c..2e99200 100644 (file)
@@ -87,8 +87,18 @@ struct v4l2_dev_info {
        /* buffer when doing conversion and using read() for read() */
        int readbuf_size;
        unsigned char *readbuf;
+       /* plugin info */
+       void *plugin_library;
+       void *dev_ops_priv;
+       const struct libv4l2_dev_ops *dev_ops;
 };
 
+/* From v4l2-plugin.c */
+void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret,
+                     const struct libv4l2_dev_ops **dev_ops_ret);
+void v4l2_plugin_cleanup(void *plugin_lib, void *plugin_priv,
+                        const struct libv4l2_dev_ops *dev_ops);
+
 /* From log.c */
 void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
 
index 4de382e..e975a65 100644 (file)
@@ -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
@@ -105,7 +106,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;
 
@@ -134,7 +136,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);
@@ -155,7 +158,9 @@ 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;
 
@@ -205,7 +210,9 @@ 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;
 
@@ -226,7 +233,9 @@ 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;
 
@@ -255,7 +264,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;
 
@@ -280,7 +290,9 @@ 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;
@@ -359,7 +371,10 @@ 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;
@@ -499,7 +514,10 @@ 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));
@@ -576,6 +594,11 @@ int v4l2_fd_open(int fd, int v4l2_flags)
        struct v4l2_format fmt;
        struct v4l2_streamparm parm;
        struct v4lconvert_data *convert;
+       void *plugin_library;
+       void *dev_ops_priv;
+       const struct libv4l2_dev_ops *dev_ops;
+
+       v4l2_plugin_init(fd, &plugin_library, &dev_ops_priv, &dev_ops);
 
        /* If no log file was set by the app, see if one was specified through the
           environment */
@@ -586,51 +609,62 @@ 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(dev_ops_priv, fd, VIDIOC_QUERYCAP, &cap)) {
                int saved_err = errno;
-
                V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
+               v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops);
                errno = saved_err;
                return -1;
        }
 
        /* 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_library, dev_ops_priv, 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(dev_ops_priv, fd, VIDIOC_G_FMT, &fmt)) {
                int saved_err = errno;
-
                V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
+               v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops);
                errno = saved_err;
                return -1;
        }
 
        /* Check for framerate setting support */
        parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       if (SYS_IOCTL(fd, VIDIOC_G_PARM, &parm))
+       if (dev_ops->ioctl(dev_ops_priv, fd, VIDIOC_G_PARM, &parm))
                parm.type = 0;
 
        /* init libv4lconvert */
-       convert = v4lconvert_create(fd);
-       if (!convert)
+       convert = v4lconvert_create(fd, dev_ops_priv, dev_ops);
+       if (!convert) {
+               int saved_err = errno;
+               v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops);
+               errno = saved_err;
                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++)
+       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 = dev_ops_priv;
+                       devices[index].dev_ops = dev_ops;
                        break;
                }
+       }
        pthread_mutex_unlock(&v4l2_open_mutex);
 
        if (index == V4L2_MAX_DEVICES) {
                V4L2_LOG_ERR("attempting to open more then %d video devices\n",
                                V4L2_MAX_DEVICES);
+               v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops);
                errno = EBUSY;
                return -1;
        }
@@ -719,8 +753,8 @@ int v4l2_close(int fd)
        if (index == -1)
                return SYS_CLOSE(fd);
 
-       /* Abuse stream_lock to stop 2 closes from racing and trying to free the
-          resources twice */
+       /* Abuse stream_lock to stop 2 closes from racing and trying to free
+          the resources twice */
        pthread_mutex_lock(&devices[index].stream_lock);
        devices[index].open_count--;
        result = devices[index].open_count != 0;
@@ -729,6 +763,10 @@ int v4l2_close(int fd)
        if (result)
                return 0;
 
+       v4l2_plugin_cleanup(devices[index].plugin_library,
+                       devices[index].dev_ops_priv,
+                       devices[index].dev_ops);
+
        /* Free resources */
        v4l2_unmap_buffers(index);
        if (devices[index].convert_mmap_buf != MAP_FAILED) {
@@ -926,7 +964,9 @@ 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;
@@ -974,7 +1014,9 @@ 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,
+                               fd, VIDIOC_QUERYCAP, cap);
                if (result == 0)
                        /* We always support read() as we fake it using mmap mode */
                        cap->capabilities |= V4L2_CAP_READWRITE;
@@ -998,8 +1040,9 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
 
        case VIDIOC_TRY_FMT:
                if (devices[index].flags & V4L2_DISABLE_CONVERSION) {
-                       result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT,
-                                          arg);
+                       result = devices[index].dev_ops->ioctl(
+                                       devices[index].dev_ops_priv,
+                                       fd, VIDIOC_TRY_FMT, arg);
                } else {
                        result = v4lconvert_try_format(devices[index].convert,
                                                       arg, NULL);
@@ -1030,8 +1073,9 @@ 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,
+                                       fd, VIDIOC_TRY_FMT, dest_fmt);
                        src_fmt = *dest_fmt;
                } else {
                        result = v4lconvert_try_format(devices[index].convert, dest_fmt,
@@ -1071,7 +1115,9 @@ 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,
+                               fd, VIDIOC_S_FMT, &src_fmt);
                if (result) {
                        saved_err = errno;
                        V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno));
@@ -1096,8 +1142,11 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
                        struct v4l2_streamparm parm = {
                                .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
                        };
-                       if (SYS_IOCTL(fd, VIDIOC_G_PARM, &parm) == 0)
-                               v4l2_update_fps(index, &parm);
+                       if (devices[index].dev_ops->ioctl(
+                                       devices[index].dev_ops_priv,
+                                       fd, VIDIOC_G_PARM, &parm))
+                               break;
+                       v4l2_update_fps(index, &parm);
                }
 
                break;
@@ -1129,7 +1178,9 @@ 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,
+                               fd, VIDIOC_REQBUFS, req);
                if (result < 0)
                        break;
                result = 0; /* some drivers return the number of buffers on success */
@@ -1150,7 +1201,9 @@ 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,
+                               fd, VIDIOC_QUERYBUF, buf);
                if (result || !v4l2_needs_conversion(index))
                        break;
 
@@ -1177,7 +1230,9 @@ 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,
+                               fd, VIDIOC_QBUF, arg);
                break;
 
        case VIDIOC_DQBUF: {
@@ -1190,7 +1245,9 @@ 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,
+                                       fd, VIDIOC_DQBUF, buf);
                        if (result) {
                                int saved_err = errno;
 
@@ -1261,7 +1318,9 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
                        v4l2_adjust_src_fmt_to_fps(index, fps);
                }
 
-               result = SYS_IOCTL(devices[index].fd, VIDIOC_S_PARM, parm);
+               result = devices[index].dev_ops->ioctl(
+                                               devices[index].dev_ops_priv,
+                                               fd, VIDIOC_S_PARM, parm);
                if (result)
                        break;
 
@@ -1270,7 +1329,9 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
        }
 
        default:
-               result = SYS_IOCTL(fd, request, arg);
+               result = devices[index].dev_ops->ioctl(
+                               devices[index].dev_ops_priv,
+                               fd, request, arg);
                break;
        }
 
@@ -1310,7 +1371,8 @@ static void v4l2_adjust_src_fmt_to_fps(int index, int fps)
                return;
 
        req_pix_fmt = src_fmt.fmt.pix;
-       if (SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT, &src_fmt))
+       if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+                       devices[index].fd, VIDIOC_S_FMT, &src_fmt))
                return;
 
        v4l2_set_src_and_dest_format(index, &src_fmt, &dest_fmt);
@@ -1337,7 +1399,8 @@ static void v4l2_adjust_src_fmt_to_fps(int index, int fps)
        src_fmt = orig_src_fmt;
        dest_fmt = orig_dest_fmt;
        req_pix_fmt = src_fmt.fmt.pix;
-       if (SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT, &src_fmt)) {
+       if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv,
+                       devices[index].fd, VIDIOC_S_FMT, &src_fmt)) {
                V4L2_LOG_ERR("restoring src fmt: %s\n", strerror(errno));
                return;
        }
@@ -1364,7 +1427,9 @@ 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 (file)
index 0000000..3c513a7
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+* 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 LIBDIR "/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);
+}
+
+const struct libv4l2_dev_ops libv4l2_default_dev_ops = {
+       .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,
+                     const struct libv4l2_dev_ops **dev_ops_ret)
+{
+       char *error;
+       int glob_ret, i;
+       void *plugin_library = NULL;
+       const struct libv4l2_dev_ops *libv4l2_plugin = NULL;
+       glob_t globbuf;
+
+       *dev_ops_ret = &libv4l2_default_dev_ops;
+       *plugin_lib_ret = NULL;
+       *plugin_priv_ret = NULL;
+
+       glob_ret = glob(PLUGINS_PATTERN, 0, NULL, &globbuf);
+
+       if (glob_ret == GLOB_NOSPACE)
+               return;
+
+       if (glob_ret == GLOB_ABORTED || glob_ret == GLOB_NOMATCH)
+               goto leave;
+
+       for (i = 0; i < globbuf.gl_pathc; i++) {
+               V4L2_LOG("PLUGIN: dlopen(%s);\n", globbuf.gl_pathv[i]);
+
+               plugin_library = dlopen(globbuf.gl_pathv[i], 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;
+               }
+
+               if (!libv4l2_plugin->init ||
+                   !libv4l2_plugin->close ||
+                   !libv4l2_plugin->ioctl ||
+                   !libv4l2_plugin->read) {
+                       V4L2_LOG("PLUGIN: does not have all functions\n");
+                       dlclose(plugin_library);
+                       continue;
+               }
+
+               *plugin_priv_ret = libv4l2_plugin->init(fd);
+               if (!*plugin_priv_ret) {
+                       V4L2_LOG("PLUGIN: plugin open() returned NULL\n");
+                       dlclose(plugin_library);
+                       continue;
+               }
+
+               *plugin_lib_ret = plugin_library;
+               *dev_ops_ret = libv4l2_plugin;
+               break;
+       }
+
+leave:
+       globfree(&globbuf);
+}
+
+void v4l2_plugin_cleanup(void *plugin_lib, void *plugin_priv,
+                        const struct libv4l2_dev_ops *dev_ops)
+{
+       if (plugin_lib) {
+               dev_ops->close(plugin_priv);
+               dlclose(plugin_lib);
+       }
+}
index 9e2f546..9b23228 100644 (file)
@@ -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);
index 4805507..39eb9c6 100644 (file)
@@ -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
@@ -38,6 +40,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;
+       void *dev_ops_priv;
+       const struct libv4l2_dev_ops *dev_ops;
 };
 
 struct v4lcontrol_flags_info {
index cafc02e..b11d96c 100644 (file)
@@ -805,7 +805,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,
+       const struct libv4l2_dev_ops *dev_ops, int always_needs_conversion)
 {
        int shm_fd;
        int i, rc, got_usb_info, speed, init = 0;
@@ -825,10 +826,14 @@ 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)
@@ -862,7 +867,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
@@ -870,8 +876,11 @@ 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);
-                       if (rc == -1 || (rc == 0 && (ctrl.flags & V4L2_CTRL_FLAG_DISABLED)))
+                       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;
                }
        }
@@ -882,17 +891,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;
 
@@ -909,7 +921,8 @@ 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;
        }
@@ -1095,7 +1108,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)) {
@@ -1132,7 +1146,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)
@@ -1153,7 +1168,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_bandwidth(struct v4lcontrol_data *data)
index 12a1d44..3d1a28d 100644 (file)
@@ -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,
+       const struct libv4l2_dev_ops *dev_ops, int always_needs_conversion);
 void v4lcontrol_destroy(struct v4lcontrol_data *data);
 
 int v4lcontrol_get_bandwidth(struct v4lcontrol_data *data);
index 2cd7a22..d183143 100644 (file)
@@ -71,6 +71,8 @@ struct v4lconvert_data {
        unsigned char *convert_pixfmt_buf;
        struct v4lcontrol_data *control;
        struct v4lprocessing_data *processing;
+       void *dev_ops_priv;
+       const struct libv4l2_dev_ops *dev_ops;
 
        /* Data for external decompression helpers code */
        pid_t decompress_pid;
index a15f234..cf0b0f1 100644 (file)
@@ -103,7 +103,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,
+               const struct libv4l2_dev_ops *dev_ops)
 {
        int i, j;
        struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data));
@@ -119,6 +120,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;
        data->fps = 30;
 
@@ -128,7 +131,8 @@ 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++)
@@ -147,7 +151,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;
 
@@ -155,7 +160,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;
@@ -221,7 +227,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) ||
@@ -385,7 +392,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))
                        continue;
 
                if (try_fmt.fmt.pix.pixelformat !=
@@ -459,7 +467,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;
@@ -1316,7 +1325,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 */
@@ -1376,7 +1386,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) {
@@ -1412,7 +1423,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;
@@ -1457,7 +1469,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;
index 4692f1f..4102939 100644 (file)
@@ -47,6 +47,7 @@
 #include <sys/mman.h>
 #include <errno.h>
 #include <dirent.h>
+#include <libv4l2.h>
 
 ApplicationWindow::ApplicationWindow() :
        m_capture(NULL),
@@ -144,7 +145,8 @@ 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,
+                                         &libv4l2_default_dev_ops);
        m_capStartAct->setEnabled(fd() >= 0);
 }