18c2ecf20Sopenharmony_ci.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_cifile: media/v4l/capture.c 48c2ecf20Sopenharmony_ci========================= 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci.. code-block:: c 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci /* 98c2ecf20Sopenharmony_ci * V4L2 video capture example 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program can be used and distributed without restrictions. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is provided with the V4L2 API 148c2ecf20Sopenharmony_ci * see https://linuxtv.org/docs.php for more information 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci #include <stdio.h> 188c2ecf20Sopenharmony_ci #include <stdlib.h> 198c2ecf20Sopenharmony_ci #include <string.h> 208c2ecf20Sopenharmony_ci #include <assert.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci #include <getopt.h> /* getopt_long() */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci #include <fcntl.h> /* low-level i/o */ 258c2ecf20Sopenharmony_ci #include <unistd.h> 268c2ecf20Sopenharmony_ci #include <errno.h> 278c2ecf20Sopenharmony_ci #include <sys/stat.h> 288c2ecf20Sopenharmony_ci #include <sys/types.h> 298c2ecf20Sopenharmony_ci #include <sys/time.h> 308c2ecf20Sopenharmony_ci #include <sys/mman.h> 318c2ecf20Sopenharmony_ci #include <sys/ioctl.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci #include <linux/videodev2.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci #define CLEAR(x) memset(&(x), 0, sizeof(x)) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci enum io_method { 388c2ecf20Sopenharmony_ci IO_METHOD_READ, 398c2ecf20Sopenharmony_ci IO_METHOD_MMAP, 408c2ecf20Sopenharmony_ci IO_METHOD_USERPTR, 418c2ecf20Sopenharmony_ci }; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci struct buffer { 448c2ecf20Sopenharmony_ci void *start; 458c2ecf20Sopenharmony_ci size_t length; 468c2ecf20Sopenharmony_ci }; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci static char *dev_name; 498c2ecf20Sopenharmony_ci static enum io_method io = IO_METHOD_MMAP; 508c2ecf20Sopenharmony_ci static int fd = -1; 518c2ecf20Sopenharmony_ci struct buffer *buffers; 528c2ecf20Sopenharmony_ci static unsigned int n_buffers; 538c2ecf20Sopenharmony_ci static int out_buf; 548c2ecf20Sopenharmony_ci static int force_format; 558c2ecf20Sopenharmony_ci static int frame_count = 70; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci static void errno_exit(const char *s) 588c2ecf20Sopenharmony_ci { 598c2ecf20Sopenharmony_ci fprintf(stderr, "%s error %d, %s\\n", s, errno, strerror(errno)); 608c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci static int xioctl(int fh, int request, void *arg) 648c2ecf20Sopenharmony_ci { 658c2ecf20Sopenharmony_ci int r; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci do { 688c2ecf20Sopenharmony_ci r = ioctl(fh, request, arg); 698c2ecf20Sopenharmony_ci } while (-1 == r && EINTR == errno); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return r; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci static void process_image(const void *p, int size) 758c2ecf20Sopenharmony_ci { 768c2ecf20Sopenharmony_ci if (out_buf) 778c2ecf20Sopenharmony_ci fwrite(p, size, 1, stdout); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci fflush(stderr); 808c2ecf20Sopenharmony_ci fprintf(stderr, "."); 818c2ecf20Sopenharmony_ci fflush(stdout); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci static int read_frame(void) 858c2ecf20Sopenharmony_ci { 868c2ecf20Sopenharmony_ci struct v4l2_buffer buf; 878c2ecf20Sopenharmony_ci unsigned int i; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci switch (io) { 908c2ecf20Sopenharmony_ci case IO_METHOD_READ: 918c2ecf20Sopenharmony_ci if (-1 == read(fd, buffers[0].start, buffers[0].length)) { 928c2ecf20Sopenharmony_ci switch (errno) { 938c2ecf20Sopenharmony_ci case EAGAIN: 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci case EIO: 978c2ecf20Sopenharmony_ci /* Could ignore EIO, see spec. */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* fall through */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci default: 1028c2ecf20Sopenharmony_ci errno_exit("read"); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci process_image(buffers[0].start, buffers[0].length); 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci case IO_METHOD_MMAP: 1108c2ecf20Sopenharmony_ci CLEAR(buf); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1138c2ecf20Sopenharmony_ci buf.memory = V4L2_MEMORY_MMAP; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { 1168c2ecf20Sopenharmony_ci switch (errno) { 1178c2ecf20Sopenharmony_ci case EAGAIN: 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci case EIO: 1218c2ecf20Sopenharmony_ci /* Could ignore EIO, see spec. */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* fall through */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci default: 1268c2ecf20Sopenharmony_ci errno_exit("VIDIOC_DQBUF"); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci assert(buf.index < n_buffers); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci process_image(buffers[buf.index].start, buf.bytesused); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 1358c2ecf20Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci case IO_METHOD_USERPTR: 1398c2ecf20Sopenharmony_ci CLEAR(buf); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1428c2ecf20Sopenharmony_ci buf.memory = V4L2_MEMORY_USERPTR; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { 1458c2ecf20Sopenharmony_ci switch (errno) { 1468c2ecf20Sopenharmony_ci case EAGAIN: 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci case EIO: 1508c2ecf20Sopenharmony_ci /* Could ignore EIO, see spec. */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* fall through */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci errno_exit("VIDIOC_DQBUF"); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci for (i = 0; i < n_buffers; ++i) 1608c2ecf20Sopenharmony_ci if (buf.m.userptr == (unsigned long)buffers[i].start 1618c2ecf20Sopenharmony_ci && buf.length == buffers[i].length) 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci assert(i < n_buffers); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci process_image((void *)buf.m.userptr, buf.bytesused); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 1698c2ecf20Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 1; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci static void mainloop(void) 1778c2ecf20Sopenharmony_ci { 1788c2ecf20Sopenharmony_ci unsigned int count; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci count = frame_count; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci while (count-- > 0) { 1838c2ecf20Sopenharmony_ci for (;;) { 1848c2ecf20Sopenharmony_ci fd_set fds; 1858c2ecf20Sopenharmony_ci struct timeval tv; 1868c2ecf20Sopenharmony_ci int r; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci FD_ZERO(&fds); 1898c2ecf20Sopenharmony_ci FD_SET(fd, &fds); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Timeout. */ 1928c2ecf20Sopenharmony_ci tv.tv_sec = 2; 1938c2ecf20Sopenharmony_ci tv.tv_usec = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci r = select(fd + 1, &fds, NULL, NULL, &tv); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (-1 == r) { 1988c2ecf20Sopenharmony_ci if (EINTR == errno) 1998c2ecf20Sopenharmony_ci continue; 2008c2ecf20Sopenharmony_ci errno_exit("select"); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (0 == r) { 2048c2ecf20Sopenharmony_ci fprintf(stderr, "select timeout\\n"); 2058c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (read_frame()) 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci /* EAGAIN - continue select loop. */ 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci static void stop_capturing(void) 2168c2ecf20Sopenharmony_ci { 2178c2ecf20Sopenharmony_ci enum v4l2_buf_type type; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci switch (io) { 2208c2ecf20Sopenharmony_ci case IO_METHOD_READ: 2218c2ecf20Sopenharmony_ci /* Nothing to do. */ 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci case IO_METHOD_MMAP: 2258c2ecf20Sopenharmony_ci case IO_METHOD_USERPTR: 2268c2ecf20Sopenharmony_ci type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2278c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) 2288c2ecf20Sopenharmony_ci errno_exit("VIDIOC_STREAMOFF"); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci static void start_capturing(void) 2348c2ecf20Sopenharmony_ci { 2358c2ecf20Sopenharmony_ci unsigned int i; 2368c2ecf20Sopenharmony_ci enum v4l2_buf_type type; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci switch (io) { 2398c2ecf20Sopenharmony_ci case IO_METHOD_READ: 2408c2ecf20Sopenharmony_ci /* Nothing to do. */ 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci case IO_METHOD_MMAP: 2448c2ecf20Sopenharmony_ci for (i = 0; i < n_buffers; ++i) { 2458c2ecf20Sopenharmony_ci struct v4l2_buffer buf; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci CLEAR(buf); 2488c2ecf20Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2498c2ecf20Sopenharmony_ci buf.memory = V4L2_MEMORY_MMAP; 2508c2ecf20Sopenharmony_ci buf.index = i; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 2538c2ecf20Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2568c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) 2578c2ecf20Sopenharmony_ci errno_exit("VIDIOC_STREAMON"); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci case IO_METHOD_USERPTR: 2618c2ecf20Sopenharmony_ci for (i = 0; i < n_buffers; ++i) { 2628c2ecf20Sopenharmony_ci struct v4l2_buffer buf; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci CLEAR(buf); 2658c2ecf20Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2668c2ecf20Sopenharmony_ci buf.memory = V4L2_MEMORY_USERPTR; 2678c2ecf20Sopenharmony_ci buf.index = i; 2688c2ecf20Sopenharmony_ci buf.m.userptr = (unsigned long)buffers[i].start; 2698c2ecf20Sopenharmony_ci buf.length = buffers[i].length; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 2728c2ecf20Sopenharmony_ci errno_exit("VIDIOC_QBUF"); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2758c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) 2768c2ecf20Sopenharmony_ci errno_exit("VIDIOC_STREAMON"); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci static void uninit_device(void) 2828c2ecf20Sopenharmony_ci { 2838c2ecf20Sopenharmony_ci unsigned int i; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci switch (io) { 2868c2ecf20Sopenharmony_ci case IO_METHOD_READ: 2878c2ecf20Sopenharmony_ci free(buffers[0].start); 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci case IO_METHOD_MMAP: 2918c2ecf20Sopenharmony_ci for (i = 0; i < n_buffers; ++i) 2928c2ecf20Sopenharmony_ci if (-1 == munmap(buffers[i].start, buffers[i].length)) 2938c2ecf20Sopenharmony_ci errno_exit("munmap"); 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci case IO_METHOD_USERPTR: 2978c2ecf20Sopenharmony_ci for (i = 0; i < n_buffers; ++i) 2988c2ecf20Sopenharmony_ci free(buffers[i].start); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci free(buffers); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci static void init_read(unsigned int buffer_size) 3068c2ecf20Sopenharmony_ci { 3078c2ecf20Sopenharmony_ci buffers = calloc(1, sizeof(*buffers)); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!buffers) { 3108c2ecf20Sopenharmony_ci fprintf(stderr, "Out of memory\\n"); 3118c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci buffers[0].length = buffer_size; 3158c2ecf20Sopenharmony_ci buffers[0].start = malloc(buffer_size); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!buffers[0].start) { 3188c2ecf20Sopenharmony_ci fprintf(stderr, "Out of memory\\n"); 3198c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci static void init_mmap(void) 3248c2ecf20Sopenharmony_ci { 3258c2ecf20Sopenharmony_ci struct v4l2_requestbuffers req; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci CLEAR(req); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci req.count = 4; 3308c2ecf20Sopenharmony_ci req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3318c2ecf20Sopenharmony_ci req.memory = V4L2_MEMORY_MMAP; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { 3348c2ecf20Sopenharmony_ci if (EINVAL == errno) { 3358c2ecf20Sopenharmony_ci fprintf(stderr, "%s does not support " 3368c2ecf20Sopenharmony_ci "memory mappingn", dev_name); 3378c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 3388c2ecf20Sopenharmony_ci } else { 3398c2ecf20Sopenharmony_ci errno_exit("VIDIOC_REQBUFS"); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (req.count < 2) { 3448c2ecf20Sopenharmony_ci fprintf(stderr, "Insufficient buffer memory on %s\\n", 3458c2ecf20Sopenharmony_ci dev_name); 3468c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci buffers = calloc(req.count, sizeof(*buffers)); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!buffers) { 3528c2ecf20Sopenharmony_ci fprintf(stderr, "Out of memory\\n"); 3538c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { 3578c2ecf20Sopenharmony_ci struct v4l2_buffer buf; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci CLEAR(buf); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3628c2ecf20Sopenharmony_ci buf.memory = V4L2_MEMORY_MMAP; 3638c2ecf20Sopenharmony_ci buf.index = n_buffers; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) 3668c2ecf20Sopenharmony_ci errno_exit("VIDIOC_QUERYBUF"); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci buffers[n_buffers].length = buf.length; 3698c2ecf20Sopenharmony_ci buffers[n_buffers].start = 3708c2ecf20Sopenharmony_ci mmap(NULL /* start anywhere */, 3718c2ecf20Sopenharmony_ci buf.length, 3728c2ecf20Sopenharmony_ci PROT_READ | PROT_WRITE /* required */, 3738c2ecf20Sopenharmony_ci MAP_SHARED /* recommended */, 3748c2ecf20Sopenharmony_ci fd, buf.m.offset); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (MAP_FAILED == buffers[n_buffers].start) 3778c2ecf20Sopenharmony_ci errno_exit("mmap"); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci static void init_userp(unsigned int buffer_size) 3828c2ecf20Sopenharmony_ci { 3838c2ecf20Sopenharmony_ci struct v4l2_requestbuffers req; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci CLEAR(req); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci req.count = 4; 3888c2ecf20Sopenharmony_ci req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3898c2ecf20Sopenharmony_ci req.memory = V4L2_MEMORY_USERPTR; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { 3928c2ecf20Sopenharmony_ci if (EINVAL == errno) { 3938c2ecf20Sopenharmony_ci fprintf(stderr, "%s does not support " 3948c2ecf20Sopenharmony_ci "user pointer i/on", dev_name); 3958c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci errno_exit("VIDIOC_REQBUFS"); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci buffers = calloc(4, sizeof(*buffers)); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (!buffers) { 4048c2ecf20Sopenharmony_ci fprintf(stderr, "Out of memory\\n"); 4058c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for (n_buffers = 0; n_buffers < 4; ++n_buffers) { 4098c2ecf20Sopenharmony_ci buffers[n_buffers].length = buffer_size; 4108c2ecf20Sopenharmony_ci buffers[n_buffers].start = malloc(buffer_size); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!buffers[n_buffers].start) { 4138c2ecf20Sopenharmony_ci fprintf(stderr, "Out of memory\\n"); 4148c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci static void init_device(void) 4208c2ecf20Sopenharmony_ci { 4218c2ecf20Sopenharmony_ci struct v4l2_capability cap; 4228c2ecf20Sopenharmony_ci struct v4l2_cropcap cropcap; 4238c2ecf20Sopenharmony_ci struct v4l2_crop crop; 4248c2ecf20Sopenharmony_ci struct v4l2_format fmt; 4258c2ecf20Sopenharmony_ci unsigned int min; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { 4288c2ecf20Sopenharmony_ci if (EINVAL == errno) { 4298c2ecf20Sopenharmony_ci fprintf(stderr, "%s is no V4L2 device\\n", 4308c2ecf20Sopenharmony_ci dev_name); 4318c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 4328c2ecf20Sopenharmony_ci } else { 4338c2ecf20Sopenharmony_ci errno_exit("VIDIOC_QUERYCAP"); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 4388c2ecf20Sopenharmony_ci fprintf(stderr, "%s is no video capture device\\n", 4398c2ecf20Sopenharmony_ci dev_name); 4408c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci switch (io) { 4448c2ecf20Sopenharmony_ci case IO_METHOD_READ: 4458c2ecf20Sopenharmony_ci if (!(cap.capabilities & V4L2_CAP_READWRITE)) { 4468c2ecf20Sopenharmony_ci fprintf(stderr, "%s does not support read i/o\\n", 4478c2ecf20Sopenharmony_ci dev_name); 4488c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci case IO_METHOD_MMAP: 4538c2ecf20Sopenharmony_ci case IO_METHOD_USERPTR: 4548c2ecf20Sopenharmony_ci if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 4558c2ecf20Sopenharmony_ci fprintf(stderr, "%s does not support streaming i/o\\n", 4568c2ecf20Sopenharmony_ci dev_name); 4578c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Select video input, video standard and tune here. */ 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci CLEAR(cropcap); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { 4718c2ecf20Sopenharmony_ci crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 4728c2ecf20Sopenharmony_ci crop.c = cropcap.defrect; /* reset to default */ 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { 4758c2ecf20Sopenharmony_ci switch (errno) { 4768c2ecf20Sopenharmony_ci case EINVAL: 4778c2ecf20Sopenharmony_ci /* Cropping not supported. */ 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci default: 4808c2ecf20Sopenharmony_ci /* Errors ignored. */ 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci } else { 4858c2ecf20Sopenharmony_ci /* Errors ignored. */ 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci CLEAR(fmt); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 4928c2ecf20Sopenharmony_ci if (force_format) { 4938c2ecf20Sopenharmony_ci fmt.fmt.pix.width = 640; 4948c2ecf20Sopenharmony_ci fmt.fmt.pix.height = 480; 4958c2ecf20Sopenharmony_ci fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 4968c2ecf20Sopenharmony_ci fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) 4998c2ecf20Sopenharmony_ci errno_exit("VIDIOC_S_FMT"); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Note VIDIOC_S_FMT may change width and height. */ 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci /* Preserve original settings as set by v4l2-ctl for example */ 5048c2ecf20Sopenharmony_ci if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) 5058c2ecf20Sopenharmony_ci errno_exit("VIDIOC_G_FMT"); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Buggy driver paranoia. */ 5098c2ecf20Sopenharmony_ci min = fmt.fmt.pix.width * 2; 5108c2ecf20Sopenharmony_ci if (fmt.fmt.pix.bytesperline < min) 5118c2ecf20Sopenharmony_ci fmt.fmt.pix.bytesperline = min; 5128c2ecf20Sopenharmony_ci min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; 5138c2ecf20Sopenharmony_ci if (fmt.fmt.pix.sizeimage < min) 5148c2ecf20Sopenharmony_ci fmt.fmt.pix.sizeimage = min; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci switch (io) { 5178c2ecf20Sopenharmony_ci case IO_METHOD_READ: 5188c2ecf20Sopenharmony_ci init_read(fmt.fmt.pix.sizeimage); 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci case IO_METHOD_MMAP: 5228c2ecf20Sopenharmony_ci init_mmap(); 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci case IO_METHOD_USERPTR: 5268c2ecf20Sopenharmony_ci init_userp(fmt.fmt.pix.sizeimage); 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci static void close_device(void) 5328c2ecf20Sopenharmony_ci { 5338c2ecf20Sopenharmony_ci if (-1 == close(fd)) 5348c2ecf20Sopenharmony_ci errno_exit("close"); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci fd = -1; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci static void open_device(void) 5408c2ecf20Sopenharmony_ci { 5418c2ecf20Sopenharmony_ci struct stat st; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (-1 == stat(dev_name, &st)) { 5448c2ecf20Sopenharmony_ci fprintf(stderr, "Cannot identify '%s': %d, %s\\n", 5458c2ecf20Sopenharmony_ci dev_name, errno, strerror(errno)); 5468c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (!S_ISCHR(st.st_mode)) { 5508c2ecf20Sopenharmony_ci fprintf(stderr, "%s is no devicen", dev_name); 5518c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (-1 == fd) { 5578c2ecf20Sopenharmony_ci fprintf(stderr, "Cannot open '%s': %d, %s\\n", 5588c2ecf20Sopenharmony_ci dev_name, errno, strerror(errno)); 5598c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci static void usage(FILE *fp, int argc, char **argv) 5648c2ecf20Sopenharmony_ci { 5658c2ecf20Sopenharmony_ci fprintf(fp, 5668c2ecf20Sopenharmony_ci "Usage: %s [options]\\n\\n" 5678c2ecf20Sopenharmony_ci "Version 1.3\\n" 5688c2ecf20Sopenharmony_ci "Options:\\n" 5698c2ecf20Sopenharmony_ci "-d | --device name Video device name [%s]n" 5708c2ecf20Sopenharmony_ci "-h | --help Print this messagen" 5718c2ecf20Sopenharmony_ci "-m | --mmap Use memory mapped buffers [default]n" 5728c2ecf20Sopenharmony_ci "-r | --read Use read() callsn" 5738c2ecf20Sopenharmony_ci "-u | --userp Use application allocated buffersn" 5748c2ecf20Sopenharmony_ci "-o | --output Outputs stream to stdoutn" 5758c2ecf20Sopenharmony_ci "-f | --format Force format to 640x480 YUYVn" 5768c2ecf20Sopenharmony_ci "-c | --count Number of frames to grab [%i]n" 5778c2ecf20Sopenharmony_ci "", 5788c2ecf20Sopenharmony_ci argv[0], dev_name, frame_count); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci static const char short_options[] = "d:hmruofc:"; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci static const struct option 5848c2ecf20Sopenharmony_ci long_options[] = { 5858c2ecf20Sopenharmony_ci { "device", required_argument, NULL, 'd' }, 5868c2ecf20Sopenharmony_ci { "help", no_argument, NULL, 'h' }, 5878c2ecf20Sopenharmony_ci { "mmap", no_argument, NULL, 'm' }, 5888c2ecf20Sopenharmony_ci { "read", no_argument, NULL, 'r' }, 5898c2ecf20Sopenharmony_ci { "userp", no_argument, NULL, 'u' }, 5908c2ecf20Sopenharmony_ci { "output", no_argument, NULL, 'o' }, 5918c2ecf20Sopenharmony_ci { "format", no_argument, NULL, 'f' }, 5928c2ecf20Sopenharmony_ci { "count", required_argument, NULL, 'c' }, 5938c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 5948c2ecf20Sopenharmony_ci }; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci int main(int argc, char **argv) 5978c2ecf20Sopenharmony_ci { 5988c2ecf20Sopenharmony_ci dev_name = "/dev/video0"; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci for (;;) { 6018c2ecf20Sopenharmony_ci int idx; 6028c2ecf20Sopenharmony_ci int c; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci c = getopt_long(argc, argv, 6058c2ecf20Sopenharmony_ci short_options, long_options, &idx); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (-1 == c) 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci switch (c) { 6118c2ecf20Sopenharmony_ci case 0: /* getopt_long() flag */ 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci case 'd': 6158c2ecf20Sopenharmony_ci dev_name = optarg; 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci case 'h': 6198c2ecf20Sopenharmony_ci usage(stdout, argc, argv); 6208c2ecf20Sopenharmony_ci exit(EXIT_SUCCESS); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci case 'm': 6238c2ecf20Sopenharmony_ci io = IO_METHOD_MMAP; 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci case 'r': 6278c2ecf20Sopenharmony_ci io = IO_METHOD_READ; 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci case 'u': 6318c2ecf20Sopenharmony_ci io = IO_METHOD_USERPTR; 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci case 'o': 6358c2ecf20Sopenharmony_ci out_buf++; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci case 'f': 6398c2ecf20Sopenharmony_ci force_format++; 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci case 'c': 6438c2ecf20Sopenharmony_ci errno = 0; 6448c2ecf20Sopenharmony_ci frame_count = strtol(optarg, NULL, 0); 6458c2ecf20Sopenharmony_ci if (errno) 6468c2ecf20Sopenharmony_ci errno_exit(optarg); 6478c2ecf20Sopenharmony_ci break; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci default: 6508c2ecf20Sopenharmony_ci usage(stderr, argc, argv); 6518c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci open_device(); 6568c2ecf20Sopenharmony_ci init_device(); 6578c2ecf20Sopenharmony_ci start_capturing(); 6588c2ecf20Sopenharmony_ci mainloop(); 6598c2ecf20Sopenharmony_ci stop_capturing(); 6608c2ecf20Sopenharmony_ci uninit_device(); 6618c2ecf20Sopenharmony_ci close_device(); 6628c2ecf20Sopenharmony_ci fprintf(stderr, "\\n"); 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci } 665