From 0d3f75db47da8e0a51eab041eb401bfb6f309174 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Tue, 1 Jul 2008 21:11:29 +0200 Subject: v4l2-library: libv4l2 and v4l2convert From: Hans de Goede 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 Signed-off-by: Thierry MERLE --- v4l2-apps/lib/libv4l/libv4l2/Makefile | 52 ++ v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h | 94 +++ v4l2-apps/lib/libv4l/libv4l2/libv4l2.c | 1060 +++++++++++++++++++++++++++ v4l2-apps/lib/libv4l/libv4l2/log.c | 138 ++++ v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c | 121 +++ 5 files changed, 1465 insertions(+) create mode 100644 v4l2-apps/lib/libv4l/libv4l2/Makefile create mode 100644 v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h create mode 100644 v4l2-apps/lib/libv4l/libv4l2/libv4l2.c create mode 100644 v4l2-apps/lib/libv4l/libv4l2/log.c create mode 100644 v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c (limited to 'v4l2-apps/lib/libv4l/libv4l2') diff --git a/v4l2-apps/lib/libv4l/libv4l2/Makefile b/v4l2-apps/lib/libv4l/libv4l2/Makefile new file mode 100644 index 000000000..c5463800b --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4l2/Makefile @@ -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/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h new file mode 100644 index 000000000..bdbd45e5c --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h @@ -0,0 +1,94 @@ +/* +# (C) 2008 Hans de Goede + +# 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 +#include +#include /* 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/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c new file mode 100644 index 000000000..33d9b2891 --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -0,0 +1,1060 @@ +/* +# (C) 2008 Hans de Goede + +# 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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/v4l2-apps/lib/libv4l/libv4l2/log.c b/v4l2-apps/lib/libv4l/libv4l2/log.c new file mode 100644 index 000000000..982a185c6 --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4l2/log.c @@ -0,0 +1,138 @@ +/* +# (C) 2008 Hans de Goede + +# 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 +#include +#include +/* 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 +#include +/* end broken header workaround includes */ +#include + +#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/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c new file mode 100644 index 000000000..bcd11c2cc --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c @@ -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 + +# 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 +#include +#include +#include +#include +/* 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 +#include +#include +/* end broken header workaround includes */ +#include +#include + +/* 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); +} -- cgit v1.2.3 From 9530d8bebf9a1259505bbe3b9ff237795bff3bee Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Thu, 3 Jul 2008 20:41:07 +0200 Subject: v4l2-library: libv4l: update mercurial tree to latest 0.3.1 release From: Hans de Goede This patch syncs (updates) the libv4l in mercurial with (to) the latest 0.3.1 release. - Only serialize V4L2_BUF_TYPE_VIDEO_CAPTURE type ioctls - Do not return an uninitialized variable as result code for GPICT (fixes vlc, but see below) - Add an apps-patches directory which includes: * vlc-0.8.6-libv4l1.patch, modify vlc's v4l1 plugin to directly call into libv4l1, in the end we want all apps todo this as its better then LD_PRELOAD tricks, but for vlc this is needed as vlc's plugin system causes LD_PRELOAD to not work on symbols in the plugins * camorama-0.19-fixes.patch, small bugfixes to camorama's v4l1 support, this patch only fixes _real_ bugs in camorama and does not change it to work with v4l1compat. Although it does work better with these bugs fixed :) With this patch and LD_PRELOAD=/v4l1compat.so it works flawless. Signed-off-by: Hans de Goede Signed-off-by: Thierry MERLE --- v4l2-apps/lib/libv4l/libv4l2/libv4l2.c | 145 ++++++++++++--------------------- 1 file changed, 52 insertions(+), 93 deletions(-) (limited to 'v4l2-apps/lib/libv4l/libv4l2') diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index 33d9b2891..f97fa09b6 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -527,31 +527,13 @@ int v4l2_dup(int fd) 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; + int result, converting, index, saved_err; + int is_capture_request = 0, stream_needs_locking = 0; va_start (ap, request); arg = va_arg (ap, void *); @@ -560,61 +542,70 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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? */ + /* Is this a capture request and do we need to take the stream lock? */ switch (request) { + case VIDIOC_ENUM_FMT: + if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + is_capture_request = 1; + break; + case VIDIOC_TRY_FMT: + if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + is_capture_request = 1; + break; case VIDIOC_S_FMT: case VIDIOC_G_FMT: + if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } + break; case VIDIOC_REQBUFS: + if (((struct v4l2_requestbuffers *)arg)->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } + break; case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: + if (((struct v4l2_buffer *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } + break; case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: - pthread_mutex_lock(&devices[index].stream_lock); - stream_locked = 1; + if (*((enum v4l2_buf_type *)arg) == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } } + if (!is_capture_request) + return syscall(SYS_ioctl, fd, request, arg); + + + if (stream_needs_locking) + pthread_mutex_lock(&devices[index].stream_lock); + 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); - } + result = v4lconvert_enum_fmt(devices[index].convert, arg); 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); - } + result = v4lconvert_try_format(devices[index].convert, arg, 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; @@ -658,7 +649,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if (v4l2_activate_read_stream(index)) V4L2_LOG_ERR( "reactivating stream after deactivate failure (AAIIEEEE)\n"); - return result; + break; } } @@ -681,11 +672,6 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { 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; } @@ -695,11 +681,6 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { 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) { @@ -753,9 +734,6 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { 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); @@ -768,23 +746,13 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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); - } + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, arg); 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; @@ -832,32 +800,23 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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); + 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) + if (stream_needs_locking) pthread_mutex_unlock(&devices[index].stream_lock); saved_err = errno; -- cgit v1.2.3 From 40536a32a310338da9cf2c697dd978780aeda18a Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Fri, 4 Jul 2008 19:28:49 +0200 Subject: v4l2-library: libv4l-makefile-improvements.patch From: Hans de Goede Makefile improvements: * Split DESTDIR into DESTDIR and PREFIX as used in most makefiles out there * Add LIBDIR variable to allow installation in /lib64 for example * Install the wrappers in /libv4l instead of directly under libdir, as they are not libraries meant for linking * preserve timestamps of header files when installing them Signed-off-by: Hans de Goede Signed-off-by: Thierry MERLE --- v4l2-apps/lib/libv4l/libv4l2/Makefile | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'v4l2-apps/lib/libv4l/libv4l2') diff --git a/v4l2-apps/lib/libv4l/libv4l2/Makefile b/v4l2-apps/lib/libv4l/libv4l2/Makefile index c5463800b..1258e379b 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l2/Makefile @@ -19,8 +19,12 @@ ifeq ($(LIB_RELEASE),) LIB_RELEASE = 0 endif -ifeq ($(DESTDIR),) -DESTDIR = /usr/local +ifeq ($(PREFIX),) +PREFIX = /usr/local +endif + +ifeq ($(LIBDIR),) +LIBDIR = $(PREFIX)/lib endif all: $(TARGETS) @@ -30,15 +34,14 @@ $(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 && \ + mkdir -p $(DESTDIR)$(PREFIX)/include + install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include + mkdir -p $(DESTDIR)$(LIBDIR)/libv4l + install -m 755 $(V4L2_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR) + cd $(DESTDIR)$(LIBDIR) && \ 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) + install -m 755 $(V4L2CONVERT).$(LIB_RELEASE) \ + $(DESTDIR)$(LIBDIR)/libv4l/$(V4L2CONVERT) clean:: rm -f *.so* *.o log *~ -- cgit v1.2.3 From 63c4e4bef7b1537de4e313ee9d20907a372d8f56 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Fri, 4 Jul 2008 19:30:13 +0200 Subject: v4l2-library: libv4l-warnings.patch From: Hans de Goede Fix all compiler warnings in libv4l Signed-off-by: Hans de Goede Signed-off-by: Thierry MERLE --- v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h | 2 +- v4l2-apps/lib/libv4l/libv4l2/libv4l2.c | 13 +++++++------ v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'v4l2-apps/lib/libv4l/libv4l2') diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h index bdbd45e5c..e4edf91f2 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h @@ -27,7 +27,7 @@ /* 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_MAX_NO_FRAMES 32 #define V4L2_DEFAULT_NREADBUFFERS 4 #define V4L2_FRAME_BUF_SIZE (4096 * 4096) diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index f97fa09b6..0ed6ebdc6 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -80,9 +80,10 @@ #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 struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = { { .fd = -1 }, + { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, + { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, + { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }, { .fd = -1 }}; static int devices_used = 0; @@ -830,7 +831,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) ssize_t v4l2_read (int fd, void* buffer, size_t n) { ssize_t result; - int index, bytesused, frame_index; + int index, bytesused = 0, frame_index; if ((index = v4l2_get_index(fd)) == -1) return syscall(SYS_read, fd, buffer, n); @@ -867,7 +868,7 @@ leave: } void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, - off_t offset) + __off_t offset) { int index; unsigned int buffer_index; @@ -877,7 +878,7 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, /* 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) { + ((unsigned int)offset & ~0xFFu) != V4L2_MMAP_OFFSET_MAGIC) { if (index != -1) V4L2_LOG("Passing mmap(%p, %d, ..., %x, through to the driver\n", start, (int)length, (int)offset); diff --git a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c index bcd11c2cc..67f563602 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c @@ -110,7 +110,7 @@ ssize_t read (int fd, void* buffer, size_t n) } void mmap(void *start, size_t length, int prot, int flags, int fd, - off_t offset) + __off_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } -- cgit v1.2.3 From ab715d2e8eb29998b60fa3bbc1ad95441cfb69b3 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Sun, 6 Jul 2008 14:07:34 +0200 Subject: v4l2-library: libv4l-sync-with-0.3.3-release From: Hans de Goede * Add open64 and mmap64 wrappers to the LD_PRELOAD wrapper libs, so that they also work for applications compiled with FILE_OFFSET_BITS=64, this fixes using them with v4l-info * While looking at xawtv in general, found a few bugs in xawtv itself, added a patch to fix those to the appl-patches dir * Talking about the appl-patches dir, restore that as it accidentally got dropped from 0.3.2 * Be more verbose in various places when it comes to logging (esp errors) * Change v4lconvert_enum_fmt code a bit, so that it is easier to add more supported destination formats to libv4lconvert * Don't return -EINVAL from try_fmt when we cannot convert because the cam doesn't have any formats we know. Instead just return as format whatever the cam returns from try_fmt, this new behavior is compliant with the v4l2 api as documented Signed-off-by: Hans de Goede Signed-off-by: Thierry MERLE --- v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h | 9 +++++ v4l2-apps/lib/libv4l/libv4l2/libv4l2.c | 58 ++++++++++++++++++++--------- v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c | 31 +++++++++++++++ 3 files changed, 81 insertions(+), 17 deletions(-) (limited to 'v4l2-apps/lib/libv4l/libv4l2') diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h index e4edf91f2..203dcffaf 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h @@ -23,6 +23,15 @@ #include #include /* includes videodev2.h for us */ +/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ +#ifdef __NR_mmap2 +#define SYS_mmap2 __NR_mmap2 +#define MMAP2_PAGE_SHIFT 12 +#else +#define SYS_mmap2 SYS_mmap +#define MMAP2_PAGE_SHIFT 0 +#endif + #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 diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index 0ed6ebdc6..5830576ae 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -55,9 +55,6 @@ When modifications are made, one should be carefull that this behavior is preserved. */ - -#define _LARGEFILE64_SOURCE 1 - #include #include #include @@ -161,8 +158,9 @@ static int v4l2_map_buffers(int index) break; } - devices[index].frame_pointers[i] = mmap64(NULL, buf.length, - PROT_READ, MAP_SHARED, devices[index].fd, buf.m.offset); + devices[index].frame_pointers[i] = (void *)syscall(SYS_mmap2, NULL, + (size_t)buf.length, PROT_READ, MAP_SHARED, devices[index].fd, + (__off_t)(buf.m.offset >> MMAP2_PAGE_SHIFT)); if (devices[index].frame_pointers[i] == MAP_FAILED) { int saved_err = errno; V4L2_LOG_ERR("mmapping buffer %u: %s\n", i, strerror(errno)); @@ -583,8 +581,13 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } } - if (!is_capture_request) - return syscall(SYS_ioctl, fd, request, arg); + if (!is_capture_request) { + result = syscall(SYS_ioctl, fd, request, arg); + saved_err = errno; + v4l2_log_ioctl(request, arg, result); + errno = saved_err; + return result; + } if (stream_needs_locking) @@ -755,16 +758,22 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) struct v4l2_buffer *buf = arg; result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); - if (result || !converting) + if (result) { + V4L2_LOG_ERR("dequeing buffer: %s\n", strerror(errno)); + break; + } + + if (!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, + devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, + (size_t)( + devices[index].no_frames * + V4L2_FRAME_BUF_SIZE), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); @@ -789,8 +798,11 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) devices[index].convert_mmap_buf + buf->index * V4L2_FRAME_BUF_SIZE, V4L2_FRAME_BUF_SIZE); - if (result < 0) + if (result < 0) { + V4L2_LOG_ERR("converting / decoding frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); break; + } buf->bytesused = result; buf->length = V4L2_FRAME_BUF_SIZE; @@ -861,6 +873,10 @@ ssize_t v4l2_read (int fd, void* buffer, size_t n) v4l2_queue_read_buffer(index, frame_index); + if (result < 0) + V4L2_LOG_ERR("converting / decoding frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + leave: pthread_mutex_unlock(&devices[index].stream_lock); @@ -868,7 +884,7 @@ leave: } void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, - __off_t offset) + __off64_t offset) { int index; unsigned int buffer_index; @@ -882,7 +898,14 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, 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); + + if (offset & ((1 << MMAP2_PAGE_SHIFT) - 1)) { + errno = EINVAL; + return MAP_FAILED; + } + + return (void *)syscall(SYS_mmap2, start, length, prot, flags, fd, + (__off_t)(offset >> MMAP2_PAGE_SHIFT)); } pthread_mutex_lock(&devices[index].stream_lock); @@ -899,9 +922,10 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, } if (devices[index].convert_mmap_buf == MAP_FAILED) { - devices[index].convert_mmap_buf = mmap64(NULL, - devices[index].no_frames * - V4L2_FRAME_BUF_SIZE, + devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, NULL, + (size_t)( + devices[index].no_frames * + V4L2_FRAME_BUF_SIZE), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); diff --git a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c index 67f563602..7db1ca6d6 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c @@ -20,6 +20,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define _LARGEFILE64_SOURCE 1 + #include #include #include @@ -82,6 +84,29 @@ int open (const char *file, int oflag, ...) return fd; } +int open64(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 = open(file, oflag | O_LARGEFILE, mode); + + va_end(ap); + } else + fd = open(file, oflag | O_LARGEFILE); + /* end of original open code */ + + return fd; +} + int close(int fd) { return v4l2_close(fd); @@ -115,6 +140,12 @@ void mmap(void *start, size_t length, int prot, int flags, int fd, return v4l2_mmap(start, length, prot, flags, fd, offset); } +void mmap64(void *start, size_t length, int prot, int flags, int fd, + __off64_t offset) +{ + return v4l2_mmap(start, length, prot, flags, fd, offset); +} + int munmap(void *start, size_t length) { return v4l2_munmap(start, length); -- cgit v1.2.3 From 4e748efa7677d0762f4b1738cd8869df5eb106b6 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Tue, 8 Jul 2008 07:01:45 +0200 Subject: v4l2-library: libv4l-really-sync-with-0.3.4.patch From: Hans de Goede * The mmap64 support in 0.3.3, has caused a bug in libv4l1 when running on 32 bit systems (who uses those now a days?), this bug caused v4l1 compatibility to not work at all, this release fixes this * Some apps (xawtv, kopete) use an ioctl wrapper internally for various reasons. This wrappers request argument is an int, but the real ioctl's request argument is an unsigned long. Passing the VIDIOC_xxx defines through to the wrapper, and then to the real ioctl, causes the request to get sign extended on 64 bit args. The kernel seems to ignore the upper 32 bits, causing the sign extension to not make a difference. libv4l now also ignores the upper 32 bits of the libv4lx_ioctl request argument on 64 bit archs * Add a bugfix patch for kopete in the appl-patches dir, currently it assumes that it got the width and height it asked for when doing a S_FMT, which is a wrong assumption Signed-off-by: Hans de Goede Signed-off-by: Thierry MERLE --- v4l2-apps/lib/libv4l/libv4l2/libv4l2.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'v4l2-apps/lib/libv4l/libv4l2') diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index 5830576ae..8dfcf9b71 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -541,6 +541,11 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if ((index = v4l2_get_index(fd)) == -1) return syscall(SYS_ioctl, fd, request, arg); + /* Appearantly the kernel and / or glibc ignore the 32 most significant bits + when long = 64 bits, and some applications pass an int holding the req to + ioctl, causing it to get sign extended, depending upon this behavior */ + request = (unsigned int)request; + /* Is this a capture request and do we need to take the stream lock? */ switch (request) { case VIDIOC_ENUM_FMT: -- cgit v1.2.3