665 lines · plain
1.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later2 3file: media/v4l/capture.c4=========================5 6.. code-block:: c7 8 /*9 * V4L2 video capture example10 *11 * This program can be used and distributed without restrictions.12 *13 * This program is provided with the V4L2 API14 * see https://linuxtv.org/docs.php for more information15 */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].start161 && 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 option584 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 }665