summaryrefslogtreecommitdiff
path: root/v4l2-apps/libv4l/libv4l2/libv4l2.c
diff options
context:
space:
mode:
Diffstat (limited to 'v4l2-apps/libv4l/libv4l2/libv4l2.c')
-rw-r--r--v4l2-apps/libv4l/libv4l2/libv4l2.c107
1 files changed, 92 insertions, 15 deletions
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);