162306a36Sopenharmony_ci.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later 262306a36Sopenharmony_ci 362306a36Sopenharmony_cifile: media/v4l/capture.c 462306a36Sopenharmony_ci========================= 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci.. code-block:: c 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci /* 962306a36Sopenharmony_ci * V4L2 video capture example 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This program can be used and distributed without restrictions. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This program is provided with the V4L2 API 1462306a36Sopenharmony_ci * see https://linuxtv.org/docs.php for more information 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci #include <stdio.h> 1862306a36Sopenharmony_ci #include <stdlib.h> 1962306a36Sopenharmony_ci #include <string.h> 2062306a36Sopenharmony_ci #include <assert.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci #include <getopt.h> /* getopt_long() */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci #include <fcntl.h> /* low-level i/o */ 2562306a36Sopenharmony_ci #include <unistd.h> 2662306a36Sopenharmony_ci #include <errno.h> 2762306a36Sopenharmony_ci #include <sys/stat.h> 2862306a36Sopenharmony_ci #include <sys/types.h> 2962306a36Sopenharmony_ci #include <sys/time.h> 3062306a36Sopenharmony_ci #include <sys/mman.h> 3162306a36Sopenharmony_ci #include <sys/ioctl.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci #include <linux/videodev2.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci #define CLEAR(x) memset(&(x), 0, sizeof(x)) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci enum io_method { 3862306a36Sopenharmony_ci IO_METHOD_READ, 3962306a36Sopenharmony_ci IO_METHOD_MMAP, 4062306a36Sopenharmony_ci IO_METHOD_USERPTR, 4162306a36Sopenharmony_ci }; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci struct buffer { 4462306a36Sopenharmony_ci void *start; 4562306a36Sopenharmony_ci size_t length; 4662306a36Sopenharmony_ci }; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci static char *dev_name; 4962306a36Sopenharmony_ci static enum io_method io = IO_METHOD_MMAP; 5062306a36Sopenharmony_ci static int fd = -1; 5162306a36Sopenharmony_ci struct buffer *buffers; 5262306a36Sopenharmony_ci static unsigned int n_buffers; 5362306a36Sopenharmony_ci static int out_buf; 5462306a36Sopenharmony_ci static int force_format; 5562306a36Sopenharmony_ci static int frame_count = 70; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci static void errno_exit(const char *s) 5862306a36Sopenharmony_ci { 5962306a36Sopenharmony_ci fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); 6062306a36Sopenharmony_ci exit(EXIT_FAILURE); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci static int xioctl(int fh, int request, void *arg) 6462306a36Sopenharmony_ci { 6562306a36Sopenharmony_ci int r; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci do { 6862306a36Sopenharmony_ci r = ioctl(fh, request, arg); 6962306a36Sopenharmony_ci } while (-1 == r && EINTR == errno); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return r; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci static void process_image(const void *p, int size) 7562306a36Sopenharmony_ci { 7662306a36Sopenharmony_ci if (out_buf) 7762306a36Sopenharmony_ci fwrite(p, size, 1, stdout); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci fflush(stderr); 8062306a36Sopenharmony_ci fprintf(stderr, "."); 8162306a36Sopenharmony_ci fflush(stdout); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci static int read_frame(void) 8562306a36Sopenharmony_ci { 8662306a36Sopenharmony_ci struct v4l2_buffer buf; 8762306a36Sopenharmony_ci unsigned int i; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci switch (io) { 9062306a36Sopenharmony_ci case IO_METHOD_READ: 9162306a36Sopenharmony_ci if (-1 == read(fd, buffers[0].start, buffers[0].length)) { 9262306a36Sopenharmony_ci switch (errno) { 9362306a36Sopenharmony_ci case EAGAIN: 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci case EIO: 9762306a36Sopenharmony_ci /* Could ignore EIO, see spec. */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* fall through */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci default: 10262306a36Sopenharmony_ci errno_exit("read"); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci process_image(buffers[0].start, buffers[0].length); 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci case IO_METHOD_MMAP: 11062306a36Sopenharmony_ci CLEAR(buf); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 11362306a36Sopenharmony_ci buf.memory = V4L2_MEMORY_MMAP; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { 11662306a36Sopenharmony_ci switch (errno) { 11762306a36Sopenharmony_ci case EAGAIN: 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci case EIO: 12162306a36Sopenharmony_ci /* Could ignore EIO, see spec. */ 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* fall through */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci default: 12662306a36Sopenharmony_ci errno_exit("VIDIOC_DQBUF"); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci assert(buf.index < n_buffers); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci process_image(buffers[buf.index].start, buf.bytesused); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 13562306a36Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci case IO_METHOD_USERPTR: 13962306a36Sopenharmony_ci CLEAR(buf); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 14262306a36Sopenharmony_ci buf.memory = V4L2_MEMORY_USERPTR; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { 14562306a36Sopenharmony_ci switch (errno) { 14662306a36Sopenharmony_ci case EAGAIN: 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci case EIO: 15062306a36Sopenharmony_ci /* Could ignore EIO, see spec. */ 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* fall through */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci errno_exit("VIDIOC_DQBUF"); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci for (i = 0; i < n_buffers; ++i) 16062306a36Sopenharmony_ci if (buf.m.userptr == (unsigned long)buffers[i].start 16162306a36Sopenharmony_ci && buf.length == buffers[i].length) 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci assert(i < n_buffers); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci process_image((void *)buf.m.userptr, buf.bytesused); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 16962306a36Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return 1; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci static void mainloop(void) 17762306a36Sopenharmony_ci { 17862306a36Sopenharmony_ci unsigned int count; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci count = frame_count; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci while (count-- > 0) { 18362306a36Sopenharmony_ci for (;;) { 18462306a36Sopenharmony_ci fd_set fds; 18562306a36Sopenharmony_ci struct timeval tv; 18662306a36Sopenharmony_ci int r; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci FD_ZERO(&fds); 18962306a36Sopenharmony_ci FD_SET(fd, &fds); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Timeout. */ 19262306a36Sopenharmony_ci tv.tv_sec = 2; 19362306a36Sopenharmony_ci tv.tv_usec = 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci r = select(fd + 1, &fds, NULL, NULL, &tv); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (-1 == r) { 19862306a36Sopenharmony_ci if (EINTR == errno) 19962306a36Sopenharmony_ci continue; 20062306a36Sopenharmony_ci errno_exit("select"); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (0 == r) { 20462306a36Sopenharmony_ci fprintf(stderr, "select timeout\n"); 20562306a36Sopenharmony_ci exit(EXIT_FAILURE); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (read_frame()) 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci /* EAGAIN - continue select loop. */ 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci static void stop_capturing(void) 21662306a36Sopenharmony_ci { 21762306a36Sopenharmony_ci enum v4l2_buf_type type; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci switch (io) { 22062306a36Sopenharmony_ci case IO_METHOD_READ: 22162306a36Sopenharmony_ci /* Nothing to do. */ 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci case IO_METHOD_MMAP: 22562306a36Sopenharmony_ci case IO_METHOD_USERPTR: 22662306a36Sopenharmony_ci type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 22762306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) 22862306a36Sopenharmony_ci errno_exit("VIDIOC_STREAMOFF"); 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci static void start_capturing(void) 23462306a36Sopenharmony_ci { 23562306a36Sopenharmony_ci unsigned int i; 23662306a36Sopenharmony_ci enum v4l2_buf_type type; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci switch (io) { 23962306a36Sopenharmony_ci case IO_METHOD_READ: 24062306a36Sopenharmony_ci /* Nothing to do. */ 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci case IO_METHOD_MMAP: 24462306a36Sopenharmony_ci for (i = 0; i < n_buffers; ++i) { 24562306a36Sopenharmony_ci struct v4l2_buffer buf; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci CLEAR(buf); 24862306a36Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 24962306a36Sopenharmony_ci buf.memory = V4L2_MEMORY_MMAP; 25062306a36Sopenharmony_ci buf.index = i; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 25362306a36Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 25662306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) 25762306a36Sopenharmony_ci errno_exit("VIDIOC_STREAMON"); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci case IO_METHOD_USERPTR: 26162306a36Sopenharmony_ci for (i = 0; i < n_buffers; ++i) { 26262306a36Sopenharmony_ci struct v4l2_buffer buf; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci CLEAR(buf); 26562306a36Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 26662306a36Sopenharmony_ci buf.memory = V4L2_MEMORY_USERPTR; 26762306a36Sopenharmony_ci buf.index = i; 26862306a36Sopenharmony_ci buf.m.userptr = (unsigned long)buffers[i].start; 26962306a36Sopenharmony_ci buf.length = buffers[i].length; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 27262306a36Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 27562306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) 27662306a36Sopenharmony_ci errno_exit("VIDIOC_STREAMON"); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci static void uninit_device(void) 28262306a36Sopenharmony_ci { 28362306a36Sopenharmony_ci unsigned int i; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci switch (io) { 28662306a36Sopenharmony_ci case IO_METHOD_READ: 28762306a36Sopenharmony_ci free(buffers[0].start); 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci case IO_METHOD_MMAP: 29162306a36Sopenharmony_ci for (i = 0; i < n_buffers; ++i) 29262306a36Sopenharmony_ci if (-1 == munmap(buffers[i].start, buffers[i].length)) 29362306a36Sopenharmony_ci errno_exit("munmap"); 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci case IO_METHOD_USERPTR: 29762306a36Sopenharmony_ci for (i = 0; i < n_buffers; ++i) 29862306a36Sopenharmony_ci free(buffers[i].start); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci free(buffers); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci static void init_read(unsigned int buffer_size) 30662306a36Sopenharmony_ci { 30762306a36Sopenharmony_ci buffers = calloc(1, sizeof(*buffers)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!buffers) { 31062306a36Sopenharmony_ci fprintf(stderr, "Out of memory\n"); 31162306a36Sopenharmony_ci exit(EXIT_FAILURE); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci buffers[0].length = buffer_size; 31562306a36Sopenharmony_ci buffers[0].start = malloc(buffer_size); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!buffers[0].start) { 31862306a36Sopenharmony_ci fprintf(stderr, "Out of memory\n"); 31962306a36Sopenharmony_ci exit(EXIT_FAILURE); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci static void init_mmap(void) 32462306a36Sopenharmony_ci { 32562306a36Sopenharmony_ci struct v4l2_requestbuffers req; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci CLEAR(req); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci req.count = 4; 33062306a36Sopenharmony_ci req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 33162306a36Sopenharmony_ci req.memory = V4L2_MEMORY_MMAP; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { 33462306a36Sopenharmony_ci if (EINVAL == errno) { 33562306a36Sopenharmony_ci fprintf(stderr, "%s does not support " 33662306a36Sopenharmony_ci "memory mappingn", dev_name); 33762306a36Sopenharmony_ci exit(EXIT_FAILURE); 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci errno_exit("VIDIOC_REQBUFS"); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (req.count < 2) { 34462306a36Sopenharmony_ci fprintf(stderr, "Insufficient buffer memory on %s\n", 34562306a36Sopenharmony_ci dev_name); 34662306a36Sopenharmony_ci exit(EXIT_FAILURE); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci buffers = calloc(req.count, sizeof(*buffers)); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!buffers) { 35262306a36Sopenharmony_ci fprintf(stderr, "Out of memory\n"); 35362306a36Sopenharmony_ci exit(EXIT_FAILURE); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { 35762306a36Sopenharmony_ci struct v4l2_buffer buf; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci CLEAR(buf); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 36262306a36Sopenharmony_ci buf.memory = V4L2_MEMORY_MMAP; 36362306a36Sopenharmony_ci buf.index = n_buffers; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) 36662306a36Sopenharmony_ci errno_exit("VIDIOC_QUERYBUF"); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci buffers[n_buffers].length = buf.length; 36962306a36Sopenharmony_ci buffers[n_buffers].start = 37062306a36Sopenharmony_ci mmap(NULL /* start anywhere */, 37162306a36Sopenharmony_ci buf.length, 37262306a36Sopenharmony_ci PROT_READ | PROT_WRITE /* required */, 37362306a36Sopenharmony_ci MAP_SHARED /* recommended */, 37462306a36Sopenharmony_ci fd, buf.m.offset); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (MAP_FAILED == buffers[n_buffers].start) 37762306a36Sopenharmony_ci errno_exit("mmap"); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci static void init_userp(unsigned int buffer_size) 38262306a36Sopenharmony_ci { 38362306a36Sopenharmony_ci struct v4l2_requestbuffers req; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci CLEAR(req); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci req.count = 4; 38862306a36Sopenharmony_ci req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 38962306a36Sopenharmony_ci req.memory = V4L2_MEMORY_USERPTR; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { 39262306a36Sopenharmony_ci if (EINVAL == errno) { 39362306a36Sopenharmony_ci fprintf(stderr, "%s does not support " 39462306a36Sopenharmony_ci "user pointer i/on", dev_name); 39562306a36Sopenharmony_ci exit(EXIT_FAILURE); 39662306a36Sopenharmony_ci } else { 39762306a36Sopenharmony_ci errno_exit("VIDIOC_REQBUFS"); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci buffers = calloc(4, sizeof(*buffers)); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (!buffers) { 40462306a36Sopenharmony_ci fprintf(stderr, "Out of memory\n"); 40562306a36Sopenharmony_ci exit(EXIT_FAILURE); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci for (n_buffers = 0; n_buffers < 4; ++n_buffers) { 40962306a36Sopenharmony_ci buffers[n_buffers].length = buffer_size; 41062306a36Sopenharmony_ci buffers[n_buffers].start = malloc(buffer_size); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!buffers[n_buffers].start) { 41362306a36Sopenharmony_ci fprintf(stderr, "Out of memory\n"); 41462306a36Sopenharmony_ci exit(EXIT_FAILURE); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci static void init_device(void) 42062306a36Sopenharmony_ci { 42162306a36Sopenharmony_ci struct v4l2_capability cap; 42262306a36Sopenharmony_ci struct v4l2_cropcap cropcap; 42362306a36Sopenharmony_ci struct v4l2_crop crop; 42462306a36Sopenharmony_ci struct v4l2_format fmt; 42562306a36Sopenharmony_ci unsigned int min; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { 42862306a36Sopenharmony_ci if (EINVAL == errno) { 42962306a36Sopenharmony_ci fprintf(stderr, "%s is no V4L2 device\n", 43062306a36Sopenharmony_ci dev_name); 43162306a36Sopenharmony_ci exit(EXIT_FAILURE); 43262306a36Sopenharmony_ci } else { 43362306a36Sopenharmony_ci errno_exit("VIDIOC_QUERYCAP"); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 43862306a36Sopenharmony_ci fprintf(stderr, "%s is no video capture device\n", 43962306a36Sopenharmony_ci dev_name); 44062306a36Sopenharmony_ci exit(EXIT_FAILURE); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci switch (io) { 44462306a36Sopenharmony_ci case IO_METHOD_READ: 44562306a36Sopenharmony_ci if (!(cap.capabilities & V4L2_CAP_READWRITE)) { 44662306a36Sopenharmony_ci fprintf(stderr, "%s does not support read i/o\n", 44762306a36Sopenharmony_ci dev_name); 44862306a36Sopenharmony_ci exit(EXIT_FAILURE); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci case IO_METHOD_MMAP: 45362306a36Sopenharmony_ci case IO_METHOD_USERPTR: 45462306a36Sopenharmony_ci if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 45562306a36Sopenharmony_ci fprintf(stderr, "%s does not support streaming i/o\n", 45662306a36Sopenharmony_ci dev_name); 45762306a36Sopenharmony_ci exit(EXIT_FAILURE); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Select video input, video standard and tune here. */ 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci CLEAR(cropcap); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { 47162306a36Sopenharmony_ci crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 47262306a36Sopenharmony_ci crop.c = cropcap.defrect; /* reset to default */ 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { 47562306a36Sopenharmony_ci switch (errno) { 47662306a36Sopenharmony_ci case EINVAL: 47762306a36Sopenharmony_ci /* Cropping not supported. */ 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci default: 48062306a36Sopenharmony_ci /* Errors ignored. */ 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci /* Errors ignored. */ 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci CLEAR(fmt); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 49262306a36Sopenharmony_ci if (force_format) { 49362306a36Sopenharmony_ci fmt.fmt.pix.width = 640; 49462306a36Sopenharmony_ci fmt.fmt.pix.height = 480; 49562306a36Sopenharmony_ci fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 49662306a36Sopenharmony_ci fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) 49962306a36Sopenharmony_ci errno_exit("VIDIOC_S_FMT"); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Note VIDIOC_S_FMT may change width and height. */ 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci /* Preserve original settings as set by v4l2-ctl for example */ 50462306a36Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) 50562306a36Sopenharmony_ci errno_exit("VIDIOC_G_FMT"); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Buggy driver paranoia. */ 50962306a36Sopenharmony_ci min = fmt.fmt.pix.width * 2; 51062306a36Sopenharmony_ci if (fmt.fmt.pix.bytesperline < min) 51162306a36Sopenharmony_ci fmt.fmt.pix.bytesperline = min; 51262306a36Sopenharmony_ci min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; 51362306a36Sopenharmony_ci if (fmt.fmt.pix.sizeimage < min) 51462306a36Sopenharmony_ci fmt.fmt.pix.sizeimage = min; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci switch (io) { 51762306a36Sopenharmony_ci case IO_METHOD_READ: 51862306a36Sopenharmony_ci init_read(fmt.fmt.pix.sizeimage); 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci case IO_METHOD_MMAP: 52262306a36Sopenharmony_ci init_mmap(); 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci case IO_METHOD_USERPTR: 52662306a36Sopenharmony_ci init_userp(fmt.fmt.pix.sizeimage); 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci static void close_device(void) 53262306a36Sopenharmony_ci { 53362306a36Sopenharmony_ci if (-1 == close(fd)) 53462306a36Sopenharmony_ci errno_exit("close"); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci fd = -1; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci static void open_device(void) 54062306a36Sopenharmony_ci { 54162306a36Sopenharmony_ci struct stat st; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (-1 == stat(dev_name, &st)) { 54462306a36Sopenharmony_ci fprintf(stderr, "Cannot identify '%s': %d, %s\n", 54562306a36Sopenharmony_ci dev_name, errno, strerror(errno)); 54662306a36Sopenharmony_ci exit(EXIT_FAILURE); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!S_ISCHR(st.st_mode)) { 55062306a36Sopenharmony_ci fprintf(stderr, "%s is no devicen", dev_name); 55162306a36Sopenharmony_ci exit(EXIT_FAILURE); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (-1 == fd) { 55762306a36Sopenharmony_ci fprintf(stderr, "Cannot open '%s': %d, %s\n", 55862306a36Sopenharmony_ci dev_name, errno, strerror(errno)); 55962306a36Sopenharmony_ci exit(EXIT_FAILURE); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci static void usage(FILE *fp, int argc, char **argv) 56462306a36Sopenharmony_ci { 56562306a36Sopenharmony_ci fprintf(fp, 56662306a36Sopenharmony_ci "Usage: %s [options]\n\n" 56762306a36Sopenharmony_ci "Version 1.3\n" 56862306a36Sopenharmony_ci "Options:\n" 56962306a36Sopenharmony_ci "-d | --device name Video device name [%s]\n" 57062306a36Sopenharmony_ci "-h | --help Print this message\n" 57162306a36Sopenharmony_ci "-m | --mmap Use memory mapped buffers [default]\n" 57262306a36Sopenharmony_ci "-r | --read Use read() calls\n" 57362306a36Sopenharmony_ci "-u | --userp Use application allocated buffers\n" 57462306a36Sopenharmony_ci "-o | --output Outputs stream to stdout\n" 57562306a36Sopenharmony_ci "-f | --format Force format to 640x480 YUYV\n" 57662306a36Sopenharmony_ci "-c | --count Number of frames to grab [%i]\n" 57762306a36Sopenharmony_ci "", 57862306a36Sopenharmony_ci argv[0], dev_name, frame_count); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci static const char short_options[] = "d:hmruofc:"; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci static const struct option 58462306a36Sopenharmony_ci long_options[] = { 58562306a36Sopenharmony_ci { "device", required_argument, NULL, 'd' }, 58662306a36Sopenharmony_ci { "help", no_argument, NULL, 'h' }, 58762306a36Sopenharmony_ci { "mmap", no_argument, NULL, 'm' }, 58862306a36Sopenharmony_ci { "read", no_argument, NULL, 'r' }, 58962306a36Sopenharmony_ci { "userp", no_argument, NULL, 'u' }, 59062306a36Sopenharmony_ci { "output", no_argument, NULL, 'o' }, 59162306a36Sopenharmony_ci { "format", no_argument, NULL, 'f' }, 59262306a36Sopenharmony_ci { "count", required_argument, NULL, 'c' }, 59362306a36Sopenharmony_ci { 0, 0, 0, 0 } 59462306a36Sopenharmony_ci }; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci int main(int argc, char **argv) 59762306a36Sopenharmony_ci { 59862306a36Sopenharmony_ci dev_name = "/dev/video0"; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci for (;;) { 60162306a36Sopenharmony_ci int idx; 60262306a36Sopenharmony_ci int c; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci c = getopt_long(argc, argv, 60562306a36Sopenharmony_ci short_options, long_options, &idx); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (-1 == c) 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci switch (c) { 61162306a36Sopenharmony_ci case 0: /* getopt_long() flag */ 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci case 'd': 61562306a36Sopenharmony_ci dev_name = optarg; 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci case 'h': 61962306a36Sopenharmony_ci usage(stdout, argc, argv); 62062306a36Sopenharmony_ci exit(EXIT_SUCCESS); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci case 'm': 62362306a36Sopenharmony_ci io = IO_METHOD_MMAP; 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci case 'r': 62762306a36Sopenharmony_ci io = IO_METHOD_READ; 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci case 'u': 63162306a36Sopenharmony_ci io = IO_METHOD_USERPTR; 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci case 'o': 63562306a36Sopenharmony_ci out_buf++; 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci case 'f': 63962306a36Sopenharmony_ci force_format++; 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci case 'c': 64362306a36Sopenharmony_ci errno = 0; 64462306a36Sopenharmony_ci frame_count = strtol(optarg, NULL, 0); 64562306a36Sopenharmony_ci if (errno) 64662306a36Sopenharmony_ci errno_exit(optarg); 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci default: 65062306a36Sopenharmony_ci usage(stderr, argc, argv); 65162306a36Sopenharmony_ci exit(EXIT_FAILURE); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci open_device(); 65662306a36Sopenharmony_ci init_device(); 65762306a36Sopenharmony_ci start_capturing(); 65862306a36Sopenharmony_ci mainloop(); 65962306a36Sopenharmony_ci stop_capturing(); 66062306a36Sopenharmony_ci uninit_device(); 66162306a36Sopenharmony_ci close_device(); 66262306a36Sopenharmony_ci fprintf(stderr, "\n"); 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci } 665