Back to home page

OSCL-LXR

 
 

    


0001 .. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
0002 
0003 file: media/v4l/capture.c
0004 =========================
0005 
0006 .. code-block:: c
0007 
0008     /*
0009      *  V4L2 video capture example
0010      *
0011      *  This program can be used and distributed without restrictions.
0012      *
0013      *      This program is provided with the V4L2 API
0014      * see https://linuxtv.org/docs.php for more information
0015      */
0016 
0017     #include <stdio.h>
0018     #include <stdlib.h>
0019     #include <string.h>
0020     #include <assert.h>
0021 
0022     #include <getopt.h>             /* getopt_long() */
0023 
0024     #include <fcntl.h>              /* low-level i/o */
0025     #include <unistd.h>
0026     #include <errno.h>
0027     #include <sys/stat.h>
0028     #include <sys/types.h>
0029     #include <sys/time.h>
0030     #include <sys/mman.h>
0031     #include <sys/ioctl.h>
0032 
0033     #include <linux/videodev2.h>
0034 
0035     #define CLEAR(x) memset(&(x), 0, sizeof(x))
0036 
0037     enum io_method {
0038             IO_METHOD_READ,
0039             IO_METHOD_MMAP,
0040             IO_METHOD_USERPTR,
0041     };
0042 
0043     struct buffer {
0044             void   *start;
0045             size_t  length;
0046     };
0047 
0048     static char            *dev_name;
0049     static enum io_method   io = IO_METHOD_MMAP;
0050     static int              fd = -1;
0051     struct buffer          *buffers;
0052     static unsigned int     n_buffers;
0053     static int              out_buf;
0054     static int              force_format;
0055     static int              frame_count = 70;
0056 
0057     static void errno_exit(const char *s)
0058     {
0059             fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
0060             exit(EXIT_FAILURE);
0061     }
0062 
0063     static int xioctl(int fh, int request, void *arg)
0064     {
0065             int r;
0066 
0067             do {
0068                     r = ioctl(fh, request, arg);
0069             } while (-1 == r && EINTR == errno);
0070 
0071             return r;
0072     }
0073 
0074     static void process_image(const void *p, int size)
0075     {
0076             if (out_buf)
0077                     fwrite(p, size, 1, stdout);
0078 
0079             fflush(stderr);
0080             fprintf(stderr, ".");
0081             fflush(stdout);
0082     }
0083 
0084     static int read_frame(void)
0085     {
0086             struct v4l2_buffer buf;
0087             unsigned int i;
0088 
0089             switch (io) {
0090             case IO_METHOD_READ:
0091                     if (-1 == read(fd, buffers[0].start, buffers[0].length)) {
0092                             switch (errno) {
0093                             case EAGAIN:
0094                                     return 0;
0095 
0096                             case EIO:
0097                                     /* Could ignore EIO, see spec. */
0098 
0099                                     /* fall through */
0100 
0101                             default:
0102                                     errno_exit("read");
0103                             }
0104                     }
0105 
0106                     process_image(buffers[0].start, buffers[0].length);
0107                     break;
0108 
0109             case IO_METHOD_MMAP:
0110                     CLEAR(buf);
0111 
0112                     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0113                     buf.memory = V4L2_MEMORY_MMAP;
0114 
0115                     if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
0116                             switch (errno) {
0117                             case EAGAIN:
0118                                     return 0;
0119 
0120                             case EIO:
0121                                     /* Could ignore EIO, see spec. */
0122 
0123                                     /* fall through */
0124 
0125                             default:
0126                                     errno_exit("VIDIOC_DQBUF");
0127                             }
0128                     }
0129 
0130                     assert(buf.index < n_buffers);
0131 
0132                     process_image(buffers[buf.index].start, buf.bytesused);
0133 
0134                     if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
0135                             errno_exit("VIDIOC_QBUF");
0136                     break;
0137 
0138             case IO_METHOD_USERPTR:
0139                     CLEAR(buf);
0140 
0141                     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0142                     buf.memory = V4L2_MEMORY_USERPTR;
0143 
0144                     if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
0145                             switch (errno) {
0146                             case EAGAIN:
0147                                     return 0;
0148 
0149                             case EIO:
0150                                     /* Could ignore EIO, see spec. */
0151 
0152                                     /* fall through */
0153 
0154                             default:
0155                                     errno_exit("VIDIOC_DQBUF");
0156                             }
0157                     }
0158 
0159                     for (i = 0; i < n_buffers; ++i)
0160                             if (buf.m.userptr == (unsigned long)buffers[i].start
0161                                 && buf.length == buffers[i].length)
0162                                     break;
0163 
0164                     assert(i < n_buffers);
0165 
0166                     process_image((void *)buf.m.userptr, buf.bytesused);
0167 
0168                     if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
0169                             errno_exit("VIDIOC_QBUF");
0170                     break;
0171             }
0172 
0173             return 1;
0174     }
0175 
0176     static void mainloop(void)
0177     {
0178             unsigned int count;
0179 
0180             count = frame_count;
0181 
0182             while (count-- > 0) {
0183                     for (;;) {
0184                             fd_set fds;
0185                             struct timeval tv;
0186                             int r;
0187 
0188                             FD_ZERO(&fds);
0189                             FD_SET(fd, &fds);
0190 
0191                             /* Timeout. */
0192                             tv.tv_sec = 2;
0193                             tv.tv_usec = 0;
0194 
0195                             r = select(fd + 1, &fds, NULL, NULL, &tv);
0196 
0197                             if (-1 == r) {
0198                                     if (EINTR == errno)
0199                                             continue;
0200                                     errno_exit("select");
0201                             }
0202 
0203                             if (0 == r) {
0204                                     fprintf(stderr, "select timeout\n");
0205                                     exit(EXIT_FAILURE);
0206                             }
0207 
0208                             if (read_frame())
0209                                     break;
0210                             /* EAGAIN - continue select loop. */
0211                     }
0212             }
0213     }
0214 
0215     static void stop_capturing(void)
0216     {
0217             enum v4l2_buf_type type;
0218 
0219             switch (io) {
0220             case IO_METHOD_READ:
0221                     /* Nothing to do. */
0222                     break;
0223 
0224             case IO_METHOD_MMAP:
0225             case IO_METHOD_USERPTR:
0226                     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0227                     if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
0228                             errno_exit("VIDIOC_STREAMOFF");
0229                     break;
0230             }
0231     }
0232 
0233     static void start_capturing(void)
0234     {
0235             unsigned int i;
0236             enum v4l2_buf_type type;
0237 
0238             switch (io) {
0239             case IO_METHOD_READ:
0240                     /* Nothing to do. */
0241                     break;
0242 
0243             case IO_METHOD_MMAP:
0244                     for (i = 0; i < n_buffers; ++i) {
0245                             struct v4l2_buffer buf;
0246 
0247                             CLEAR(buf);
0248                             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0249                             buf.memory = V4L2_MEMORY_MMAP;
0250                             buf.index = i;
0251 
0252                             if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
0253                                     errno_exit("VIDIOC_QBUF");
0254                     }
0255                     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0256                     if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
0257                             errno_exit("VIDIOC_STREAMON");
0258                     break;
0259 
0260             case IO_METHOD_USERPTR:
0261                     for (i = 0; i < n_buffers; ++i) {
0262                             struct v4l2_buffer buf;
0263 
0264                             CLEAR(buf);
0265                             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0266                             buf.memory = V4L2_MEMORY_USERPTR;
0267                             buf.index = i;
0268                             buf.m.userptr = (unsigned long)buffers[i].start;
0269                             buf.length = buffers[i].length;
0270 
0271                             if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
0272                                     errno_exit("VIDIOC_QBUF");
0273                     }
0274                     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0275                     if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
0276                             errno_exit("VIDIOC_STREAMON");
0277                     break;
0278             }
0279     }
0280 
0281     static void uninit_device(void)
0282     {
0283             unsigned int i;
0284 
0285             switch (io) {
0286             case IO_METHOD_READ:
0287                     free(buffers[0].start);
0288                     break;
0289 
0290             case IO_METHOD_MMAP:
0291                     for (i = 0; i < n_buffers; ++i)
0292                             if (-1 == munmap(buffers[i].start, buffers[i].length))
0293                                     errno_exit("munmap");
0294                     break;
0295 
0296             case IO_METHOD_USERPTR:
0297                     for (i = 0; i < n_buffers; ++i)
0298                             free(buffers[i].start);
0299                     break;
0300             }
0301 
0302             free(buffers);
0303     }
0304 
0305     static void init_read(unsigned int buffer_size)
0306     {
0307             buffers = calloc(1, sizeof(*buffers));
0308 
0309             if (!buffers) {
0310                     fprintf(stderr, "Out of memory\n");
0311                     exit(EXIT_FAILURE);
0312             }
0313 
0314             buffers[0].length = buffer_size;
0315             buffers[0].start = malloc(buffer_size);
0316 
0317             if (!buffers[0].start) {
0318                     fprintf(stderr, "Out of memory\n");
0319                     exit(EXIT_FAILURE);
0320             }
0321     }
0322 
0323     static void init_mmap(void)
0324     {
0325             struct v4l2_requestbuffers req;
0326 
0327             CLEAR(req);
0328 
0329             req.count = 4;
0330             req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0331             req.memory = V4L2_MEMORY_MMAP;
0332 
0333             if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
0334                     if (EINVAL == errno) {
0335                             fprintf(stderr, "%s does not support "
0336                                      "memory mappingn", dev_name);
0337                             exit(EXIT_FAILURE);
0338                     } else {
0339                             errno_exit("VIDIOC_REQBUFS");
0340                     }
0341             }
0342 
0343             if (req.count < 2) {
0344                     fprintf(stderr, "Insufficient buffer memory on %s\n",
0345                              dev_name);
0346                     exit(EXIT_FAILURE);
0347             }
0348 
0349             buffers = calloc(req.count, sizeof(*buffers));
0350 
0351             if (!buffers) {
0352                     fprintf(stderr, "Out of memory\n");
0353                     exit(EXIT_FAILURE);
0354             }
0355 
0356             for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
0357                     struct v4l2_buffer buf;
0358 
0359                     CLEAR(buf);
0360 
0361                     buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0362                     buf.memory      = V4L2_MEMORY_MMAP;
0363                     buf.index       = n_buffers;
0364 
0365                     if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
0366                             errno_exit("VIDIOC_QUERYBUF");
0367 
0368                     buffers[n_buffers].length = buf.length;
0369                     buffers[n_buffers].start =
0370                             mmap(NULL /* start anywhere */,
0371                                   buf.length,
0372                                   PROT_READ | PROT_WRITE /* required */,
0373                                   MAP_SHARED /* recommended */,
0374                                   fd, buf.m.offset);
0375 
0376                     if (MAP_FAILED == buffers[n_buffers].start)
0377                             errno_exit("mmap");
0378             }
0379     }
0380 
0381     static void init_userp(unsigned int buffer_size)
0382     {
0383             struct v4l2_requestbuffers req;
0384 
0385             CLEAR(req);
0386 
0387             req.count  = 4;
0388             req.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0389             req.memory = V4L2_MEMORY_USERPTR;
0390 
0391             if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
0392                     if (EINVAL == errno) {
0393                             fprintf(stderr, "%s does not support "
0394                                      "user pointer i/on", dev_name);
0395                             exit(EXIT_FAILURE);
0396                     } else {
0397                             errno_exit("VIDIOC_REQBUFS");
0398                     }
0399             }
0400 
0401             buffers = calloc(4, sizeof(*buffers));
0402 
0403             if (!buffers) {
0404                     fprintf(stderr, "Out of memory\n");
0405                     exit(EXIT_FAILURE);
0406             }
0407 
0408             for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
0409                     buffers[n_buffers].length = buffer_size;
0410                     buffers[n_buffers].start = malloc(buffer_size);
0411 
0412                     if (!buffers[n_buffers].start) {
0413                             fprintf(stderr, "Out of memory\n");
0414                             exit(EXIT_FAILURE);
0415                     }
0416             }
0417     }
0418 
0419     static void init_device(void)
0420     {
0421             struct v4l2_capability cap;
0422             struct v4l2_cropcap cropcap;
0423             struct v4l2_crop crop;
0424             struct v4l2_format fmt;
0425             unsigned int min;
0426 
0427             if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
0428                     if (EINVAL == errno) {
0429                             fprintf(stderr, "%s is no V4L2 device\n",
0430                                      dev_name);
0431                             exit(EXIT_FAILURE);
0432                     } else {
0433                             errno_exit("VIDIOC_QUERYCAP");
0434                     }
0435             }
0436 
0437             if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
0438                     fprintf(stderr, "%s is no video capture device\n",
0439                              dev_name);
0440                     exit(EXIT_FAILURE);
0441             }
0442 
0443             switch (io) {
0444             case IO_METHOD_READ:
0445                     if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
0446                             fprintf(stderr, "%s does not support read i/o\n",
0447                                      dev_name);
0448                             exit(EXIT_FAILURE);
0449                     }
0450                     break;
0451 
0452             case IO_METHOD_MMAP:
0453             case IO_METHOD_USERPTR:
0454                     if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
0455                             fprintf(stderr, "%s does not support streaming i/o\n",
0456                                      dev_name);
0457                             exit(EXIT_FAILURE);
0458                     }
0459                     break;
0460             }
0461 
0462 
0463             /* Select video input, video standard and tune here. */
0464 
0465 
0466             CLEAR(cropcap);
0467 
0468             cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0469 
0470             if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
0471                     crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0472                     crop.c = cropcap.defrect; /* reset to default */
0473 
0474                     if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
0475                             switch (errno) {
0476                             case EINVAL:
0477                                     /* Cropping not supported. */
0478                                     break;
0479                             default:
0480                                     /* Errors ignored. */
0481                                     break;
0482                             }
0483                     }
0484             } else {
0485                     /* Errors ignored. */
0486             }
0487 
0488 
0489             CLEAR(fmt);
0490 
0491             fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0492             if (force_format) {
0493                     fmt.fmt.pix.width       = 640;
0494                     fmt.fmt.pix.height      = 480;
0495                     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
0496                     fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
0497 
0498                     if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
0499                             errno_exit("VIDIOC_S_FMT");
0500 
0501                     /* Note VIDIOC_S_FMT may change width and height. */
0502             } else {
0503                     /* Preserve original settings as set by v4l2-ctl for example */
0504                     if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
0505                             errno_exit("VIDIOC_G_FMT");
0506             }
0507 
0508             /* Buggy driver paranoia. */
0509             min = fmt.fmt.pix.width * 2;
0510             if (fmt.fmt.pix.bytesperline < min)
0511                     fmt.fmt.pix.bytesperline = min;
0512             min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
0513             if (fmt.fmt.pix.sizeimage < min)
0514                     fmt.fmt.pix.sizeimage = min;
0515 
0516             switch (io) {
0517             case IO_METHOD_READ:
0518                     init_read(fmt.fmt.pix.sizeimage);
0519                     break;
0520 
0521             case IO_METHOD_MMAP:
0522                     init_mmap();
0523                     break;
0524 
0525             case IO_METHOD_USERPTR:
0526                     init_userp(fmt.fmt.pix.sizeimage);
0527                     break;
0528             }
0529     }
0530 
0531     static void close_device(void)
0532     {
0533             if (-1 == close(fd))
0534                     errno_exit("close");
0535 
0536             fd = -1;
0537     }
0538 
0539     static void open_device(void)
0540     {
0541             struct stat st;
0542 
0543             if (-1 == stat(dev_name, &st)) {
0544                     fprintf(stderr, "Cannot identify '%s': %d, %s\n",
0545                              dev_name, errno, strerror(errno));
0546                     exit(EXIT_FAILURE);
0547             }
0548 
0549             if (!S_ISCHR(st.st_mode)) {
0550                     fprintf(stderr, "%s is no devicen", dev_name);
0551                     exit(EXIT_FAILURE);
0552             }
0553 
0554             fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
0555 
0556             if (-1 == fd) {
0557                     fprintf(stderr, "Cannot open '%s': %d, %s\n",
0558                              dev_name, errno, strerror(errno));
0559                     exit(EXIT_FAILURE);
0560             }
0561     }
0562 
0563     static void usage(FILE *fp, int argc, char **argv)
0564     {
0565             fprintf(fp,
0566                      "Usage: %s [options]\n\n"
0567                      "Version 1.3\n"
0568                      "Options:\n"
0569                      "-d | --device name   Video device name [%s]\n"
0570                      "-h | --help          Print this message\n"
0571                      "-m | --mmap          Use memory mapped buffers [default]\n"
0572                      "-r | --read          Use read() calls\n"
0573                      "-u | --userp         Use application allocated buffers\n"
0574                      "-o | --output        Outputs stream to stdout\n"
0575                      "-f | --format        Force format to 640x480 YUYV\n"
0576                      "-c | --count         Number of frames to grab [%i]\n"
0577                      "",
0578                      argv[0], dev_name, frame_count);
0579     }
0580 
0581     static const char short_options[] = "d:hmruofc:";
0582 
0583     static const struct option
0584     long_options[] = {
0585             { "device", required_argument, NULL, 'd' },
0586             { "help",   no_argument,       NULL, 'h' },
0587             { "mmap",   no_argument,       NULL, 'm' },
0588             { "read",   no_argument,       NULL, 'r' },
0589             { "userp",  no_argument,       NULL, 'u' },
0590             { "output", no_argument,       NULL, 'o' },
0591             { "format", no_argument,       NULL, 'f' },
0592             { "count",  required_argument, NULL, 'c' },
0593             { 0, 0, 0, 0 }
0594     };
0595 
0596     int main(int argc, char **argv)
0597     {
0598             dev_name = "/dev/video0";
0599 
0600             for (;;) {
0601                     int idx;
0602                     int c;
0603 
0604                     c = getopt_long(argc, argv,
0605                                     short_options, long_options, &idx);
0606 
0607                     if (-1 == c)
0608                             break;
0609 
0610                     switch (c) {
0611                     case 0: /* getopt_long() flag */
0612                             break;
0613 
0614                     case 'd':
0615                             dev_name = optarg;
0616                             break;
0617 
0618                     case 'h':
0619                             usage(stdout, argc, argv);
0620                             exit(EXIT_SUCCESS);
0621 
0622                     case 'm':
0623                             io = IO_METHOD_MMAP;
0624                             break;
0625 
0626                     case 'r':
0627                             io = IO_METHOD_READ;
0628                             break;
0629 
0630                     case 'u':
0631                             io = IO_METHOD_USERPTR;
0632                             break;
0633 
0634                     case 'o':
0635                             out_buf++;
0636                             break;
0637 
0638                     case 'f':
0639                             force_format++;
0640                             break;
0641 
0642                     case 'c':
0643                             errno = 0;
0644                             frame_count = strtol(optarg, NULL, 0);
0645                             if (errno)
0646                                     errno_exit(optarg);
0647                             break;
0648 
0649                     default:
0650                             usage(stderr, argc, argv);
0651                             exit(EXIT_FAILURE);
0652                     }
0653             }
0654 
0655             open_device();
0656             init_device();
0657             start_capturing();
0658             mainloop();
0659             stop_capturing();
0660             uninit_device();
0661             close_device();
0662             fprintf(stderr, "\n");
0663             return 0;
0664     }