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 }