new file mode 100644
@@ -0,0 +1,74 @@
+/*
+* 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>
+
+/* 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/mmap/munmap) on the
+ actual /dev/video node in question. When called the plugin can then choose
+ to do one of the following:
+ 1. Pass the call unmodified to the fd, and return the return value unmodifed
+ 2. Modify some arguments in the call and pass it through
+ 3. Modify the return(ed) value(s) of a passed through call
+ 4. Not do any operation on the fd at all but instead completely fake it
+ (which opens the possibility for "fake" v4l devices)
+
+ libv4l2 plugins should *never* use any global variables. All data should be
+ bound to the specific fd to which the plugin is bound. This ensures that for
+ example a plugin for a specific type of usb webcam will also work when 2
+ identical cameras are plugged into a system (and both are used from the same
+ process).
+
+ A libv4l2 plugin can register plugin private data using:
+ void libv4l2_set_plugindata(int fd, void *plugin_data);
+
+ And can get this data out of libv4l2 again inside a callback using:
+ void *libv4l2_get_plugindata(int fd);
+
+ Note that a plugin should call libv4l2_set_plugindata only once per fd !
+ Calling it a second time will overwrite the previous value. The logical
+ place to use libv4l2_set_plugindata is from the plugin's open callback.
+*/
+
+/* Plugin callback function struct */
+struct libv4l2_plugin_data {
+ int (*open)(const char *file, int oflag, ...);
+ int (*close)(int fd);
+ int (*ioctl)(int fd, unsigned long int request, ...);
+ ssize_t (*read)(int fd, void *buffer, size_t n);
+ void *(*mmap)(void *start, size_t length, int prot, int flags,
+ int fd, int64_t offset);
+ /* Note as munmap has no fd argument, defining a callback for munmap
+ will result in it getting called for *any* call to v4l2_munmap.
+ So if a plugin defines a callback for munmap (because for
+ example it returns fake mmap buffers from its mmap callback).
+ Then it must keep track of the addresses at which these buffers
+ live and their size and check the munmap arguments to see if the
+ munmap call was meant for it. */
+ int (*munmap)(void *_start, size_t length);
+};
+
+/* Plugin utility functions */
+void libv4l2_set_plugindata(int fd, void *plugin_data);
+void *libv4l2_get_plugindata(int fd);
+
+#endif
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
+#include "libv4l2-plugin.h"
#ifdef __cplusplus
extern "C" {
@@ -108,6 +109,20 @@ LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid);
(note the fd is left open in this case). */
LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags);
+
+LIBV4L_PUBLIC int v4l2_plugin_open(int *plugin_used, const char *file,
+ int oflag, ...);
+LIBV4L_PUBLIC int v4l2_plugin_close(int *plugin_used, int fd);
+LIBV4L_PUBLIC int v4l2_plugin_ioctl(int *plugin_used, int fd,
+ unsigned long int request, ...);
+LIBV4L_PUBLIC ssize_t v4l2_plugin_read(int *plugin_used, int fd, void *dest,
+ size_t n);
+LIBV4L_PUBLIC void *v4l2_plugin_mmap(int *plugin_used, void *start,
+ size_t length, int prot, int flags,
+ int fd, int64_t offset);
+LIBV4L_PUBLIC int v4l2_plugin_munmap(int *plugin_used, void *_start,
+ size_t length);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -1,8 +1,8 @@
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
@@ -87,6 +87,15 @@ struct v4l2_dev_info {
unsigned char *readbuf;
};
+struct v4l2_plugin_info {
+ int fd;
+ void *plugin_library;
+ struct libv4l2_plugin_data *libv4l2_plugin;
+ unsigned char *frame_pointers[V4L2_MAX_NO_FRAMES];
+ int frame_sizes[V4L2_MAX_NO_FRAMES];
+ void *plugin_data;
+};
+
/* From log.c */
void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
@@ -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
@@ -522,7 +523,24 @@ static int v4l2_buffers_mapped(int index)
int v4l2_open(const char *file, int oflag, ...)
{
- int fd;
+ int fd, plugin_used;
+
+ if (oflag & O_CREAT) {
+ va_list ap;
+ mode_t mode;
+
+ va_start(ap, oflag);
+ mode = va_arg(ap, mode_t);
+
+ fd = v4l2_plugin_open(&plugin_used, file, oflag, mode);
+
+ va_end(ap);
+ } else {
+ fd = v4l2_plugin_open(&plugin_used, file, oflag, 0);
+ }
+
+ if (plugin_used)
+ return fd;
/* original open code */
if (oflag & O_CREAT) {
@@ -684,7 +702,12 @@ static int v4l2_get_index(int fd)
int v4l2_close(int fd)
{
- int index, result;
+ int index, result, plugin_used;
+
+ result = v4l2_plugin_close(&plugin_used, fd);
+
+ if (plugin_used)
+ return result;
index = v4l2_get_index(fd);
if (index == -1)
@@ -806,13 +829,18 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
{
void *arg;
va_list ap;
- int result, index, saved_err;
+ int result, index, saved_err, plugin_used;
int is_capture_request = 0, stream_needs_locking = 0;
va_start(ap, request);
arg = va_arg(ap, void *);
va_end(ap);
+ result = v4l2_plugin_ioctl(&plugin_used, fd, request, arg);
+
+ if (plugin_used)
+ return result;
+
index = v4l2_get_index(fd);
if (index == -1)
return SYS_IOCTL(fd, request, arg);
@@ -1205,9 +1233,14 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
ssize_t v4l2_read(int fd, void *dest, size_t n)
{
ssize_t result;
- int saved_errno;
+ int saved_errno, plugin_used;
int index = v4l2_get_index(fd);
+ result = v4l2_plugin_read(&plugin_used, fd, dest, n);
+
+ if (plugin_used)
+ return result;
+
if (index == -1)
return SYS_READ(fd, dest, n);
@@ -1264,10 +1297,16 @@ leave:
void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd,
int64_t offset)
{
- int index;
+ int index, plugin_used;
unsigned int buffer_index;
void *result;
+ result = v4l2_plugin_mmap(&plugin_used, start, length, prot,
+ flags, fd, offset);
+
+ if (plugin_used)
+ return result;
+
index = v4l2_get_index(fd);
if (index == -1 ||
/* Check if the mmap data matches our answer to QUERY_BUF, if it doesn't
@@ -1329,10 +1368,15 @@ leave:
int v4l2_munmap(void *_start, size_t length)
{
- int index;
+ int index, result, plugin_used;
unsigned int buffer_index;
unsigned char *start = _start;
+ result = v4l2_plugin_munmap(&plugin_used, _start, length);
+
+ if (plugin_used)
+ return result;
+
/* Is this memory ours? */
if (start != MAP_FAILED && length == V4L2_FRAME_BUF_SIZE) {
for (index = 0; index < devices_used; index++)
new file mode 100644
@@ -0,0 +1,399 @@
+/*
+* 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 pthread_mutex_t v4l2_plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static struct v4l2_plugin_info plugins[V4L2_MAX_DEVICES];
+
+int v4l2_plugin_open(int *plugin_used, const char *file, int oflag, ...)
+{
+ char *error;
+ int index, fd = -1, glob_ret, plugin_num, i;
+ void *plugin_library = NULL;
+ struct libv4l2_plugin_data *libv4l2_plugin = NULL;
+ glob_t globbuf;
+ static int structs_initialized = 0;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ if (!structs_initialized) {
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ plugins[index].fd = -1;
+
+ structs_initialized = 1;
+ }
+
+ /* Check if there is empty slot for plugin */
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == -1)
+ break;
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ if (index == V4L2_MAX_DEVICES) {
+ V4L2_LOG_ERR("attempting to open more than %d libv4l plugins\n",
+ V4L2_MAX_DEVICES);
+ *plugin_used = 0;
+ return -1;
+ }
+
+ glob_ret = glob(PLUGINS_PATTERN, 0, NULL, &globbuf);
+
+ if (glob_ret == GLOB_NOSPACE) {
+ *plugin_used = 0;
+ return -1;
+ }
+
+ if (glob_ret == GLOB_ABORTED || glob_ret == GLOB_NOMATCH) {
+ *plugin_used = 0;
+ 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_plugin_data *)
+ 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->open == NULL) {
+ fd = -1;
+ } else {
+ if (oflag & O_CREAT) {
+ va_list ap;
+ mode_t mode;
+
+ va_start(ap, oflag);
+ mode = va_arg(ap, mode_t);
+
+ fd = libv4l2_plugin->open(file, oflag, mode);
+
+ va_end(ap);
+ } else {
+ fd = libv4l2_plugin->open(file, oflag, 0);
+ }
+ }
+
+ if (fd != -1) {
+ V4L2_LOG("PLUGIN: plugin open() returned %d\n", fd);
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ /* The plugin may have called libv4l2_set_plugindata()
+ and there is already reserved slot with that fd */
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == fd)
+ break;
+
+ /* There is no such fd */
+ if (index == V4L2_MAX_DEVICES)
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == -1)
+ break;
+
+ plugins[index].fd = fd;
+ plugins[index].plugin_library = plugin_library;
+ plugins[index].libv4l2_plugin = libv4l2_plugin;
+ for (i = 0; i < V4L2_MAX_NO_FRAMES; i++) {
+ plugins[index].frame_pointers[i] = MAP_FAILED;
+ plugins[index].frame_sizes[i] = 0;
+ }
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+ break;
+ } else {
+ V4L2_LOG("PLUGIN: plugin open() returned -1\n");
+ dlclose(plugin_library);
+ plugin_library = NULL;
+ }
+
+ }
+
+leave:
+ globfree(&globbuf);
+
+ if (fd == -1)
+ *plugin_used = 0;
+ else
+ *plugin_used = 1;
+
+ return fd;
+}
+
+int v4l2_plugin_close(int *plugin_used, int fd)
+{
+ int index, result = -1;
+ void *plugin_library = NULL;
+ struct libv4l2_plugin_data *libv4l2_plugin = NULL;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == fd) {
+ plugin_library = plugins[index].plugin_library;
+ libv4l2_plugin = plugins[index].libv4l2_plugin;
+ break;
+ }
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ if (index == V4L2_MAX_DEVICES) {
+ *plugin_used = 0;
+ } else {
+ if (libv4l2_plugin->close == NULL) {
+ *plugin_used = 0;
+ } else {
+ result = libv4l2_plugin->close(fd);
+ *plugin_used = 1;
+ }
+
+ dlclose(plugin_library);
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ plugins[index].fd = -1;
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+ }
+
+ return result;
+}
+
+int v4l2_plugin_ioctl(int *plugin_used, int fd, unsigned long int request, ...)
+{
+ void *arg;
+ va_list ap;
+ int index, result = -1;
+ void *plugin_library = NULL;
+ struct libv4l2_plugin_data *libv4l2_plugin = NULL;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == fd) {
+ plugin_library = plugins[index].plugin_library;
+ libv4l2_plugin = plugins[index].libv4l2_plugin;
+ break;
+ }
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ if (index == V4L2_MAX_DEVICES || libv4l2_plugin->ioctl == NULL) {
+ *plugin_used = 0;
+ } else {
+ va_start(ap, request);
+ arg = va_arg(ap, void *);
+ va_end(ap);
+
+ result = libv4l2_plugin->ioctl(fd, request, arg);
+ *plugin_used = 1;
+ }
+
+ return result;
+}
+
+ssize_t v4l2_plugin_read(int *plugin_used, int fd, void *dest, size_t n)
+{
+ int index, result = -1;
+ void *plugin_library = NULL;
+ struct libv4l2_plugin_data *libv4l2_plugin = NULL;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == fd) {
+ plugin_library = plugins[index].plugin_library;
+ libv4l2_plugin = plugins[index].libv4l2_plugin;
+ break;
+ }
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ if (index == V4L2_MAX_DEVICES || libv4l2_plugin->read == NULL) {
+ *plugin_used = 0;
+ } else {
+ result = libv4l2_plugin->read(fd, dest, n);
+ *plugin_used = 1;
+ }
+
+ return result;
+}
+
+void *v4l2_plugin_mmap(int *plugin_used, void *start, size_t length, int prot,
+ int flags, int fd, int64_t offset)
+{
+ int index, i;
+ void *result = NULL;
+ void *plugin_library = NULL;
+ struct libv4l2_plugin_data *libv4l2_plugin = NULL;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == fd) {
+ plugin_library = plugins[index].plugin_library;
+ libv4l2_plugin = plugins[index].libv4l2_plugin;
+ break;
+ }
+
+ if (fd == -1 || index == V4L2_MAX_DEVICES
+ || libv4l2_plugin->mmap == NULL) {
+ *plugin_used = 0;
+ } else {
+ for (i = 0; i < V4L2_MAX_NO_FRAMES; i++)
+ if (plugins[index].frame_pointers[i] == MAP_FAILED)
+ break;
+
+ if (i == V4L2_MAX_NO_FRAMES) {
+ *plugin_used = 0;
+ result = NULL;
+ goto leave;
+ }
+
+ result = libv4l2_plugin->mmap(start, length, prot,
+ flags, fd, offset);
+ if (result) {
+ plugins[index].frame_pointers[i] = result;
+ plugins[index].frame_sizes[i] = length;
+ *plugin_used = 1;
+ } else {
+ *plugin_used = 0;
+ }
+ }
+
+leave:
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ return result;
+}
+
+int v4l2_plugin_munmap(int *plugin_used, void *_start, size_t length)
+{
+ int index, map, result = 0;
+ void *plugin_library = NULL;
+ struct libv4l2_plugin_data *libv4l2_plugin = NULL;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ for (index = 0; index < V4L2_MAX_DEVICES; index++) {
+ for (map = 0; map < V4L2_MAX_NO_FRAMES; map++) {
+ if (plugins[index].frame_pointers[map] == _start &&
+ plugins[index].frame_sizes[map] == length) {
+
+ plugin_library = plugins[index].plugin_library;
+ libv4l2_plugin = plugins[index].libv4l2_plugin;
+ plugins[index].frame_pointers[map] = MAP_FAILED;
+ plugins[index].frame_sizes[map] = 0;
+ break;
+ }
+ }
+ if (plugin_library)
+ break;
+ }
+
+ if (plugin_library) {
+ result = libv4l2_plugin->munmap(_start, length);
+
+ if (result)
+ *plugin_used = 0;
+ else
+ *plugin_used = 1;
+
+ } else {
+ *plugin_used = 0;
+ }
+
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ return result;
+}
+
+LIBV4L_PUBLIC void libv4l2_set_plugindata(int fd, void *plugin_data)
+{
+ int index;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == fd)
+ break;
+
+ /* We have no info about this fd - reserve an empty slot */
+ if (index == V4L2_MAX_DEVICES) {
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == -1)
+ break;
+ }
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ if (index == V4L2_MAX_DEVICES) {
+ V4L2_LOG_ERR("store private data for more than %d plugins\n",
+ V4L2_MAX_DEVICES);
+ return;
+ }
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ plugins[index].fd = fd;
+ plugins[index].plugin_data = plugin_data;
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+}
+
+LIBV4L_PUBLIC void *libv4l2_get_plugindata(int fd)
+{
+ int index;
+ void *result = NULL;
+
+ pthread_mutex_lock(&v4l2_plugin_mutex);
+ for (index = 0; index < V4L2_MAX_DEVICES; index++)
+ if (plugins[index].fd == fd) {
+ result = plugins[index].plugin_data;
+ break;
+ }
+ pthread_mutex_unlock(&v4l2_plugin_mutex);
+
+ return result;
+}
@@ -31,6 +31,7 @@
#include "../libv4lconvert/libv4lsyscall-priv.h"
#include <linux/videodev2.h>
#include <libv4l2.h>
+#include "libv4l2-plugin.h"
/* Check that open/read/mmap is not a define */
#if defined open || defined read || defined mmap
@@ -45,10 +46,27 @@
LIBV4L_PUBLIC int open(const char *file, int oflag, ...)
{
- int fd;
+ int fd, plugin_used;
struct v4l2_capability cap;
int v4l_device = 0;
+ if (oflag & O_CREAT) {
+ va_list ap;
+ mode_t mode;
+
+ va_start(ap, oflag);
+ mode = va_arg(ap, mode_t);
+
+ fd = v4l2_plugin_open(&plugin_used, file, oflag, mode);
+
+ va_end(ap);
+ } else {
+ fd = v4l2_plugin_open(&plugin_used, file, oflag, 0);
+ }
+
+ if (plugin_used)
+ return fd;
+
/* check if we're opening a video4linux2 device */
if (!strncmp(file, "/dev/video", 10) || !strncmp(file, "/dev/v4l/", 9)) {
/* Some apps open the device read only, but we need rw rights as the