1 .. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later 2 3 file: media/v4l/capture.c 4 ========================= 5 6 .. code-block:: c 7 8 /* 9 * V4L2 video capture example 10 * 11 * This program can be used and distributed without restrictions. 12 * 13 * This program is provided with the V4L2 API 14 * see https://linuxtv.org/docs.php for more information 15 */ 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <assert.h> 21 22 #include <getopt.h> /* getopt_long() */ 23 24 #include <fcntl.h> /* low-level i/o */ 25 #include <unistd.h> 26 #include <errno.h> 27 #include <sys/stat.h> 28 #include <sys/types.h> 29 #include <sys/time.h> 30 #include <sys/mman.h> 31 #include <sys/ioctl.h> 32 33 #include <linux/videodev2.h> 34 35 #define CLEAR(x) memset(&(x), 0, sizeof(x)) 36 37 enum io_method { 38 IO_METHOD_READ, 39 IO_METHOD_MMAP, 40 IO_METHOD_USERPTR, 41 }; 42 43 struct buffer { 44 void *start; 45 size_t length; 46 }; 47 48 static char *dev_name; 49 static enum io_method io = IO_METHOD_MMAP; 50 static int fd = -1; 51 struct buffer *buffers; 52 static unsigned int n_buffers; 53 static int out_buf; 54 static int force_format; 55 static int frame_count = 70; 56 57 static void errno_exit(const char *s) 58 { 59 fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); 60 exit(EXIT_FAILURE); 61 } 62 63 static int xioctl(int fh, int request, void *arg) 64 { 65 int r; 66 67 do { 68 r = ioctl(fh, request, arg); 69 } while (-1 == r && EINTR == errno); 70 71 return r; 72 } 73 74 static void process_image(const void *p, int size) 75 { 76 if (out_buf) 77 fwrite(p, size, 1, stdout); 78 79 fflush(stderr); 80 fprintf(stderr, "."); 81 fflush(stdout); 82 } 83 84 static int read_frame(void) 85 { 86 struct v4l2_buffer buf; 87 unsigned int i; 88 89 switch (io) { 90 case IO_METHOD_READ: 91 if (-1 == read(fd, buffers[0].start, buffers[0].length)) { 92 switch (errno) { 93 case EAGAIN: 94 return 0; 95 96 case EIO: 97 /* Could ignore EIO, see spec. */ 98 99 /* fall through */ 100 101 default: 102 errno_exit("read"); 103 } 104 } 105 106 process_image(buffers[0].start, buffers[0].length); 107 break; 108 109 case IO_METHOD_MMAP: 110 CLEAR(buf); 111 112 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 113 buf.memory = V4L2_MEMORY_MMAP; 114 115 if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { 116 switch (errno) { 117 case EAGAIN: 118 return 0; 119 120 case EIO: 121 /* Could ignore EIO, see spec. */ 122 123 /* fall through */ 124 125 default: 126 errno_exit("VIDIOC_DQBUF"); 127 } 128 } 129 130 assert(buf.index < n_buffers); 131 132 process_image(buffers[buf.index].start, buf.bytesused); 133 134 if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 135 errno_exit("VIDIOC_QBUF"); 136 break; 137 138 case IO_METHOD_USERPTR: 139 CLEAR(buf); 140 141 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 142 buf.memory = V4L2_MEMORY_USERPTR; 143 144 if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { 145 switch (errno) { 146 case EAGAIN: 147 return 0; 148 149 case EIO: 150 /* Could ignore EIO, see spec. */ 151 152 /* fall through */ 153 154 default: 155 errno_exit("VIDIOC_DQBUF"); 156 } 157 } 158 159 for (i = 0; i < n_buffers; ++i) 160 if (buf.m.userptr == (unsigned long)buffers[i].start 161 && buf.length == buffers[i].length) 162 break; 163 164 assert(i < n_buffers); 165 166 process_image((void *)buf.m.userptr, buf.bytesused); 167 168 if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 169 errno_exit("VIDIOC_QBUF"); 170 break; 171 } 172 173 return 1; 174 } 175 176 static void mainloop(void) 177 { 178 unsigned int count; 179 180 count = frame_count; 181 182 while (count-- > 0) { 183 for (;;) { 184 fd_set fds; 185 struct timeval tv; 186 int r; 187 188 FD_ZERO(&fds); 189 FD_SET(fd, &fds); 190 191 /* Timeout. */ 192 tv.tv_sec = 2; 193 tv.tv_usec = 0; 194 195 r = select(fd + 1, &fds, NULL, NULL, &tv); 196 197 if (-1 == r) { 198 if (EINTR == errno) 199 continue; 200 errno_exit("select"); 201 } 202 203 if (0 == r) { 204 fprintf(stderr, "select timeout\n"); 205 exit(EXIT_FAILURE); 206 } 207 208 if (read_frame()) 209 break; 210 /* EAGAIN - continue select loop. */ 211 } 212 } 213 } 214 215 static void stop_capturing(void) 216 { 217 enum v4l2_buf_type type; 218 219 switch (io) { 220 case IO_METHOD_READ: 221 /* Nothing to do. */ 222 break; 223 224 case IO_METHOD_MMAP: 225 case IO_METHOD_USERPTR: 226 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 227 if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) 228 errno_exit("VIDIOC_STREAMOFF"); 229 break; 230 } 231 } 232 233 static void start_capturing(void) 234 { 235 unsigned int i; 236 enum v4l2_buf_type type; 237 238 switch (io) { 239 case IO_METHOD_READ: 240 /* Nothing to do. */ 241 break; 242 243 case IO_METHOD_MMAP: 244 for (i = 0; i < n_buffers; ++i) { 245 struct v4l2_buffer buf; 246 247 CLEAR(buf); 248 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 249 buf.memory = V4L2_MEMORY_MMAP; 250 buf.index = i; 251 252 if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 253 errno_exit("VIDIOC_QBUF"); 254 } 255 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 256 if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) 257 errno_exit("VIDIOC_STREAMON"); 258 break; 259 260 case IO_METHOD_USERPTR: 261 for (i = 0; i < n_buffers; ++i) { 262 struct v4l2_buffer buf; 263 264 CLEAR(buf); 265 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 266 buf.memory = V4L2_MEMORY_USERPTR; 267 buf.index = i; 268 buf.m.userptr = (unsigned long)buffers[i].start; 269 buf.length = buffers[i].length; 270 271 if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 272 errno_exit("VIDIOC_QBUF"); 273 } 274 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 275 if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) 276 errno_exit("VIDIOC_STREAMON"); 277 break; 278 } 279 } 280 281 static void uninit_device(void) 282 { 283 unsigned int i; 284 285 switch (io) { 286 case IO_METHOD_READ: 287 free(buffers[0].start); 288 break; 289 290 case IO_METHOD_MMAP: 291 for (i = 0; i < n_buffers; ++i) 292 if (-1 == munmap(buffers[i].start, buffers[i].length)) 293 errno_exit("munmap"); 294 break; 295 296 case IO_METHOD_USERPTR: 297 for (i = 0; i < n_buffers; ++i) 298 free(buffers[i].start); 299 break; 300 } 301 302 free(buffers); 303 } 304 305 static void init_read(unsigned int buffer_size) 306 { 307 buffers = calloc(1, sizeof(*buffers)); 308 309 if (!buffers) { 310 fprintf(stderr, "Out of memory\n"); 311 exit(EXIT_FAILURE); 312 } 313 314 buffers[0].length = buffer_size; 315 buffers[0].start = malloc(buffer_size); 316 317 if (!buffers[0].start) { 318 fprintf(stderr, "Out of memory\n"); 319 exit(EXIT_FAILURE); 320 } 321 } 322 323 static void init_mmap(void) 324 { 325 struct v4l2_requestbuffers req; 326 327 CLEAR(req); 328 329 req.count = 4; 330 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 331 req.memory = V4L2_MEMORY_MMAP; 332 333 if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { 334 if (EINVAL == errno) { 335 fprintf(stderr, "%s does not support " 336 "memory mapping\n", dev_name); 337 exit(EXIT_FAILURE); 338 } else { 339 errno_exit("VIDIOC_REQBUFS"); 340 } 341 } 342 343 if (req.count < 2) { 344 fprintf(stderr, "Insufficient buffer memory on %s\n", 345 dev_name); 346 exit(EXIT_FAILURE); 347 } 348 349 buffers = calloc(req.count, sizeof(*buffers)); 350 351 if (!buffers) { 352 fprintf(stderr, "Out of memory\n"); 353 exit(EXIT_FAILURE); 354 } 355 356 for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { 357 struct v4l2_buffer buf; 358 359 CLEAR(buf); 360 361 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 362 buf.memory = V4L2_MEMORY_MMAP; 363 buf.index = n_buffers; 364 365 if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) 366 errno_exit("VIDIOC_QUERYBUF"); 367 368 buffers[n_buffers].length = buf.length; 369 buffers[n_buffers].start = 370 mmap(NULL /* start anywhere */, 371 buf.length, 372 PROT_READ | PROT_WRITE /* required */, 373 MAP_SHARED /* recommended */, 374 fd, buf.m.offset); 375 376 if (MAP_FAILED == buffers[n_buffers].start) 377 errno_exit("mmap"); 378 } 379 } 380 381 static void init_userp(unsigned int buffer_size) 382 { 383 struct v4l2_requestbuffers req; 384 385 CLEAR(req); 386 387 req.count = 4; 388 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 389 req.memory = V4L2_MEMORY_USERPTR; 390 391 if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { 392 if (EINVAL == errno) { 393 fprintf(stderr, "%s does not support " 394 "user pointer i/o\n", dev_name); 395 exit(EXIT_FAILURE); 396 } else { 397 errno_exit("VIDIOC_REQBUFS"); 398 } 399 } 400 401 buffers = calloc(4, sizeof(*buffers)); 402 403 if (!buffers) { 404 fprintf(stderr, "Out of memory\n"); 405 exit(EXIT_FAILURE); 406 } 407 408 for (n_buffers = 0; n_buffers < 4; ++n_buffers) { 409 buffers[n_buffers].length = buffer_size; 410 buffers[n_buffers].start = malloc(buffer_size); 411 412 if (!buffers[n_buffers].start) { 413 fprintf(stderr, "Out of memory\n"); 414 exit(EXIT_FAILURE); 415 } 416 } 417 } 418 419 static void init_device(void) 420 { 421 struct v4l2_capability cap; 422 struct v4l2_cropcap cropcap; 423 struct v4l2_crop crop; 424 struct v4l2_format fmt; 425 unsigned int min; 426 427 if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { 428 if (EINVAL == errno) { 429 fprintf(stderr, "%s is no V4L2 device\n", 430 dev_name); 431 exit(EXIT_FAILURE); 432 } else { 433 errno_exit("VIDIOC_QUERYCAP"); 434 } 435 } 436 437 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 438 fprintf(stderr, "%s is no video capture device\n", 439 dev_name); 440 exit(EXIT_FAILURE); 441 } 442 443 switch (io) { 444 case IO_METHOD_READ: 445 if (!(cap.capabilities & V4L2_CAP_READWRITE)) { 446 fprintf(stderr, "%s does not support read i/o\n", 447 dev_name); 448 exit(EXIT_FAILURE); 449 } 450 break; 451 452 case IO_METHOD_MMAP: 453 case IO_METHOD_USERPTR: 454 if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 455 fprintf(stderr, "%s does not support streaming i/o\n", 456 dev_name); 457 exit(EXIT_FAILURE); 458 } 459 break; 460 } 461 462 463 /* Select video input, video standard and tune here. */ 464 465 466 CLEAR(cropcap); 467 468 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 469 470 if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { 471 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 472 crop.c = cropcap.defrect; /* reset to default */ 473 474 if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { 475 switch (errno) { 476 case EINVAL: 477 /* Cropping not supported. */ 478 break; 479 default: 480 /* Errors ignored. */ 481 break; 482 } 483 } 484 } else { 485 /* Errors ignored. */ 486 } 487 488 489 CLEAR(fmt); 490 491 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 492 if (force_format) { 493 fmt.fmt.pix.width = 640; 494 fmt.fmt.pix.height = 480; 495 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 496 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 497 498 if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) 499 errno_exit("VIDIOC_S_FMT"); 500 501 /* Note VIDIOC_S_FMT may change width and height. */ 502 } else { 503 /* Preserve original settings as set by v4l2-ctl for example */ 504 if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) 505 errno_exit("VIDIOC_G_FMT"); 506 } 507 508 /* Buggy driver paranoia. */ 509 min = fmt.fmt.pix.width * 2; 510 if (fmt.fmt.pix.bytesperline < min) 511 fmt.fmt.pix.bytesperline = min; 512 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; 513 if (fmt.fmt.pix.sizeimage < min) 514 fmt.fmt.pix.sizeimage = min; 515 516 switch (io) { 517 case IO_METHOD_READ: 518 init_read(fmt.fmt.pix.sizeimage); 519 break; 520 521 case IO_METHOD_MMAP: 522 init_mmap(); 523 break; 524 525 case IO_METHOD_USERPTR: 526 init_userp(fmt.fmt.pix.sizeimage); 527 break; 528 } 529 } 530 531 static void close_device(void) 532 { 533 if (-1 == close(fd)) 534 errno_exit("close"); 535 536 fd = -1; 537 } 538 539 static void open_device(void) 540 { 541 struct stat st; 542 543 if (-1 == stat(dev_name, &st)) { 544 fprintf(stderr, "Cannot identify '%s': %d, %s\n", 545 dev_name, errno, strerror(errno)); 546 exit(EXIT_FAILURE); 547 } 548 549 if (!S_ISCHR(st.st_mode)) { 550 fprintf(stderr, "%s is no device\n", dev_name); 551 exit(EXIT_FAILURE); 552 } 553 554 fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); 555 556 if (-1 == fd) { 557 fprintf(stderr, "Cannot open '%s': %d, %s\n", 558 dev_name, errno, strerror(errno)); 559 exit(EXIT_FAILURE); 560 } 561 } 562 563 static void usage(FILE *fp, int argc, char **argv) 564 { 565 fprintf(fp, 566 "Usage: %s [options]\n\n" 567 "Version 1.3\n" 568 "Options:\n" 569 "-d | --device name Video device name [%s]\n" 570 "-h | --help Print this message\n" 571 "-m | --mmap Use memory mapped buffers [default]\n" 572 "-r | --read Use read() calls\n" 573 "-u | --userp Use application allocated buffers\n" 574 "-o | --output Outputs stream to stdout\n" 575 "-f | --format Force format to 640x480 YUYV\n" 576 "-c | --count Number of frames to grab [%i]\n" 577 "", 578 argv[0], dev_name, frame_count); 579 } 580 581 static const char short_options[] = "d:hmruofc:"; 582 583 static const struct option 584 long_options[] = { 585 { "device", required_argument, NULL, 'd' }, 586 { "help", no_argument, NULL, 'h' }, 587 { "mmap", no_argument, NULL, 'm' }, 588 { "read", no_argument, NULL, 'r' }, 589 { "userp", no_argument, NULL, 'u' }, 590 { "output", no_argument, NULL, 'o' }, 591 { "format", no_argument, NULL, 'f' }, 592 { "count", required_argument, NULL, 'c' }, 593 { 0, 0, 0, 0 } 594 }; 595 596 int main(int argc, char **argv) 597 { 598 dev_name = "/dev/video0"; 599 600 for (;;) { 601 int idx; 602 int c; 603 604 c = getopt_long(argc, argv, 605 short_options, long_options, &idx); 606 607 if (-1 == c) 608 break; 609 610 switch (c) { 611 case 0: /* getopt_long() flag */ 612 break; 613 614 case 'd': 615 dev_name = optarg; 616 break; 617 618 case 'h': 619 usage(stdout, argc, argv); 620 exit(EXIT_SUCCESS); 621 622 case 'm': 623 io = IO_METHOD_MMAP; 624 break; 625 626 case 'r': 627 io = IO_METHOD_READ; 628 break; 629 630 case 'u': 631 io = IO_METHOD_USERPTR; 632 break; 633 634 case 'o': 635 out_buf++; 636 break; 637 638 case 'f': 639 force_format++; 640 break; 641 642 case 'c': 643 errno = 0; 644 frame_count = strtol(optarg, NULL, 0); 645 if (errno) 646 errno_exit(optarg); 647 break; 648 649 default: 650 usage(stderr, argc, argv); 651 exit(EXIT_FAILURE); 652 } 653 } 654 655 open_device(); 656 init_device(); 657 start_capturing(); 658 mainloop(); 659 stop_capturing(); 660 uninit_device(); 661 close_device(); 662 fprintf(stderr, "\n"); 663 return 0; 664 }
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.