From faf2479d2dfa16ad1c7a316f0443df4fa6f9c3c8 Mon Sep 17 00:00:00 2001 From: "hans@rhel5-devel.localdomain" Date: Wed, 5 Aug 2009 12:28:07 +0200 Subject: libv4l: update my email address From: Hans de Goede libv4l: update my email address Priority: normal Signed-off-by: Hans de Goede --- v4l2-apps/libv4l/libv4l2/libv4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'v4l2-apps/libv4l/libv4l2/libv4l2.c') diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index b71c9b4c6..b7e9ae7c4 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/libv4l2/libv4l2.c @@ -1,5 +1,5 @@ /* -# (C) 2008 Hans de Goede +# (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 -- cgit v1.2.3 From 1717b46f2e34d1655a665db31979f4bfaf969c06 Mon Sep 17 00:00:00 2001 From: "hans@rhel5-devel.localdomain" Date: Fri, 14 Aug 2009 09:50:33 +0200 Subject: libv4l: Fix reqbuf Device or Resource busy error when using read From: Hans de Goede Some applications such as v4l2-apps/test/capture-example.c, in read mode use select() together with read() and do a select() before the first read(). This causes issues together with certain drivers (gspca for example), do not allow switching from read mode to mmap mode and they assume read() mode if a select or poll() is done before any buffers are requested. When not using libv4l2, this is not an issue but libv4l2 uses mmap mode under the hood when converting as that safes a memcpy for each frame read. This fails with such drivers when the application has done a select() before the first read() as the driver now is in "read mode" and disallows switching to mmap mode. This patch fixes this by falling back to using read() for v4l2_read() when using mmap mode fails. Priority: normal Signed-off-by: Hans de Goede --- v4l2-apps/libv4l/libv4l2/libv4l2.c | 107 +++++++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 15 deletions(-) (limited to 'v4l2-apps/libv4l/libv4l2/libv4l2.c') diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index b7e9ae7c4..07bd9484e 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/libv4l2/libv4l2.c @@ -77,6 +77,7 @@ #define V4L2_SUPPORTS_READ 0x0800 #define V4L2_IS_UVC 0x1000 #define V4L2_STREAM_TOUCHED 0x2000 +#define V4L2_USE_READ_FOR_READ 0x4000 #define V4L2_MMAP_OFFSET_MAGIC 0xABCDEF00u @@ -101,7 +102,7 @@ static int v4l2_request_read_buffers(int index) req.memory = V4L2_MEMORY_MMAP; if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){ int saved_err = errno; - V4L2_LOG_ERR("requesting %u buffers: %s\n", req.count, strerror(errno)); + V4L2_LOG("warning reqbuf (%u) failed: %s\n", req.count, strerror(errno)); errno = saved_err; return result; } @@ -305,6 +306,63 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, return result; } +static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size) +{ + const int max_tries = 10; + int result, buf_size, tries = max_tries; + + buf_size = devices[index].dest_fmt.fmt.pix.sizeimage; + buf_size = (buf_size + 8191) & ~8191; + + if (devices[index].readbuf_size < buf_size) { + unsigned char *new_buf; + + new_buf = realloc(devices[index].readbuf, buf_size); + if (!new_buf) + return -1; + + devices[index].readbuf = new_buf; + devices[index].readbuf_size = buf_size; + } + + do { + result = SYS_READ(devices[index].fd, devices[index].readbuf, buf_size); + if (result <= 0) { + if (result && errno != EAGAIN) { + int saved_err = errno; + V4L2_LOG_ERR("reading: %s\n", strerror(errno)); + errno = saved_err; + } + return result; + } + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].readbuf, result, dest, 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)); + + 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)); + } + + return result; +} + static int v4l2_queue_read_buffers(int index) { unsigned int i; @@ -332,6 +390,11 @@ static int v4l2_activate_read_stream(int index) { int result; + if ((devices[index].flags & V4L2_STREAMON) || devices[index].frame_queued) { + errno = EBUSY; + return -1; + } + if ((result = v4l2_request_read_buffers(index))) return result; @@ -528,6 +591,8 @@ int v4l2_fd_open(int fd, int v4l2_flags) devices[index].frame_map_count[i] = 0; } devices[index].frame_queued = 0; + devices[index].readbuf = NULL; + devices[index].readbuf_size = 0; if (index >= devices_used) devices_used = index + 1; @@ -585,6 +650,9 @@ int v4l2_close(int fd) devices[index].convert_mmap_buf = MAP_FAILED; } v4lconvert_destroy(devices[index].convert); + free(devices[index].readbuf); + devices[index].readbuf = NULL; + devices[index].readbuf_size = 0; /* 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 @@ -1070,7 +1138,6 @@ ssize_t v4l2_read (int fd, void* dest, size_t n) { ssize_t result; int index; - struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) return SYS_READ(fd, dest, n); @@ -1085,23 +1152,33 @@ ssize_t v4l2_read (int fd, void* dest, size_t n) 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; - } + /* Since we need to do conversion try to use mmap (streaming) mode under + the hood as that safes a memcpy for each frame read. + + Note sometimes this will fail as some drivers (atleast gspca) do not allow + switching from read mode to mmap mode and they assume read() mode if a + select or poll() is done before any buffers are requested. So using mmap + mode under the hood will fail if a select() or poll() is done before the + first emulated read() call. */ + if (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) && + !(devices[index].flags & V4L2_USE_READ_FOR_READ)) { if ((result = v4l2_activate_read_stream(index))) - goto leave; + /* Activating mmap mode failed, use read() instead */ + devices[index].flags |= V4L2_USE_READ_FOR_READ; } - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - result = v4l2_dequeue_and_convert(index, &buf, dest, n); + if (devices[index].flags & V4L2_USE_READ_FOR_READ) { + result = v4l2_read_and_convert(index, dest, n); + } else { + struct v4l2_buffer buf; - if (result >= 0) - v4l2_queue_read_buffer(index, buf.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_queue_read_buffer(index, buf.index); + } leave: pthread_mutex_unlock(&devices[index].stream_lock); -- cgit v1.2.3 From 0201b5f76e609d40a5ac955aab083854bf21d5b7 Mon Sep 17 00:00:00 2001 From: "hans@rhel5-devel.localdomain" Date: Thu, 20 Aug 2009 11:42:57 +0200 Subject: libv4l: make get / set control use libv4lconvert functions From: Hans de Goede libv4l: make get / set control use libv4lconvert functions Priority: normal Signed-off-by: Hans de Goede --- v4l2-apps/libv4l/libv4l2/libv4l2.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'v4l2-apps/libv4l/libv4l2/libv4l2.c') diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index 07bd9484e..c035ae533 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/libv4l2/libv4l2.c @@ -1303,9 +1303,15 @@ int v4l2_set_control(int fd, int cid, int value) { struct v4l2_queryctrl qctrl = { .id = cid }; struct v4l2_control ctrl = { .id = cid }; - int result; + int index, result; + + if ((index = v4l2_get_index(fd)) == -1) { + V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd); + errno = EBADF; + return -1; + } - if ((result = SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl))) + if ((result = v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl))) return result; if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) && @@ -1316,7 +1322,7 @@ int v4l2_set_control(int fd, int cid, int value) ctrl.value = (value * (qctrl.maximum - qctrl.minimum) + 32767) / 65535 + qctrl.minimum; - result = SYS_IOCTL(fd, VIDIOC_S_CTRL, &ctrl); + result = v4lconvert_vidioc_s_ctrl(devices[index].convert, &ctrl); } return result; @@ -1326,14 +1332,21 @@ int v4l2_get_control(int fd, int cid) { struct v4l2_queryctrl qctrl = { .id = cid }; struct v4l2_control ctrl = { .id = cid }; + int index; + + if ((index = v4l2_get_index(fd)) == -1) { + V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd); + errno = EBADF; + return -1; + } - if (SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl)) + if (v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl)) return 0; if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) return 0; - if (SYS_IOCTL(fd, VIDIOC_G_CTRL, &ctrl)) + if (v4lconvert_vidioc_g_ctrl(devices[index].convert, &ctrl)) return 0; return ((ctrl.value - qctrl.minimum) * 65535 + -- cgit v1.2.3 From 8ea6a6c6c6f581cbf41e8813f6edb45acd1181f6 Mon Sep 17 00:00:00 2001 From: "hans@rhel5-devel.localdomain" Date: Thu, 3 Sep 2009 13:28:26 +0200 Subject: libv4l: enable libv4l2 usage with devices which just support read() From: Hans de Goede libv4l: enable libv4l2 usage with devices which just support read(), this allows applications to work with mpeg capture devices like the cx18, which only do read() and only support mpeg and some exotic raw format (which libv4l can convert). Thanks to Simon Farnsworth for testing this. Priority: normal Signed-off-by: Hans de Goede --- v4l2-apps/libv4l/libv4l2/libv4l2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'v4l2-apps/libv4l/libv4l2/libv4l2.c') diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index c035ae533..3366538a7 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/libv4l2/libv4l2.c @@ -312,7 +312,6 @@ static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size) int result, buf_size, tries = max_tries; buf_size = devices[index].dest_fmt.fmt.pix.sizeimage; - buf_size = (buf_size + 8191) & ~8191; if (devices[index].readbuf_size < buf_size) { unsigned char *new_buf; @@ -526,10 +525,9 @@ int v4l2_fd_open(int fd, int v4l2_flags) return -1; } - /* we only add functionality for video capture devices, and we do not - handle devices which don't do mmap */ + /* we only add functionality for video capture devices */ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || - !(cap.capabilities & V4L2_CAP_STREAMING)) + !(cap.capabilities & (V4L2_CAP_STREAMING|V4L2_CAP_READWRITE))) return fd; /* Get current cam format */ @@ -564,6 +562,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; + if (!(cap.capabilities & V4L2_CAP_STREAMING)) + devices[index].flags |= V4L2_USE_READ_FOR_READ; if (!strcmp((char *)cap.driver, "uvcvideo")) devices[index].flags |= V4L2_IS_UVC; devices[index].open_count = 1; @@ -571,7 +571,7 @@ int v4l2_fd_open(int fd, int v4l2_flags) devices[index].dest_fmt = fmt; /* When a user does a try_fmt with the current dest_fmt and the dest_fmt - is a supported one we will align the resulution (see try_fmt for why). + is a supported one we will align the resolution (see try_fmt for why). Do the same here now, so that a try_fmt on the result of a get_fmt done immediately after open leaves the fmt unchanged. */ if (v4lconvert_supported_dst_format( -- cgit v1.2.3