diff options
Diffstat (limited to 'v4l2-apps/lib/libv4l/libv4l2')
-rw-r--r-- | v4l2-apps/lib/libv4l/libv4l2/Makefile | 51 | ||||
-rw-r--r-- | v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h | 16 | ||||
-rw-r--r-- | v4l2-apps/lib/libv4l/libv4l2/libv4l2.c | 422 | ||||
-rw-r--r-- | v4l2-apps/lib/libv4l/libv4l2/log.c | 16 | ||||
-rw-r--r-- | v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c | 33 |
5 files changed, 324 insertions, 214 deletions
diff --git a/v4l2-apps/lib/libv4l/libv4l2/Makefile b/v4l2-apps/lib/libv4l/libv4l2/Makefile index 1258e379b..648d27c0c 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l2/Makefile @@ -1,20 +1,25 @@ -CC = gcc -LD = gcc - -CPPFLAGS = -fPIC -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -LDFLAGS = -shared +LIBS = -lpthread -V4L2_OBJS = libv4l2.o log.o ../libv4lconvert/libv4lconvert.so -V4L2_LIB = libv4l2.so +V4L2_OBJS = libv4l2.o log.o V4L2CONVERT = v4l2convert.so V4L2CONVERT_O = v4l2convert.o libv4l2.so -TARGETS = $(V4L2_LIB) $(V4L2CONVERT) +TARGETS = $(V4L2_LIB) libv4l2.pc INCLUDES = ../include/libv4l2.h +ifeq ($(LINKTYPE),static) +V4L2_LIB = libv4l2.a +else +V4L2_LIB = libv4l2.so +V4L2_OBJS += ../libv4lconvert/libv4lconvert.so +TARGETS += $(V4L2CONVERT) +override CPPFLAGS += -fPIC +endif + ifeq ($(LIB_RELEASE),) LIB_RELEASE = 0 endif @@ -33,23 +38,45 @@ $(V4L2_LIB): $(V4L2_OBJS) $(V4L2CONVERT): $(V4L2CONVERT_O) $(V4L2_LIB) +libv4l2.pc: + @echo prefix=$(PREFIX) > libv4l2.pc + @echo libdir=$(LIBDIR) >> libv4l2.pc + @echo >> libv4l2.pc + @echo 'Name: libv4l2' >> libv4l2.pc + @echo 'Description: v4l2 device access library' >> libv4l2.pc + @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4l2.pc + @echo 'Requires: libv4lconvert' >> libv4l2.pc + @echo 'Libs: -L$${libdir} -lv4l2' >> libv4l2.pc + @echo 'Libs.private: -lpthread' >> libv4l2.pc + @echo 'Cflags: -I$${prefix}/include' >> libv4l2.pc + install: all mkdir -p $(DESTDIR)$(PREFIX)/include install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include +ifeq ($(LINKTYPE),static) + mkdir -p $(DESTDIR)$(LIBDIR) + install -m 644 $(V4L2_LIB) $(DESTDIR)$(LIBDIR) +else 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) install -m 755 $(V4L2CONVERT).$(LIB_RELEASE) \ $(DESTDIR)$(LIBDIR)/libv4l/$(V4L2CONVERT) +endif + mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig + install -m 644 libv4l2.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.so* *.o log *~ - rm -f *.d + rm -f *.a *.so* *.o *.d libv4l2.pc log *~ %.o: %.c $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: - $(CC) $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS) ln -f -s $@.$(LIB_RELEASE) $@ + +%.a: + $(AR) cqs $@ $^ + diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h index 203dcffaf..8724832e1 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h @@ -8,8 +8,8 @@ # # 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. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software @@ -68,8 +68,6 @@ #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; @@ -81,20 +79,14 @@ struct v4l2_dev_info { 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 */ + /* mapping tracking of our fake (converting mmap) frame buffers */ + unsigned char frame_map_count[V4L2_MAX_NO_FRAMES]; }; /* From log.c */ diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index 8dfcf9b71..7029f69c3 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -8,8 +8,8 @@ # # 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. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software @@ -35,12 +35,12 @@ 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 + and when opening /dev/videoX or /dev/v4l/ 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 @@ -72,7 +72,10 @@ /* 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_STREAMON 0x0100 +#define V4L2_BUFFERS_REQUESTED_BY_READ 0x0200 +#define V4L2_STREAM_CONTROLLED_BY_READ 0x0400 +#define V4L2_SUPPORTS_READ 0x0800 #define V4L2_MMAP_OFFSET_MAGIC 0xABCDEF00u @@ -89,52 +92,45 @@ 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; + /* Note we re-request the buffers if they are already requested as the format + and thus the needed buffersize may have changed. */ + req.count = (devices[index].no_frames)? devices[index].no_frames: + 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))){ + if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){ int saved_err = errno; - V4L2_LOG_ERR("requesting buffers: %s\n", strerror(errno)); + V4L2_LOG_ERR("requesting %u buffers: %s\n", req.count, strerror(errno)); errno = saved_err; return result; } + if (!devices[index].no_frames && req.count) + devices[index].flags |= V4L2_BUFFERS_REQUESTED_BY_READ; + devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES); return 0; } -static int v4l2_unrequest_read_buffers(int index) +static void 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; + if (!(devices[index].flags & V4L2_BUFFERS_REQUESTED_BY_READ) || + devices[index].no_frames == 0) + return; - /* (Un)Request buffers */ + /* (Un)Request buffers, note not all driver support this, and those + who do not support it don't need it. */ 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; - } + if(syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req) < 0) + return; 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; + if (devices[index].no_frames == 0) + devices[index].flags &= ~V4L2_BUFFERS_REQUESTED_BY_READ; } static int v4l2_map_buffers(int index) @@ -159,7 +155,7 @@ static int v4l2_map_buffers(int index) } devices[index].frame_pointers[i] = (void *)syscall(SYS_mmap2, NULL, - (size_t)buf.length, PROT_READ, MAP_SHARED, devices[index].fd, + (size_t)buf.length, PROT_READ|PROT_WRITE, 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; @@ -241,6 +237,7 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) if (devices[index].frame_queued & (1 << buffer_index)) return 0; + memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = buffer_index; @@ -255,23 +252,54 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) return 0; } -static int v4l2_dequeue_read_buffer(int index, int *bytesused) +static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, + unsigned char *dest, int dest_size) { - int result; - struct v4l2_buffer buf; + const int max_tries = 10; + int result, tries = max_tries; - 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; + /* Make sure we have the real v4l2 buffers mapped */ + if ((result = v4l2_map_buffers(index))) return result; + + do { + 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); + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].frame_pointers[buf->index], + buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf + + buf->index * V4L2_FRAME_BUF_SIZE), dest_size); + + if (result < 0) { + int saved_err = errno; + + if(errno == EAGAIN) + V4L2_LOG("warning error while converting frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + else + V4L2_LOG_ERR("converting / decoding frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + + v4l2_queue_read_buffer(index, buf->index); + errno = saved_err; + } + tries--; + } while (result < 0 && errno == EAGAIN && tries); + + if (result < 0 && errno == EAGAIN) { + V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s\n", + max_tries, v4lconvert_get_error_message(devices[index].convert)); } - devices[index].frame_queued &= ~(1 << buf.index); - *bytesused = buf.bytesused; - return buf.index; + return result; } static int v4l2_queue_read_buffers(int index) @@ -310,6 +338,8 @@ static int v4l2_activate_read_stream(int index) if ((result = v4l2_queue_read_buffers(index))) return result; + devices[index].flags |= V4L2_STREAM_CONTROLLED_BY_READ; + return result = v4l2_streamon(index); } @@ -324,12 +354,48 @@ static int v4l2_deactivate_read_stream(int index) v4l2_unmap_buffers(index); - if ((result = v4l2_unrequest_read_buffers(index))) - return result; + v4l2_unrequest_read_buffers(index); + + devices[index].flags &= ~V4L2_STREAM_CONTROLLED_BY_READ; return 0; } +static int v4l2_buffers_mapped(int index) +{ + unsigned int i; + + if (devices[index].src_fmt.fmt.pix.pixelformat == + devices[index].dest_fmt.fmt.pix.pixelformat) { + /* Normal (no conversion) mode */ + struct v4l2_buffer buf; + + for (i = 0; i < devices[index].no_frames; i++) { + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf)) { + int saved_err = errno; + V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno)); + errno = saved_err; + break; + } + if (buf.flags & V4L2_BUF_FLAG_MAPPED) + break; + } + } else { + /* Conversion mode */ + for (i = 0; i < devices[index].no_frames; i++) + if (devices[index].frame_map_count[i]) + break; + } + + if (i != devices[index].no_frames) + V4L2_LOG("v4l2_buffers_mapped(): buffers still mapped\n"); + + return i != devices[index].no_frames; +} + int v4l2_open (const char *file, int oflag, ...) { @@ -386,8 +452,10 @@ int v4l2_fd_open(int fd, int v4l2_flags) return -1; } - /* we only add functionality for video capture devices */ - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) + /* we only add functionality for video capture devices, and we do not + handle devices which don't do mmap */ + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || + !(cap.capabilities & V4L2_CAP_STREAMING)) return fd; /* Get current cam format */ @@ -420,6 +488,8 @@ int v4l2_fd_open(int fd, int v4l2_flags) } devices[index].flags = v4l2_flags; + if (cap.capabilities & V4L2_CAP_READWRITE) + devices[index].flags |= V4L2_SUPPORTS_READ; devices[index].open_count = 1; devices[index].src_fmt = fmt; devices[index].dest_fmt = fmt; @@ -428,15 +498,13 @@ int v4l2_fd_open(int fd, int v4l2_flags) 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_map_count[i] = 0; } devices[index].frame_queued = 0; - devices[index].frame_mapped = 0; - devices[index].frame_map_count = 0; if (index >= devices_used) devices_used = index + 1; @@ -487,11 +555,8 @@ int v4l2_close(int fd) 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); + if (v4l2_buffers_mapped(index)) + V4L2_LOG_WARN("v4l2 mmap buffers still mapped on close()\n"); else syscall(SYS_munmap, devices[index].convert_mmap_buf, devices[index].no_frames * V4L2_FRAME_BUF_SIZE); @@ -526,6 +591,34 @@ int v4l2_dup(int fd) return fd; } +static int v4l2_check_buffer_change_ok(int index) +{ + v4l2_unmap_buffers(index); + + /* Check if the app itself still is using the stream */ + if (v4l2_buffers_mapped(index) || + (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) && + ((devices[index].flags & V4L2_STREAMON) || + devices[index].frame_queued))) { + V4L2_LOG("v4l2_check_buffer_change_ok(): stream busy\n"); + errno = EBUSY; + return -1; + } + + /* We may change from convert to non conversion mode and + v4l2_unrequest_read_buffers may change the no_frames, so free the + convert mmap buffer */ + syscall(SYS_munmap, devices[index].convert_mmap_buf, + devices[index].no_frames * V4L2_FRAME_BUF_SIZE); + devices[index].convert_mmap_buf = MAP_FAILED; + + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) { + V4L2_LOG("deactivating read-stream for settings change\n"); + return v4l2_deactivate_read_stream(index); + } + + return 0; +} int v4l2_ioctl (int fd, unsigned long int request, ...) { @@ -548,8 +641,12 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) /* Is this a capture request and do we need to take the stream lock? */ switch (request) { + case VIDIOC_QUERYCAP: + is_capture_request = 1; + break; case VIDIOC_ENUM_FMT: - if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION)) is_capture_request = 1; break; case VIDIOC_TRY_FMT: @@ -598,11 +695,21 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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; - + converting = v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt); switch (request) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYCAP, cap); + if (result == 0) + /* We always support read() as we fake it using mmap mode */ + cap->capabilities |= V4L2_CAP_READWRITE; + } + break; + case VIDIOC_ENUM_FMT: result = v4lconvert_enum_fmt(devices[index].convert, arg); break; @@ -620,17 +727,6 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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); @@ -651,16 +747,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) 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"); - break; - } - } + if ((result = v4l2_check_buffer_change_ok(index))) + break; result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT, &src_fmt); if (result) { @@ -690,22 +778,6 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { struct v4l2_requestbuffers *req = arg; - /* 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; @@ -713,29 +785,20 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; } + if ((result = v4l2_check_buffer_change_ok(index))) + 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) + if (result < 0) break; + result = 0; /* some drivers return the number of buffers on success */ - /* 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; + devices[index].no_frames = MIN(req->count, V4L2_MAX_NO_FRAMES); + devices[index].flags &= ~V4L2_BUFFERS_REQUESTED_BY_READ; } break; @@ -743,6 +806,10 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { struct v4l2_buffer *buf = arg; + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + 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); @@ -751,10 +818,23 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; buf->length = V4L2_FRAME_BUF_SIZE; + if (devices[index].frame_map_count[buf->index]) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + else + buf->flags &= ~V4L2_BUF_FLAG_MAPPED; } break; case VIDIOC_QBUF: + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + break; + + /* With some drivers the buffers must be mapped before queuing */ + if (converting) + if ((result = v4l2_map_buffers(index))) + break; + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, arg); break; @@ -762,14 +842,19 @@ 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) { - V4L2_LOG_ERR("dequeing buffer: %s\n", strerror(errno)); - break; - } + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + break; - if (!converting) + if (!converting) { + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); + if (result) { + int saved_err = errno; + V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); + errno = saved_err; + } break; + } /* An application can do a DQBUF before mmap-ing in the buffer, but we need the buffer _now_ to write our converted data @@ -791,26 +876,17 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } } - /* 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) { - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + result = v4l2_dequeue_and_convert(index, buf, 0, V4L2_FRAME_BUF_SIZE); + if (result < 0) break; - } buf->bytesused = result; + buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; buf->length = V4L2_FRAME_BUF_SIZE; + if (devices[index].frame_map_count[buf->index]) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + else + buf->flags &= ~V4L2_BUF_FLAG_MAPPED; result = 0; } @@ -818,11 +894,9 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: - if (devices[index].io != v4l2_io_mmap) { - errno = EINVAL; - result = -1; - break; - } + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + break; if (request == VIDIOC_STREAMON) result = v4l2_streamon(index); @@ -845,42 +919,43 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } -ssize_t v4l2_read (int fd, void* buffer, size_t n) +ssize_t v4l2_read (int fd, void* dest, size_t n) { ssize_t result; - int index, bytesused = 0, frame_index; + int index; + struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_read, fd, buffer, n); + return syscall(SYS_read, fd, dest, 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; + /* When not converting and the device supports read let the kernel handle + it */ + if ((devices[index].flags & V4L2_SUPPORTS_READ) && + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { + result = syscall(SYS_read, fd, dest, n); 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; + if (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ)) { + if ((devices[index].flags & V4L2_STREAMON) || + devices[index].frame_queued) { + errno = EBUSY; + result = -1; + goto leave; + } + if ((result = v4l2_activate_read_stream(index))) + 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); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + result = v4l2_dequeue_and_convert(index, &buf, dest, n); - if (result < 0) - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + if (result >= 0) + v4l2_queue_read_buffer(index, buf.index); leave: pthread_mutex_unlock(&devices[index].stream_lock); @@ -917,10 +992,9 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, 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) { + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { errno = EINVAL; result = MAP_FAILED; goto leave; @@ -943,8 +1017,7 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, } } - devices[index].frame_mapped |= 1 << buffer_index; - devices[index].frame_map_count++; + devices[index].frame_map_count[buffer_index]++; result = devices[index].convert_mmap_buf + buffer_index * V4L2_FRAME_BUF_SIZE; @@ -985,9 +1058,8 @@ int v4l2_munmap(void *_start, size_t length) 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--; + if (devices[index].frame_map_count[buffer_index] > 0) + devices[index].frame_map_count[buffer_index]--; unmapped = 1; } diff --git a/v4l2-apps/lib/libv4l/libv4l2/log.c b/v4l2-apps/lib/libv4l/libv4l2/log.c index 982a185c6..6237d55ec 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/log.c +++ b/v4l2-apps/lib/libv4l/libv4l2/log.c @@ -8,8 +8,8 @@ # # 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. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software @@ -25,6 +25,8 @@ #include <asm/types.h> /* end broken header workaround includes */ #include <linux/videodev2.h> +#include "libv4l2.h" +#include "libv4l2-priv.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -116,7 +118,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) 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", + fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %ux%u\n", pixfmt & 0xff, (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, @@ -131,6 +133,14 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) } } break; + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *req = arg; + + fprintf(v4l2_log_file, " count: %u type: %d memory: %d\n", + req->count, (int)req->type, (int)req->memory); + } + break; } fprintf(v4l2_log_file, "result == %d\n", result); diff --git a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c index 7db1ca6d6..e8756f338 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c @@ -12,8 +12,8 @@ # # 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. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software @@ -27,6 +27,8 @@ #include <syscall.h> #include <fcntl.h> #include <string.h> +#include <sys/ioctl.h> +#include <sys/mman.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> @@ -41,7 +43,13 @@ #error open/read/mmap is a prepocessor macro !! #endif -int open (const char *file, int oflag, ...) +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open (const char *file, int oflag, ...) { int fd; struct v4l2_capability cap; @@ -66,7 +74,7 @@ int open (const char *file, int oflag, ...) return fd; /* check if we're opening a video4linux2 device */ - if (strncmp(file, "/dev/video", 10)) + if (strncmp(file, "/dev/video", 10) && strncmp(file, "/dev/v4l/", 9)) return fd; /* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */ @@ -84,7 +92,7 @@ int open (const char *file, int oflag, ...) return fd; } -int open64(const char *file, int oflag, ...) +LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) { int fd; @@ -107,17 +115,17 @@ int open64(const char *file, int oflag, ...) return fd; } -int close(int fd) +LIBV4L_PUBLIC int close(int fd) { return v4l2_close(fd); } -int dup(int fd) +LIBV4L_PUBLIC int dup(int fd) { return v4l2_dup(fd); } -int ioctl (int fd, unsigned long int request, ...) +LIBV4L_PUBLIC int ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; @@ -129,24 +137,25 @@ int ioctl (int fd, unsigned long int request, ...) return v4l2_ioctl (fd, request, arg); } -ssize_t read (int fd, void* buffer, size_t n) +LIBV4L_PUBLIC 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, +LIBV4L_PUBLIC 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); } -void mmap64(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC 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) +LIBV4L_PUBLIC int munmap(void *start, size_t length) { return v4l2_munmap(start, length); } + |