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