v4l2-library: libv4l2 and v4l2convert
authorThierry MERLE <thierry.merle@free.fr>
Tue, 1 Jul 2008 19:11:29 +0000 (21:11 +0200)
committerThierry MERLE <thierry.merle@free.fr>
Tue, 1 Jul 2008 19:11:29 +0000 (21:11 +0200)
From: Hans de Goede <j.w.r.degoede at hhs.nl>

libv4l2 offers v4l2_ prefixed versions of
   open/close/etc. The API is 100% the same as directly opening /dev/videoX
   using regular open/close/etc, the big difference is 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,
   then the try_fmt / set_fmt will succeed as if the cam supports the format
   and on dqbuf / read the data will be converted for you and returned in
   the request format.
v4l2convert: open/close/ioctl/mmap/munmap library call wrapper doing format conversion
   for v4l2 applications which want to be able to simply capture bgr24 / yuv420
   from v4l2 devices with more exotic frame formats.

Signed-off-by: Hans de Goede <j.w.r.degoede at hhs.nl>
Signed-off-by: Thierry MERLE <thierry.merle@free.fr>
lib/include/libv4l2.h [new file with mode: 0644]
lib/libv4l2/Makefile [new file with mode: 0644]
lib/libv4l2/libv4l2-priv.h [new file with mode: 0644]
lib/libv4l2/libv4l2.c [new file with mode: 0644]
lib/libv4l2/log.c [new file with mode: 0644]
lib/libv4l2/v4l2convert.c [new file with mode: 0644]

diff --git a/lib/include/libv4l2.h b/lib/include/libv4l2.h
new file mode 100644 (file)
index 0000000..0e88039
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+#             (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+# 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 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_H
+#define __LIBV4L2_H
+
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Point this to a FILE opened for writing when you want to log error and
+   status messages to a file, when NULL errors will get send to stderr */
+extern FILE *v4l2_log_file;
+
+/* 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,
+   then the try_fmt / set_fmt will succeed as if the cam supports the format
+   and on dqbuf / read the data will be converted for you and returned in
+   the request format.
+
+   Another difference is that you can make v4l2_read() calls even on devices
+   which do not support the regular read() method.
+
+   Note that libv4l2 normally does not interfere with enum_fmt, so enum_fmt
+   will still return the actual formats the hardware supports, and not any
+   formats which may be emulated on top of that. If you pass the
+   V4L2_ENABLE_ENUM_FMT_EMULATION flag to v4l2_fd_open (as the v4l2convert.so
+   wrapper does) then enum_fmt will also report support for the formats to
+   which conversion is possible.
+
+   Note the device name passed to v4l2_open must be of a video4linux2 device,
+   if it is anything else (including a video4linux1 device), v4l2_open will
+   fail.
+*/
+
+int v4l2_open (const char *file, int oflag, ...);
+int v4l2_close(int fd);
+int v4l2_dup(int fd);
+int v4l2_ioctl (int fd, unsigned long int request, ...);
+ssize_t v4l2_read (int fd, void* buffer, size_t n);
+void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd,
+  off_t offset);
+int v4l2_munmap(void *_start, size_t length);
+
+
+/* Misc utility functions */
+
+/* This function takes a value of 0 - 65535, and then scales that range to
+   the actual range of the given v4l control id, and then if the cid exists
+   and is not locked sets the cid to the scaled value.
+
+   Normally returns 0, even if the cid did not exist or was locked, returns
+   non 0 when an other error occured. */
+int v4l2_set_control(int fd, int cid, int value);
+
+/* This function returns a value of 0 - 65535, scaled to from the actual range
+   of the given v4l control id. when the cid does not exist, could not be
+   accessed for some reason, or some error occured 0 is returned. */
+int v4l2_get_control(int fd, int cid);
+
+
+/* "low level" access functions, these functions allow somewhat lower level
+   access to libv4l2 (currently there only is v4l2_fd_open here) */
+
+/* Flags for v4l2_fd_open's v4l2_flags argument */
+
+/* Disable all format conversion done by libv4l2 (reduces libv4l2 functionality
+   to offering v4l2_read() even on devices which don't implement read()) */
+#define V4L2_DISABLE_CONVERSION 0x01
+/* Report not only real but also emulated formats with the ENUM_FMT ioctl */
+#define V4L2_ENABLE_ENUM_FMT_EMULATION 02
+
+/* v4l2_fd_open: open an already opened fd for further use through
+   v4l2lib and possibly modify libv4l2's default behavior through the
+   v4l2_flags argument.
+
+   Returns fd on success, -1 if the fd is not suitable for use through libv4l2
+   (note the fd is left open in this case). */
+int v4l2_fd_open(int fd, int v4l2_flags);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/lib/libv4l2/Makefile b/lib/libv4l2/Makefile
new file mode 100644 (file)
index 0000000..c546380
--- /dev/null
@@ -0,0 +1,52 @@
+CC            = gcc
+LD            = gcc
+
+CPPFLAGS      = -fPIC -I../include -I../../../../linux/include
+
+CFLAGS := -g -O1
+CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes
+
+LDFLAGS       = -shared
+
+V4L2_OBJS     = libv4l2.o log.o ../libv4lconvert/libv4lconvert.so
+V4L2_LIB      = libv4l2.so
+V4L2CONVERT   = v4l2convert.so
+V4L2CONVERT_O = v4l2convert.o libv4l2.so
+TARGETS       = $(V4L2_LIB) $(V4L2CONVERT)
+INCLUDES      = ../include/libv4l2.h
+
+ifeq ($(LIB_RELEASE),)
+LIB_RELEASE = 0
+endif
+
+ifeq ($(DESTDIR),)
+DESTDIR = /usr/local
+endif
+
+all: $(TARGETS)
+
+$(V4L2_LIB): $(V4L2_OBJS)
+
+$(V4L2CONVERT): $(V4L2CONVERT_O) $(V4L2_LIB)
+
+install: all
+       mkdir -p $(DESTDIR)/include
+       cp $(INCLUDES) $(DESTDIR)/include
+       mkdir -p $(DESTDIR)/lib
+       cp $(V4L2_LIB).$(LIB_RELEASE) $(DESTDIR)/lib
+       cd $(DESTDIR)/lib && \
+         ln -f -s $(V4L2_LIB).$(LIB_RELEASE) $(V4L2_LIB)
+       cp $(V4L2CONVERT).$(LIB_RELEASE) $(DESTDIR)/lib
+       cd $(DESTDIR)/lib && \
+         ln -f -s $(V4L2CONVERT).$(LIB_RELEASE) $(V4L2CONVERT)
+
+clean::
+       rm -f *.so* *.o log *~
+       rm -f *.d
+
+%.o: %.c
+       $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $<
+
+%.so:
+       $(CC) $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^
+       ln -f -s $@.$(LIB_RELEASE) $@
diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
new file mode 100644 (file)
index 0000000..bdbd45e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+#             (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+# 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 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_PRIV_H
+#define __LIBV4L2_PRIV_H
+
+#include <stdio.h>
+#include <pthread.h>
+#include <libv4lconvert.h> /* includes videodev2.h for us */
+
+#define V4L2_MAX_DEVICES 16
+/* Warning when making this larger the frame_queued and frame_mapped members of
+   the v4l2_dev_info struct can no longer be a bitfield, so the code needs to
+   be adjusted! */
+#define V4L2_MAX_NO_FRAMES 32u
+#define V4L2_DEFAULT_NREADBUFFERS 4
+#define V4L2_FRAME_BUF_SIZE (4096 * 4096)
+
+#define V4L2_LOG_ERR(...) \
+  do { \
+    if (v4l2_log_file) { \
+      fprintf(v4l2_log_file, "libv4l2: error " __VA_ARGS__); \
+      fflush(v4l2_log_file); \
+    } else \
+      fprintf(stderr, "libv4l2: error " __VA_ARGS__); \
+  } while(0)
+
+#define V4L2_LOG_WARN(...) \
+  do { \
+    if (v4l2_log_file) { \
+      fprintf(v4l2_log_file, "libv4l2: warning " __VA_ARGS__); \
+      fflush(v4l2_log_file); \
+    } else \
+      fprintf(stderr, "libv4l2: warning " __VA_ARGS__); \
+  } while(0)
+
+#define V4L2_LOG(...) \
+  do { \
+    if (v4l2_log_file) { \
+      fprintf(v4l2_log_file, "libv4l2: " __VA_ARGS__); \
+      fflush(v4l2_log_file); \
+    } \
+  } while(0)
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+enum v4l2_io { v4l2_io_none, v4l2_io_read, v4l2_io_mmap };
+
+struct v4l2_dev_info {
+  int fd;
+  int flags;
+  int open_count;
+  /* actually format of the cam */
+  struct v4l2_format src_fmt;
+  /* fmt as seen by the application (iow after conversion) */
+  struct v4l2_format dest_fmt;
+  pthread_mutex_t stream_lock;
+  unsigned int no_frames;
+  unsigned int nreadbuffers;
+  enum v4l2_io io;
+  struct v4lconvert_data *convert;
+  unsigned char *convert_mmap_buf;
+  /* Frame bookkeeping is only done when in read or mmap-conversion mode */
+  unsigned char *frame_pointers[V4L2_MAX_NO_FRAMES];
+  int frame_sizes[V4L2_MAX_NO_FRAMES];
+  int frame_queued; /* 1 status bit per frame */
+  /* mapping tracking of our fake (converting mmap) frame buffers, todo this
+     perfect we should use a map counter per frame, this is a good
+     approximation but there are scenarios thinkable where this doesn't work.
+     However no normal application not even a buggy one is likely to exhibit
+     the patterns needed to fail this somewhat simplified tracking */
+  int frame_mapped; /* 1 status bit per frame */
+  int frame_map_count; /* total number of maps of (fake) buffers combined */
+};
+
+/* From log.c */
+void v4l2_log_ioctl(unsigned long int request, void *arg, int result);
+
+#endif
diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
new file mode 100644 (file)
index 0000000..33d9b28
--- /dev/null
@@ -0,0 +1,1060 @@
+/*
+#             (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+# 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 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
+*/
+
+/* MAKING CHANGES TO THIS FILE??   READ THIS FIRST!!!
+
+   This file implements libv4l2, which offers v4l2_ prefixed versions of
+   open/close/etc. The API is 100% the same as directly opening /dev/videoX
+   using regular open/close/etc, the big difference is 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,
+   then the try_fmt / set_fmt will succeed as if the cam supports the format
+   and on dqbuf / read the data will be converted for you and returned in
+   the request format.
+
+   Important note to people making changes to this file: All functions
+   (v4l2_close, v4l2_ioctl, etc.) are designed to function as their regular
+   counterpart when they get passed a fd that is not "registered" by libv4l2,
+   there are 2 reasons for this:
+   1) This allows us to get completely out of the way when dealing with non
+      capture devices.
+   2) libv4l2 is the base of the v4l2convert.so wrapper lib, which is a .so
+      which can be LD_PRELOAD-ed and the overrules the libc's open/close/etc,
+      and when opening /dev/videoX calls v4l2_open. Because we behave as the
+      regular counterpart when the fd is not known (instead of say throwing
+      an error), v4l2convert.so can simply call the v4l2_ prefixed function
+      for all wrapped functions (except for v4l2_open which will fail when not
+      called on a v4l2 device). This way the wrapper does not have to keep
+      track of which fd's are being handled by libv4l2, as libv4l2 already
+      keeps track of this itself.
+
+      This also means that libv4l2 may not use any of the regular functions
+      it mimics, as for example open could be a symbol in v4l2convert.so, which
+      in turn will call v4l2_open, so therefor v4l2_open (for example) may not
+      use the regular open()!
+
+   Another important note: libv4l2 does conversion for capture usage only, if
+   any calls are made which are passed a v4l2_buffer or v4l2_format with a
+   v4l2_buf_type which is different from V4L2_BUF_TYPE_VIDEO_CAPTURE, then
+   the v4l2_ methods behave exactly the same as their regular counterparts.
+   When modifications are made, one should be carefull that this behavior is
+   preserved.
+*/
+
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include "libv4l2.h"
+#include "libv4l2-priv.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
+   use the same bits! */
+#define V4L2_STREAMON        0x0100
+
+#define V4L2_MMAP_OFFSET_MAGIC      0xABCDEF00u
+
+static pthread_mutex_t v4l2_open_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = { {-1}, {-1},
+    {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+    {-1}, {-1}};
+static int devices_used = 0;
+
+
+static int v4l2_request_read_buffers(int index)
+{
+  int result;
+  struct v4l2_requestbuffers req;
+
+  /* No-op if already done */
+  if (devices[index].no_frames)
+    return 0;
+
+  /* Request buffers */
+  req.count = devices[index].nreadbuffers;
+  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  req.memory = V4L2_MEMORY_MMAP;
+  if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req))){
+    int saved_err = errno;
+    V4L2_LOG_ERR("requesting buffers: %s\n", strerror(errno));
+    errno = saved_err;
+    return result;
+  }
+
+  devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES);
+  return 0;
+}
+
+static int v4l2_unrequest_read_buffers(int index)
+{
+  int result;
+  struct v4l2_requestbuffers req;
+
+  /* No-op of already done */
+  if (devices[index].no_frames == 0)
+    return 0;
+
+  /* (Un)Request buffers */
+  req.count = 0;
+  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  req.memory = V4L2_MEMORY_MMAP;
+  if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req))) {
+    int saved_err = errno;
+    V4L2_LOG_ERR("unrequesting buffers: %s\n", strerror(errno));
+    errno = saved_err;
+    return result;
+  }
+
+  devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES);
+  if (devices[index].no_frames) {
+    V4L2_LOG_ERR("number of buffers > 0 after requesting 0 buffers\n");
+    errno = EBUSY;
+    return -1;
+  }
+  return 0;
+}
+
+static int v4l2_map_buffers(int index)
+{
+  int result = 0;
+  unsigned int i;
+  struct v4l2_buffer buf;
+
+  for (i = 0; i < devices[index].no_frames; i++) {
+    if (devices[index].frame_pointers[i] != MAP_FAILED)
+      continue;
+
+    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buf.memory = V4L2_MEMORY_MMAP;
+    buf.index = i;
+    result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf);
+    if (result) {
+      int saved_err = errno;
+      V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno));
+      errno = saved_err;
+      break;
+    }
+
+    devices[index].frame_pointers[i] = mmap64(NULL, buf.length,
+      PROT_READ, MAP_SHARED, devices[index].fd, buf.m.offset);
+    if (devices[index].frame_pointers[i] == MAP_FAILED) {
+      int saved_err = errno;
+      V4L2_LOG_ERR("mmapping buffer %u: %s\n", i, strerror(errno));
+      errno = saved_err;
+      result = -1;
+      break;
+    }
+    V4L2_LOG("mapped buffer %u at %p\n", i,
+      devices[index].frame_pointers[i]);
+
+    devices[index].frame_sizes[i] = buf.length;
+  }
+
+  return result;
+}
+
+static void v4l2_unmap_buffers(int index)
+{
+  unsigned int i;
+
+  /* unmap the buffers */
+  for (i = 0; i < devices[index].no_frames; i++) {
+    if (devices[index].frame_pointers[i] != MAP_FAILED) {
+      syscall(SYS_munmap, devices[index].frame_pointers[i],
+             devices[index].frame_sizes[i]);
+      devices[index].frame_pointers[i] = MAP_FAILED;
+      V4L2_LOG("unmapped buffer %u\n", i);
+    }
+  }
+}
+
+static int v4l2_streamon(int index)
+{
+  int result;
+  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+  if (!(devices[index].flags & V4L2_STREAMON)) {
+    if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMON,
+                         &type))) {
+      int saved_err = errno;
+      V4L2_LOG_ERR("turning on stream: %s\n", strerror(errno));
+      errno = saved_err;
+      return result;
+    }
+    devices[index].flags |= V4L2_STREAMON;
+  }
+
+  return 0;
+}
+
+static int v4l2_streamoff(int index)
+{
+  int result;
+  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+  if (devices[index].flags & V4L2_STREAMON) {
+    if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMOFF,
+                         &type))) {
+      int saved_err = errno;
+      V4L2_LOG_ERR("turning off stream: %s\n", strerror(errno));
+      errno = saved_err;
+      return result;
+    }
+    devices[index].flags &= ~V4L2_STREAMON;
+
+    /* Stream off also unqueues all our buffers! */
+    devices[index].frame_queued = 0;
+  }
+
+  return 0;
+}
+
+static int v4l2_queue_read_buffer(int index, int buffer_index)
+{
+  int result;
+  struct v4l2_buffer buf;
+
+  if (devices[index].frame_queued & (1 << buffer_index))
+    return 0;
+
+  buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  buf.memory = V4L2_MEMORY_MMAP;
+  buf.index  = buffer_index;
+  if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, &buf))) {
+    int saved_err = errno;
+    V4L2_LOG_ERR("queuing buf %d: %s\n", buffer_index, strerror(errno));
+    errno = saved_err;
+    return result;
+  }
+
+  devices[index].frame_queued |= 1 << buffer_index;
+  return 0;
+}
+
+static int v4l2_dequeue_read_buffer(int index, int *bytesused)
+{
+  int result;
+  struct v4l2_buffer buf;
+
+  buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  buf.memory = V4L2_MEMORY_MMAP;
+  if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, &buf))) {
+    int saved_err = errno;
+    V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno));
+    errno = saved_err;
+    return result;
+  }
+
+  devices[index].frame_queued &= ~(1 << buf.index);
+  *bytesused = buf.bytesused;
+  return buf.index;
+}
+
+static int v4l2_queue_read_buffers(int index)
+{
+  unsigned int i;
+  int last_error = EIO, queued = 0;
+
+  for (i = 0; i < devices[index].no_frames; i++) {
+    /* Don't queue unmapped buffers (should never happen) */
+    if (devices[index].frame_pointers[i] != MAP_FAILED) {
+      if (v4l2_queue_read_buffer(index, i)) {
+       last_error = errno;
+       continue;
+      }
+      queued++;
+    }
+  }
+
+  if (!queued) {
+    errno = last_error;
+    return -1;
+  }
+  return 0;
+}
+
+static int v4l2_activate_read_stream(int index)
+{
+  int result;
+
+  if ((result = v4l2_request_read_buffers(index)))
+    return result;
+
+  if ((result = v4l2_map_buffers(index)))
+    return result;
+
+  if ((result = v4l2_queue_read_buffers(index)))
+    return result;
+
+  return result = v4l2_streamon(index);
+}
+
+static int v4l2_deactivate_read_stream(int index)
+{
+  int result;
+
+  if ((result = v4l2_streamoff(index)))
+    return result;
+
+  /* No need to unqueue our buffers, streamoff does that for us */
+
+  v4l2_unmap_buffers(index);
+
+  if ((result = v4l2_unrequest_read_buffers(index)))
+    return result;
+
+  return 0;
+}
+
+
+int v4l2_open (const char *file, int oflag, ...)
+{
+  int fd;
+
+  /* original open code */
+  if (oflag & O_CREAT)
+  {
+    va_list ap;
+    mode_t mode;
+
+    va_start (ap, oflag);
+    mode = va_arg (ap, mode_t);
+
+    fd = syscall(SYS_open, file, oflag, mode);
+
+    va_end(ap);
+  }
+  else
+    fd = syscall(SYS_open, file, oflag);
+  /* end of original open code */
+
+  if (fd == -1)
+    return fd;
+
+  if (v4l2_fd_open(fd, 0) == -1) {
+    int saved_err = errno;
+    syscall(SYS_close, fd);
+    errno = saved_err;
+    return -1;
+  }
+
+  return fd;
+}
+
+int v4l2_fd_open(int fd, int v4l2_flags)
+{
+  int i, index;
+  char *lfname;
+  struct v4l2_capability cap;
+  struct v4l2_format fmt;
+  struct v4lconvert_data *convert;
+
+  /* If no log file was set by the app, see if one was specified through the
+     environment */
+  if (!v4l2_log_file && (lfname = getenv("LIBV4L2_LOG_FILENAME")))
+    v4l2_log_file = fopen(lfname, "w");
+
+  /* check that this is an v4l2 device */
+  if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap)) {
+    int saved_err = errno;
+    V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno));
+    errno = saved_err;
+    return -1;
+  }
+
+  /* we only add functionality for video capture devices */
+  if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+    return fd;
+
+  /* Get current cam format */
+  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  if (syscall(SYS_ioctl, fd, VIDIOC_G_FMT, &fmt)) {
+    int saved_err = errno;
+    V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno));
+    errno = saved_err;
+    return -1;
+  }
+
+  /* init libv4lconvert */
+  if (!(convert = v4lconvert_create(fd)))
+    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;
+      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);
+    errno = EBUSY;
+    return -1;
+  }
+
+  devices[index].flags = v4l2_flags;
+  devices[index].open_count = 1;
+  devices[index].src_fmt = fmt;
+  devices[index].dest_fmt = fmt;
+
+  pthread_mutex_init(&devices[index].stream_lock, NULL);
+
+  devices[index].no_frames = 0;
+  devices[index].nreadbuffers = V4L2_DEFAULT_NREADBUFFERS;
+  devices[index].io = v4l2_io_none;
+  devices[index].convert = convert;
+  devices[index].convert_mmap_buf = MAP_FAILED;
+  for (i = 0; i < V4L2_MAX_NO_FRAMES; i++) {
+    devices[index].frame_pointers[i] = MAP_FAILED;
+  }
+  devices[index].frame_queued = 0;
+  devices[index].frame_mapped = 0;
+  devices[index].frame_map_count = 0;
+
+  if (index >= devices_used)
+    devices_used = index + 1;
+
+  V4L2_LOG("open: %d\n", fd);
+
+  return fd;
+}
+
+/* Is this an fd for which we are emulating v4l1 ? */
+static int v4l2_get_index(int fd)
+{
+  int index;
+
+  /* We never handle fd -1 */
+  if (fd == -1)
+    return -1;
+
+  for (index = 0; index < devices_used; index++)
+    if (devices[index].fd == fd)
+      break;
+
+  if (index == devices_used)
+    return -1;
+
+  return index;
+}
+
+
+int v4l2_close(int fd)
+{
+  int index, result;
+
+  if ((index = v4l2_get_index(fd)) == -1)
+    return syscall(SYS_close, fd);
+
+  /* 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;
+  pthread_mutex_unlock(&devices[index].stream_lock);
+
+  if (result)
+    return 0;
+
+  /* Free resources */
+  v4l2_unmap_buffers(index);
+  v4lconvert_destroy(devices[index].convert);
+  if (devices[index].convert_mmap_buf != MAP_FAILED) {
+    if (devices[index].frame_mapped || devices[index].frame_map_count)
+      V4L2_LOG(
+       "v4l2 mmap buffers still mapped on close(), mask: %08x, count: %d\n",
+       (unsigned int)devices[index].frame_mapped,
+       devices[index].frame_map_count);
+    else
+      syscall(SYS_munmap, devices[index].convert_mmap_buf,
+             devices[index].no_frames * V4L2_FRAME_BUF_SIZE);
+    devices[index].convert_mmap_buf = MAP_FAILED;
+  }
+
+  /* Remove the fd from our list of managed fds before closing it, because as
+     soon as we've done the actual close the fd maybe returned by an open in
+     another thread and we don't want to intercept calls to this new fd. */
+  devices[index].fd = -1;
+
+  /* Since we've marked the fd as no longer used, and freed the resources,
+     redo the close in case it was interrupted */
+  do {
+    result = syscall(SYS_close, fd);
+  } while (result == -1 && errno == EINTR);
+
+  V4L2_LOG("close: %d\n", fd);
+
+  return result;
+}
+
+int v4l2_dup(int fd)
+{
+  int index;
+
+  if ((index = v4l2_get_index(fd)) == -1)
+    return syscall(SYS_dup, fd);
+
+  devices[index].open_count++;
+
+  return fd;
+}
+
+static int v4l2_buf_ioctl_pre_check(int index, unsigned long int request,
+  struct v4l2_buffer *buf, int *result)
+{
+  if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+    *result = syscall(SYS_ioctl, devices[index].fd, request, buf);
+    return 1;
+  }
+
+  /* IMPROVEME (maybe?) add support for userptr's? */
+  if (devices[index].io != v4l2_io_mmap ||
+      buf->memory != V4L2_MEMORY_MMAP ||
+      buf->index  >= devices[index].no_frames) {
+    errno = EINVAL;
+    *result = -1;
+    return 1;
+  }
+
+  return 0;
+}
+
+int v4l2_ioctl (int fd, unsigned long int request, ...)
+{
+  void *arg;
+  va_list ap;
+  int result, converting, index, saved_err, stream_locked = 0;
+
+  va_start (ap, request);
+  arg = va_arg (ap, void *);
+  va_end (ap);
+
+  if ((index = v4l2_get_index(fd)) == -1)
+    return syscall(SYS_ioctl, fd, request, arg);
+
+  /* do we need to take the stream lock for this ioctl? */
+  switch (request) {
+    case VIDIOC_S_FMT:
+    case VIDIOC_G_FMT:
+    case VIDIOC_REQBUFS:
+    case VIDIOC_QUERYBUF:
+    case VIDIOC_QBUF:
+    case VIDIOC_DQBUF:
+    case VIDIOC_STREAMON:
+    case VIDIOC_STREAMOFF:
+      pthread_mutex_lock(&devices[index].stream_lock);
+      stream_locked = 1;
+  }
+
+  converting = devices[index].src_fmt.fmt.pix.pixelformat !=
+              devices[index].dest_fmt.fmt.pix.pixelformat;
+
+
+  switch (request) {
+    case VIDIOC_ENUM_FMT:
+      {
+       struct v4l2_fmtdesc *fmtdesc = arg;
+
+       if (fmtdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           !(devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION))
+         result = syscall(SYS_ioctl, devices[index].fd, request, arg);
+       else
+         result = v4lconvert_enum_fmt(devices[index].convert, fmtdesc);
+      }
+      break;
+
+    case VIDIOC_TRY_FMT:
+      {
+       struct v4l2_format *fmt = arg;
+
+       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           (devices[index].flags & V4L2_DISABLE_CONVERSION)) {
+         result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, fmt);
+         break;
+       }
+
+       result = v4lconvert_try_format(devices[index].convert, fmt, NULL);
+      }
+      break;
+
+    case VIDIOC_S_FMT:
+      {
+       struct v4l2_format src_fmt, *dest_fmt = arg;
+
+       if (dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+         result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT,
+                          dest_fmt);
+         break;
+       }
+
+       if (!memcmp(&devices[index].dest_fmt, dest_fmt, sizeof(*dest_fmt))) {
+         result = 0;
+         break;
+       }
+
+       /* Don't allow changing the format when mmap-ed IO is active, we could
+          allow this to happen in certain special circumstances, but it is
+          best to consistently deny this so that application developers do not
+          go expect this to work, because in their test setup it happens to
+          work. This also keeps the code much saner. */
+       if (devices[index].io == v4l2_io_mmap) {
+         errno = EBUSY;
+         result = -1;
+         break;
+       }
+
+       if (devices[index].flags & V4L2_DISABLE_CONVERSION) {
+         result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT,
+                          dest_fmt);
+         src_fmt = *dest_fmt;
+       } else {
+         result = v4lconvert_try_format(devices[index].convert, dest_fmt,
+                                        &src_fmt);
+       }
+
+       if (result)
+         break;
+
+       /* Maybe after try format has adjusted width/height etc, to whats
+          available nothing has changed (on the cam side) ? */
+       if (!memcmp(&devices[index].src_fmt, &src_fmt, sizeof(src_fmt))) {
+         devices[index].dest_fmt = *dest_fmt;
+         result = 0;
+         break;
+       }
+
+       if (devices[index].io == v4l2_io_read) {
+         V4L2_LOG("deactivating read-stream for format change\n");
+         if ((result = v4l2_deactivate_read_stream(index))) {
+           /* Undo what we've done to leave things in a consisten state */
+           if (v4l2_activate_read_stream(index))
+             V4L2_LOG_ERR(
+               "reactivating stream after deactivate failure (AAIIEEEE)\n");
+           return result;
+         }
+       }
+
+       result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT, &src_fmt);
+       if (result) {
+         saved_err = errno;
+         V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno));
+         /* Report to the app dest_fmt has not changed */
+         *dest_fmt = devices[index].dest_fmt;
+         errno = saved_err;
+         break;
+       }
+
+       devices[index].src_fmt = src_fmt;
+       devices[index].dest_fmt = *dest_fmt;
+      }
+      break;
+
+    case VIDIOC_G_FMT:
+      {
+       struct v4l2_format* fmt = arg;
+
+       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+         result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_G_FMT, fmt);
+         break;
+       }
+
+       *fmt = devices[index].dest_fmt;
+       result = 0;
+      }
+      break;
+
+    case VIDIOC_REQBUFS:
+      {
+       struct v4l2_requestbuffers *req = arg;
+
+       if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+         result = syscall(SYS_ioctl, devices[index].fd, request, arg);
+         return 1;
+       }
+
+       /* Don't allow mixing read / mmap io, either we control the buffers
+          (read based io), or the app does */
+       if (devices[index].io == v4l2_io_read) {
+         V4L2_LOG_ERR("to change from read io to mmap io open and close the device first!\n");
+         errno = EBUSY;
+         result = -1;
+         break;
+       }
+
+       /* Are any of our fake (convert_mmap_buf) buffers still mapped ? */
+       if (devices[index].frame_mapped || devices[index].frame_map_count) {
+         errno = EBUSY;
+         result = -1;
+         break;
+       }
+
+       /* IMPROVEME (maybe?) add support for userptr's? */
+       if (req->memory != V4L2_MEMORY_MMAP) {
+         errno = EINVAL;
+         result = -1;
+         break;
+       }
+
+       /* No more buffers then we can manage please */
+       if (req->count > V4L2_MAX_NO_FRAMES)
+         req->count = V4L2_MAX_NO_FRAMES;
+
+       /* Stop stream and unmap our real mapping of the buffers
+          (only relevant when we're converting, otherwise a no-op) */
+       v4l2_streamoff(index);
+       v4l2_unmap_buffers(index);
+
+       result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, req);
+       if (result)
+         break;
+
+       /* If we got more frames then we can handle lie to the app */
+       if (req->count > V4L2_MAX_NO_FRAMES)
+         req->count = V4L2_MAX_NO_FRAMES;
+
+       /* Force reallocation of convert_mmap_buf to fit the new no_frames */
+       syscall(SYS_munmap, devices[index].convert_mmap_buf,
+               devices[index].no_frames * V4L2_FRAME_BUF_SIZE);
+       devices[index].convert_mmap_buf = MAP_FAILED;
+       devices[index].no_frames = req->count;
+       devices[index].io = req->count? v4l2_io_mmap:v4l2_io_none;
+      }
+      break;
+
+    case VIDIOC_QUERYBUF:
+      {
+       struct v4l2_buffer *buf = arg;
+
+       if (v4l2_buf_ioctl_pre_check(index, request, buf, &result))
+         break;
+
+       /* Do a real query even when converting to let the driver fill in
+          things like buf->field */
+       result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, buf);
+       if (result || !converting)
+         break;
+
+       buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index;
+       buf->length = V4L2_FRAME_BUF_SIZE;
+      }
+      break;
+
+    case VIDIOC_QBUF:
+      {
+       struct v4l2_buffer *buf = arg;
+
+       if (v4l2_buf_ioctl_pre_check(index, request, buf, &result))
+         break;
+
+       result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, buf);
+      }
+      break;
+
+    case VIDIOC_DQBUF:
+      {
+       struct v4l2_buffer *buf = arg;
+
+       if (v4l2_buf_ioctl_pre_check(index, request, buf, &result))
+         break;
+
+       result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf);
+       if (result || !converting)
+         break;
+
+       /* An application can do a DQBUF before mmap-ing in the buffer,
+          but we need the buffer _now_ to write our converted data
+          to it! */
+       if (devices[index].convert_mmap_buf == MAP_FAILED) {
+         devices[index].convert_mmap_buf = mmap64(NULL,
+                                                  devices[index].no_frames *
+                                                    V4L2_FRAME_BUF_SIZE,
+                                                  PROT_READ|PROT_WRITE,
+                                                  MAP_ANONYMOUS|MAP_PRIVATE,
+                                                  -1, 0);
+         if (devices[index].convert_mmap_buf == MAP_FAILED) {
+           saved_err = errno;
+           V4L2_LOG_ERR("allocating conversion buffer\n");
+           errno = saved_err;
+           result = -1;
+           break;
+         }
+       }
+
+       /* Make sure we have the real v4l2 buffers mapped before trying to
+          read from them */
+       if ((result = v4l2_map_buffers(index)))
+         break;
+
+       result = v4lconvert_convert(devices[index].convert,
+                  &devices[index].src_fmt, &devices[index].dest_fmt,
+                  devices[index].frame_pointers[buf->index],
+                  buf->bytesused,
+                  devices[index].convert_mmap_buf +
+                    buf->index * V4L2_FRAME_BUF_SIZE,
+                  V4L2_FRAME_BUF_SIZE);
+       if (result < 0)
+         break;
+
+       buf->bytesused = result;
+       buf->length = V4L2_FRAME_BUF_SIZE;
+
+       result = 0;
+      }
+      break;
+
+    case VIDIOC_STREAMON:
+    case VIDIOC_STREAMOFF:
+      {
+       enum v4l2_buf_type *type = arg;
+
+       if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+         result = syscall(SYS_ioctl, devices[index].fd, request, type);
+         break;
+       }
+
+       if (devices[index].io != v4l2_io_mmap) {
+         errno = EINVAL;
+         result = -1;
+         break;
+       }
+
+       if (request == VIDIOC_STREAMON)
+         result = v4l2_streamon(index);
+       else
+         result = v4l2_streamoff(index);
+      }
+      break;
+
+    default:
+      result = syscall(SYS_ioctl, fd, request, arg);
+  }
+
+  if (stream_locked)
+    pthread_mutex_unlock(&devices[index].stream_lock);
+
+  saved_err = errno;
+  v4l2_log_ioctl(request, arg, result);
+  errno = saved_err;
+
+  return result;
+}
+
+
+ssize_t v4l2_read (int fd, void* buffer, size_t n)
+{
+  ssize_t result;
+  int index, bytesused, frame_index;
+
+  if ((index = v4l2_get_index(fd)) == -1)
+    return syscall(SYS_read, fd, buffer, n);
+
+  pthread_mutex_lock(&devices[index].stream_lock);
+
+  if (devices[index].io == v4l2_io_mmap) {
+    V4L2_LOG_ERR("to change from mmap io to read io first do request_buffers with a count of 0\n");
+    errno = EBUSY;
+    result = -1;
+    goto leave;
+  }
+  devices[index].io = v4l2_io_read;
+
+  if ((result = v4l2_activate_read_stream(index)))
+    goto leave;
+
+  if ((frame_index = v4l2_dequeue_read_buffer(index, &bytesused)) < 0) {
+    result = -1;
+    goto leave;
+  }
+
+  result = v4lconvert_convert(devices[index].convert,
+              &devices[index].src_fmt, &devices[index].dest_fmt,
+              devices[index].frame_pointers[frame_index], bytesused,
+              buffer, n);
+
+  v4l2_queue_read_buffer(index, frame_index);
+
+leave:
+  pthread_mutex_unlock(&devices[index].stream_lock);
+
+  return result;
+}
+
+void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd,
+  off_t offset)
+{
+  int index;
+  unsigned int buffer_index;
+  void *result;
+
+  if ((index = v4l2_get_index(fd)) == -1 ||
+      /* Check if the mmap data matches our answer to QUERY_BUF, if it doesn't
+        let the kernel handle it (to allow for mmap based non capture use) */
+      start || length != V4L2_FRAME_BUF_SIZE ||
+      (offset & ~0xff) != V4L2_MMAP_OFFSET_MAGIC) {
+    if (index != -1)
+      V4L2_LOG("Passing mmap(%p, %d, ..., %x, through to the driver\n",
+       start, (int)length, (int)offset);
+    return mmap64(start, length, prot, flags, fd, offset);
+  }
+
+  pthread_mutex_lock(&devices[index].stream_lock);
+
+  buffer_index = offset & 0xff;
+  if (buffer_index >= devices[index].no_frames ||
+      devices[index].io != v4l2_io_mmap ||
+      /* Got magic offset and not converting ?? */
+      devices[index].src_fmt.fmt.pix.pixelformat ==
+      devices[index].dest_fmt.fmt.pix.pixelformat) {
+    errno = EINVAL;
+    result = MAP_FAILED;
+    goto leave;
+  }
+
+  if (devices[index].convert_mmap_buf == MAP_FAILED) {
+    devices[index].convert_mmap_buf = mmap64(NULL,
+                                            devices[index].no_frames *
+                                              V4L2_FRAME_BUF_SIZE,
+                                            PROT_READ|PROT_WRITE,
+                                            MAP_ANONYMOUS|MAP_PRIVATE,
+                                            -1, 0);
+    if (devices[index].convert_mmap_buf == MAP_FAILED) {
+      int saved_err = errno;
+      V4L2_LOG_ERR("allocating conversion buffer\n");
+      errno = saved_err;
+      result = MAP_FAILED;
+      goto leave;
+    }
+  }
+
+  devices[index].frame_mapped |= 1 << buffer_index;
+  devices[index].frame_map_count++;
+
+  result = devices[index].convert_mmap_buf +
+    buffer_index * V4L2_FRAME_BUF_SIZE;
+
+  V4L2_LOG("Fake (conversion) mmap buf %u, seen by app at: %p\n",
+    buffer_index, result);
+
+leave:
+  pthread_mutex_unlock(&devices[index].stream_lock);
+
+  return result;
+}
+
+int v4l2_munmap(void *_start, size_t length)
+{
+  int index;
+  unsigned int buffer_index;
+  unsigned char *start = _start;
+
+  /* Is this memory ours? */
+  if (start != MAP_FAILED && length == V4L2_FRAME_BUF_SIZE) {
+    for (index = 0; index < devices_used; index++)
+      if (devices[index].fd != -1 &&
+         devices[index].convert_mmap_buf != MAP_FAILED &&
+         start >= devices[index].convert_mmap_buf &&
+         (start - devices[index].convert_mmap_buf) % length == 0)
+       break;
+
+    if (index != devices_used) {
+      int unmapped = 0;
+
+      pthread_mutex_lock(&devices[index].stream_lock);
+
+      buffer_index = (start - devices[index].convert_mmap_buf) / length;
+
+      /* Redo our checks now that we have the lock, things may have changed */
+      if (devices[index].convert_mmap_buf != MAP_FAILED &&
+         start >= devices[index].convert_mmap_buf &&
+         (start - devices[index].convert_mmap_buf) % length == 0 &&
+         buffer_index < devices[index].no_frames) {
+       devices[index].frame_mapped &= ~(1 << buffer_index);
+       if (devices[index].frame_map_count > 0)
+         devices[index].frame_map_count--;
+       unmapped = 1;
+      }
+
+      pthread_mutex_unlock(&devices[index].stream_lock);
+
+      if (unmapped) {
+       V4L2_LOG("v4l2 fake buffer munmap %p, %d\n", start, (int)length);
+       return 0;
+      }
+    }
+  }
+
+  V4L2_LOG("v4l2 unknown munmap %p, %d\n", start, (int)length);
+
+  return syscall(SYS_munmap, _start, length);
+}
+
+/* Misc utility functions */
+int v4l2_set_control(int fd, int cid, int value)
+{
+  struct v4l2_queryctrl qctrl = { .id = cid };
+  struct v4l2_control ctrl = { .id = cid };
+  int result;
+
+  if ((result = syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl)))
+    return result;
+
+  if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) &&
+      !(qctrl.flags & V4L2_CTRL_FLAG_GRABBED)) {
+    if (qctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
+      ctrl.value = value? 1:0;
+    else
+      ctrl.value = (value * (qctrl.maximum - qctrl.minimum) + 32767) / 65535 +
+                  qctrl.minimum;
+
+    result = syscall(SYS_ioctl, fd, VIDIOC_S_CTRL, &ctrl);
+  }
+
+  return result;
+}
+
+int v4l2_get_control(int fd, int cid)
+{
+  struct v4l2_queryctrl qctrl = { .id = cid };
+  struct v4l2_control ctrl = { .id = cid };
+
+  if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl))
+    return 0;
+
+  if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+    return 0;
+
+  if (syscall(SYS_ioctl, fd, VIDIOC_G_CTRL, &ctrl))
+    return 0;
+
+  return ((ctrl.value - qctrl.minimum) * 65535 +
+         (qctrl.maximum - qctrl.minimum) / 2) /
+        (qctrl.maximum - qctrl.minimum);
+}
diff --git a/lib/libv4l2/log.c b/lib/libv4l2/log.c
new file mode 100644 (file)
index 0000000..982a185
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+#             (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+# 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 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 <stdio.h>
+#include <stdlib.h>
+#include <linux/ioctl.h>
+/* These headers are not needed by us, but by linux/videodev2.h,
+   which is broken on some systems and doesn't include them itself :( */
+#include <sys/time.h>
+#include <asm/types.h>
+/* end broken header workaround includes */
+#include <linux/videodev2.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+FILE *v4l2_log_file = NULL;
+
+static const char *v4l2_ioctls[] = {
+       /* start v4l2 ioctls */
+       [_IOC_NR(VIDIOC_QUERYCAP)]         = "VIDIOC_QUERYCAP",
+       [_IOC_NR(VIDIOC_RESERVED)]         = "VIDIOC_RESERVED",
+       [_IOC_NR(VIDIOC_ENUM_FMT)]         = "VIDIOC_ENUM_FMT",
+       [_IOC_NR(VIDIOC_G_FMT)]            = "VIDIOC_G_FMT",
+       [_IOC_NR(VIDIOC_S_FMT)]            = "VIDIOC_S_FMT",
+       [_IOC_NR(VIDIOC_REQBUFS)]          = "VIDIOC_REQBUFS",
+       [_IOC_NR(VIDIOC_QUERYBUF)]         = "VIDIOC_QUERYBUF",
+       [_IOC_NR(VIDIOC_G_FBUF)]           = "VIDIOC_G_FBUF",
+       [_IOC_NR(VIDIOC_S_FBUF)]           = "VIDIOC_S_FBUF",
+       [_IOC_NR(VIDIOC_OVERLAY)]          = "VIDIOC_OVERLAY",
+       [_IOC_NR(VIDIOC_QBUF)]             = "VIDIOC_QBUF",
+       [_IOC_NR(VIDIOC_DQBUF)]            = "VIDIOC_DQBUF",
+       [_IOC_NR(VIDIOC_STREAMON)]         = "VIDIOC_STREAMON",
+       [_IOC_NR(VIDIOC_STREAMOFF)]        = "VIDIOC_STREAMOFF",
+       [_IOC_NR(VIDIOC_G_PARM)]           = "VIDIOC_G_PARM",
+       [_IOC_NR(VIDIOC_S_PARM)]           = "VIDIOC_S_PARM",
+       [_IOC_NR(VIDIOC_G_STD)]            = "VIDIOC_G_STD",
+       [_IOC_NR(VIDIOC_S_STD)]            = "VIDIOC_S_STD",
+       [_IOC_NR(VIDIOC_ENUMSTD)]          = "VIDIOC_ENUMSTD",
+       [_IOC_NR(VIDIOC_ENUMINPUT)]        = "VIDIOC_ENUMINPUT",
+       [_IOC_NR(VIDIOC_G_CTRL)]           = "VIDIOC_G_CTRL",
+       [_IOC_NR(VIDIOC_S_CTRL)]           = "VIDIOC_S_CTRL",
+       [_IOC_NR(VIDIOC_G_TUNER)]          = "VIDIOC_G_TUNER",
+       [_IOC_NR(VIDIOC_S_TUNER)]          = "VIDIOC_S_TUNER",
+       [_IOC_NR(VIDIOC_G_AUDIO)]          = "VIDIOC_G_AUDIO",
+       [_IOC_NR(VIDIOC_S_AUDIO)]          = "VIDIOC_S_AUDIO",
+       [_IOC_NR(VIDIOC_QUERYCTRL)]        = "VIDIOC_QUERYCTRL",
+       [_IOC_NR(VIDIOC_QUERYMENU)]        = "VIDIOC_QUERYMENU",
+       [_IOC_NR(VIDIOC_G_INPUT)]          = "VIDIOC_G_INPUT",
+       [_IOC_NR(VIDIOC_S_INPUT)]          = "VIDIOC_S_INPUT",
+       [_IOC_NR(VIDIOC_G_OUTPUT)]         = "VIDIOC_G_OUTPUT",
+       [_IOC_NR(VIDIOC_S_OUTPUT)]         = "VIDIOC_S_OUTPUT",
+       [_IOC_NR(VIDIOC_ENUMOUTPUT)]       = "VIDIOC_ENUMOUTPUT",
+       [_IOC_NR(VIDIOC_G_AUDOUT)]         = "VIDIOC_G_AUDOUT",
+       [_IOC_NR(VIDIOC_S_AUDOUT)]         = "VIDIOC_S_AUDOUT",
+       [_IOC_NR(VIDIOC_G_MODULATOR)]      = "VIDIOC_G_MODULATOR",
+       [_IOC_NR(VIDIOC_S_MODULATOR)]      = "VIDIOC_S_MODULATOR",
+       [_IOC_NR(VIDIOC_G_FREQUENCY)]      = "VIDIOC_G_FREQUENCY",
+       [_IOC_NR(VIDIOC_S_FREQUENCY)]      = "VIDIOC_S_FREQUENCY",
+       [_IOC_NR(VIDIOC_CROPCAP)]          = "VIDIOC_CROPCAP",
+       [_IOC_NR(VIDIOC_G_CROP)]           = "VIDIOC_G_CROP",
+       [_IOC_NR(VIDIOC_S_CROP)]           = "VIDIOC_S_CROP",
+       [_IOC_NR(VIDIOC_G_JPEGCOMP)]       = "VIDIOC_G_JPEGCOMP",
+       [_IOC_NR(VIDIOC_S_JPEGCOMP)]       = "VIDIOC_S_JPEGCOMP",
+       [_IOC_NR(VIDIOC_QUERYSTD)]         = "VIDIOC_QUERYSTD",
+       [_IOC_NR(VIDIOC_TRY_FMT)]          = "VIDIOC_TRY_FMT",
+       [_IOC_NR(VIDIOC_ENUMAUDIO)]        = "VIDIOC_ENUMAUDIO",
+       [_IOC_NR(VIDIOC_ENUMAUDOUT)]       = "VIDIOC_ENUMAUDOUT",
+       [_IOC_NR(VIDIOC_G_PRIORITY)]       = "VIDIOC_G_PRIORITY",
+       [_IOC_NR(VIDIOC_S_PRIORITY)]       = "VIDIOC_S_PRIORITY",
+       [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP",
+       [_IOC_NR(VIDIOC_LOG_STATUS)]       = "VIDIOC_LOG_STATUS",
+       [_IOC_NR(VIDIOC_G_EXT_CTRLS)]      = "VIDIOC_G_EXT_CTRLS",
+       [_IOC_NR(VIDIOC_S_EXT_CTRLS)]      = "VIDIOC_S_EXT_CTRLS",
+       [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)]    = "VIDIOC_TRY_EXT_CTRLS",
+};
+
+void v4l2_log_ioctl(unsigned long int request, void *arg, int result)
+{
+  const char *ioctl_str;
+  char buf[40];
+
+  if (!v4l2_log_file)
+    return;
+
+  if (_IOC_TYPE(request) == 'V' && _IOC_NR(request) < ARRAY_SIZE(v4l2_ioctls))
+    ioctl_str = v4l2_ioctls[_IOC_NR(request)];
+  else {
+    snprintf(buf, sizeof(buf), "unknown request: %c %d\n",
+      (int)_IOC_TYPE(request), (int)_IOC_NR(request));
+    ioctl_str = buf;
+  }
+
+  fprintf(v4l2_log_file, "request == %s\n", ioctl_str);
+
+  switch (request) {
+    case VIDIOC_G_FMT:
+    case VIDIOC_S_FMT:
+    case VIDIOC_TRY_FMT:
+      {
+       struct v4l2_format* fmt = arg;
+       int pixfmt = fmt->fmt.pix.pixelformat;
+
+       if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+         fprintf(v4l2_log_file, "  pixelformat: %c%c%c%c %dx%d\n",
+           pixfmt & 0xff,
+           (pixfmt >> 8) & 0xff,
+           (pixfmt >> 16) & 0xff,
+           pixfmt >> 24,
+           fmt->fmt.pix.width,
+           fmt->fmt.pix.height);
+         fprintf(v4l2_log_file, "  field: %d bytesperline: %d imagesize%d\n",
+           (int)fmt->fmt.pix.field, (int)fmt->fmt.pix.bytesperline,
+           (int)fmt->fmt.pix.sizeimage);
+         fprintf(v4l2_log_file, "  colorspace: %d, priv: %x\n",
+           (int)fmt->fmt.pix.colorspace, (int)fmt->fmt.pix.priv);
+       }
+      }
+      break;
+  }
+
+  fprintf(v4l2_log_file, "result == %d\n", result);
+  fflush(v4l2_log_file);
+}
diff --git a/lib/libv4l2/v4l2convert.c b/lib/libv4l2/v4l2convert.c
new file mode 100644 (file)
index 0000000..bcd11c2
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+# open/close/ioctl/mmap/munmap library call wrapper doing format conversion
+# for v4l2 applications which want to be able to simply capture bgr24 / yuv420
+# from v4l2 devices with more exotic frame formats.
+
+#             (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+
+# 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 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 <stdlib.h>
+#include <syscall.h>
+#include <fcntl.h>
+#include <string.h>
+/* These headers are not needed by us, but by linux/videodev2.h,
+   which is broken on some systems and doesn't include them itself :( */
+#include <sys/time.h>
+#include <asm/types.h>
+#include <linux/ioctl.h>
+/* end broken header workaround includes */
+#include <linux/videodev2.h>
+#include <libv4l2.h>
+
+/* Check that open/read/mmap is not a define */
+#if defined open || defined read || defined mmap
+#error open/read/mmap is a prepocessor macro !!
+#endif
+
+int open (const char *file, int oflag, ...)
+{
+  int fd;
+  struct v4l2_capability cap;
+
+  /* original open code */
+  if (oflag & O_CREAT)
+  {
+    va_list ap;
+    mode_t mode;
+
+    va_start (ap, oflag);
+    mode = va_arg (ap, mode_t);
+
+    fd = syscall(SYS_open, file, oflag, mode);
+
+    va_end(ap);
+  } else
+    fd = syscall(SYS_open, file, oflag);
+  /* end of original open code */
+
+  if (fd == -1)
+    return fd;
+
+  /* check if we're opening a video4linux2 device */
+  if (strncmp(file, "/dev/video", 10))
+    return fd;
+
+  /* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */
+  if (syscall(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, V4L2_ENABLE_ENUM_FMT_EMULATION);
+
+  return fd;
+}
+
+int close(int fd)
+{
+  return v4l2_close(fd);
+}
+
+int dup(int fd)
+{
+  return v4l2_dup(fd);
+}
+
+int ioctl (int fd, unsigned long int request, ...)
+{
+  void *arg;
+  va_list ap;
+
+  va_start (ap, request);
+  arg = va_arg (ap, void *);
+  va_end (ap);
+
+  return v4l2_ioctl (fd, request, arg);
+}
+
+ssize_t read (int fd, void* buffer, size_t n)
+{
+  return v4l2_read (fd, buffer, n);
+}
+
+void mmap(void *start, size_t length, int prot, int flags, int fd,
+  off_t offset)
+{
+  return v4l2_mmap(start, length, prot, flags, fd, offset);
+}
+
+int munmap(void *start, size_t length)
+{
+  return v4l2_munmap(start, length);
+}