v4l2采集视频和图片源码
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <assert.h> 5 6 #include <getopt.h> /* getopt_long() */ 7 8 #include <fcntl.h> /* low-level i/o */ 9 #include <unistd.h> 10 #include <errno.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <sys/time.h> 14 #include <sys/mman.h> 15 #include <sys/ioctl.h> 16 17 #include <linux/videodev2.h> 18 19 #define FORCED_WIDTH 640 20 #define FORCED_HEIGHT 480 21 #define FORCED_FORMAT V4L2_PIX_FMT_YUYV 22 #define FORCED_FIELD V4L2_FIELD_ANY 23 24 static int verbose = 0; 25 #define pr_debug(fmt, arg...) \ 26 if (verbose) fprintf(stderr, fmt, ##arg) 27 28 #define CLEAR(x) memset(&(x), 0, sizeof(x)) //相当于CLEAR(x) =memset(&(x), 0, sizeof(x)) 29 30 enum io_method { //枚举,即只能取其中的一个值。也是一种数据结构 31 IO_METHOD_READ, 32 IO_METHOD_MMAP, 33 IO_METHOD_USERPTR, 34 }; 35 36 struct buffer { 37 void *start; 38 size_t length; //size_t =unsigned int 用来表示sizeof的 39 }; 40 41 static char *dev_name; //用来保存设备名 42 static enum io_method io = IO_METHOD_MMAP; 43 static int fd = -1; 44 struct buffer *buffers; 45 static unsigned int n_buffers; 46 static int out_buf; 47 static int force_format; 48 static int frame_count = 100; 49 static char *video_name="video.yuv"; 50 FILE *fp; //保存视频 51 52 53 static void errno_exit(const char *s) 54 { 55 fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); 56 exit(EXIT_FAILURE); 57 } 58 59 static int xioctl(int fh, int request, void *arg) 60 { 61 int r; 62 63 do { 64 r = ioctl(fh, request, arg); 65 } while (-1 == r && EINTR == errno); 66 67 return r; 68 } 69 70 static void process_image(const void *p, int size) 71 { 72 pr_debug("%s: called!\n", __func__); 73 //采集视频 74 fwrite(p,size,1,fp);//如果要查看采集视频的格式,在linux下可以通过安装v4l-utils,然后执行v4l2-ctl --all来查看视频采集的相关信息 75 // 保存为一张张图片 76 /* FILE *fp; 77 static int num = 1; 78 char picture_name[20]; 79 80 sprintf(picture_name,"picture%d.jpg",num ++); 81 82 if((fp = fopen(picture_name,"w")) == NULL) 83 { 84 perror("Fail to fopen"); 85 exit(EXIT_FAILURE); 86 } 87 88 fwrite(p,size,1,fp); 89 usleep(500); 90 91 fclose(fp); 92 */ 93 94 // 打印到标准输出 95 /*if (out_buf) 96 fwrite(p, size, 1, stdout); 97 98 fflush(stderr); 99 fprintf(stderr, "."); 100 fflush(stdout); 101 */ 102 103 104 } 105 106 static int read_frame(void) 107 { 108 struct v4l2_buffer buf; 109 unsigned int i; 110 111 pr_debug("%s: called!\n", __func__); 112 113 switch (io) { 114 case IO_METHOD_READ: 115 116 break; 117 118 case IO_METHOD_MMAP: 119 CLEAR(buf); 120 121 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 122 buf.memory = V4L2_MEMORY_MMAP; 123 124 if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { 125 switch (errno) { 126 case EAGAIN: 127 return 0; 128 129 case EIO: 130 /* Could ignore EIO, see spec. */ 131 132 /* fall through */ 133 134 default: 135 errno_exit("VIDIOC_DQBUF"); 136 } 137 } 138 139 assert(buf.index < n_buffers); 140 141 process_image(buffers[buf.index].start, buf.bytesused); 142 143 if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) 144 errno_exit("VIDIOC_QBUF"); 145 break; 146 147 case IO_METHOD_USERPTR: 148 149 break; 150 } 151 152 return 1; 153 } 154 155 static void mainloop(void) 156 { 157 unsigned int count; 158 159 pr_debug("%s: called!\n", __func__); 160 161 count = frame_count; 162 163 while (count-- > 0) { 164 for (;;) { 165 fd_set fds; 166 struct timeval tv; 167 int r; 168 169 FD_ZERO(&fds); 170 FD_SET(fd, &fds); 171 172 /* Timeout. */ 173 tv.tv_sec = 2; 174 tv.tv_usec = 0; 175 176 r = select(fd + 1, &fds, NULL, NULL, &tv); 177 178 if (-1 == r) { 179 if (EINTR == errno) 180 continue; 181 errno_exit("select"); 182 } 183 184 if (0 == r) { 185 fprintf(stderr, "select timeout\n"); 186 exit(EXIT_FAILURE); 187 } 188 189 if (read_frame()) 190 break; 191 /* EAGAIN - continue select loop. */ 192 } 193 } 194 } 195 196 static void stop_capturing(void) 197 { 198 enum v4l2_buf_type type; 199 200 pr_debug("%s: called!\n", __func__); 201 202 switch (io) { 203 case IO_METHOD_READ: 204 /* Nothing to do. */ 205 break; 206 207 case IO_METHOD_MMAP: //会执行下面的 208 case IO_METHOD_USERPTR: 209 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 210 if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) 211 errno_exit("VIDIOC_STREAMOFF"); 212 break; 213 } 214 } 215 216 static void start_capturing(void) 217 { 218 unsigned int i; 219 enum v4l2_buf_type type; 220 int err; 221 222 pr_debug("%s: called!\n", __func__); 223 224 pr_debug("\tn_buffers: %d\n", n_buffers); 225 226 switch (io) { 227 case IO_METHOD_READ: 228 /* Nothing to do. */ 229 break; 230 231 case IO_METHOD_MMAP: 232 for (i = 0; i < n_buffers; ++i) { 233 struct v4l2_buffer buf; 234 235 pr_debug("\ti: %d\n", i); 236 237 CLEAR(buf); 238 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 239 buf.memory = V4L2_MEMORY_MMAP; 240 buf.index = i; 241 242 pr_debug("\tbuf.index: %d\n", buf.index); 243 244 err == xioctl(fd, VIDIOC_QBUF, &buf); 245 pr_debug("\terr: %d\n", err); 246 247 if (-1 == err) 248 errno_exit("VIDIOC_QBUF"); 249 250 pr_debug("\tbuffer queued!\n"); 251 } 252 253 pr_debug("Before STREAMON\n"); 254 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 255 if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) 256 errno_exit("VIDIOC_STREAMON"); 257 pr_debug("After STREAMON\n"); 258 break; 259 260 case IO_METHOD_USERPTR: 261 262 break; 263 } 264 } 265 266 static void uninit_device(void) 267 { 268 unsigned int i; 269 270 pr_debug("%s: called!\n", __func__); 271 272 switch (io) { 273 case IO_METHOD_READ: 274 free(buffers[0].start); 275 break; 276 277 case IO_METHOD_MMAP: 278 for (i = 0; i < n_buffers; ++i) 279 if (-1 == munmap(buffers[i].start, buffers[i].length)) 280 errno_exit("munmap"); 281 break; 282 283 case IO_METHOD_USERPTR: 284 for (i = 0; i < n_buffers; ++i) 285 free(buffers[i].start); 286 break; 287 } 288 289 free(buffers); 290 } 291 292 static void init_read(unsigned int buffer_size) 293 { 294 pr_debug("%s: called!\n", __func__); 295 296 buffers = calloc(1, sizeof(*buffers)); 297 298 if (!buffers) { 299 fprintf(stderr, "Out of memory\n"); 300 exit(EXIT_FAILURE); 301 } 302 303 buffers[0].length = buffer_size; 304 buffers[0].start = malloc(buffer_size); 305 306 if (!buffers[0].start) { 307 fprintf(stderr, "Out of memory\n"); 308 exit(EXIT_FAILURE); 309 } 310 } 311 312 static void init_mmap(void) //(3) 313 { 314 struct v4l2_requestbuffers req; 315 316 pr_debug("%s: called!\n", __func__); 317 318 CLEAR(req); 319 320 req.count = 4; 321 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 322 req.memory = V4L2_MEMORY_MMAP; 323 324 if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { 325 if (EINVAL == errno) { 326 fprintf(stderr, "%s does not support " 327 "memory mapping\n", dev_name); 328 exit(EXIT_FAILURE); 329 } else { 330 errno_exit("VIDIOC_REQBUFS"); 331 } 332 } 333 pr_debug("\treq.count: %d\n", req.count); 334 pr_debug("\treq.type: %d\n", req.type); 335 pr_debug("\treq.memory: %d\n", req.memory); 336 pr_debug("\n"); 337 338 339 if (req.count < 2) { 340 fprintf(stderr, "Insufficient buffer memory on %s\n", 341 dev_name); 342 exit(EXIT_FAILURE); 343 } 344 345 buffers = calloc(req.count, sizeof(*buffers)); 346 347 if (!buffers) { 348 fprintf(stderr, "Out of memory\n"); 349 exit(EXIT_FAILURE); 350 } 351 352 for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { 353 struct v4l2_buffer buf; 354 355 CLEAR(buf); 356 357 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 358 buf.memory = V4L2_MEMORY_MMAP; 359 buf.index = n_buffers; 360 361 if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) 362 errno_exit("VIDIOC_QUERYBUF"); 363 364 pr_debug("\tbuf.index: %d\n", buf.index); 365 pr_debug("\tbuf.type: %d\n", buf.type); 366 pr_debug("\tbuf.bytesused: %d\n", buf.bytesused); 367 pr_debug("\tbuf.flags: %d\n", buf.flags); 368 pr_debug("\tbuf.field: %d\n", buf.field); 369 pr_debug("\tbuf.timestamp.tv_sec: %ld\n", (long) buf.timestamp.tv_sec); 370 pr_debug("\tbuf.timestamp.tv_usec: %ld\n", (long) buf.timestamp.tv_usec); 371 pr_debug("\tbuf.timecode.type: %d\n", buf.timecode.type); 372 pr_debug("\tbuf.timecode.flags: %d\n", buf.timecode.flags); 373 pr_debug("\tbuf.timecode.frames: %d\n", buf.timecode.frames); 374 pr_debug("\tbuf.timecode.seconds: %d\n", buf.timecode.seconds); 375 pr_debug("\tbuf.timecode.minutes: %d\n", buf.timecode.minutes); 376 pr_debug("\tbuf.timecode.hours: %d\n", buf.timecode.hours); 377 pr_debug("\tbuf.timecode.userbits: %d,%d,%d,%d\n", 378 buf.timecode.userbits[0], 379 buf.timecode.userbits[1], 380 buf.timecode.userbits[2], 381 buf.timecode.userbits[3]); 382 pr_debug("\tbuf.sequence: %d\n", buf.sequence); 383 pr_debug("\tbuf.memory: %d\n", buf.memory); 384 pr_debug("\tbuf.m.offset: %d\n", buf.m.offset); 385 pr_debug("\tbuf.length: %d\n", buf.length); 386 pr_debug("\tbuf.input: %d\n", buf.input); 387 pr_debug("\n"); 388 389 buffers[n_buffers].length = buf.length; 390 buffers[n_buffers].start = 391 mmap(NULL /* start anywhere */, 392 buf.length, 393 PROT_READ | PROT_WRITE /* required */, 394 MAP_SHARED /* recommended */, 395 fd, buf.m.offset); 396 397 if (MAP_FAILED == buffers[n_buffers].start) 398 errno_exit("mmap"); 399 } 400 } 401 402 403 static void init_device(void) //static表示次函数只能在本文件中调用 404 { 405 struct v4l2_capability cap; 406 struct v4l2_cropcap cropcap; 407 struct v4l2_crop crop; 408 struct v4l2_format fmt; 409 unsigned int min; 410 411 pr_debug("%s: called!\n", __func__); 412 413 if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { 414 if (EINVAL == errno) { 415 fprintf(stderr, "%s is no V4L2 device\n", 416 dev_name); 417 exit(EXIT_FAILURE); 418 } else { 419 errno_exit("VIDIOC_QUERYCAP"); 420 } 421 } 422 423 pr_debug("\tdriver: %s\n" 424 "\tcard: %s \n" 425 "\tbus_info: %s\n", 426 cap.driver, cap.card, cap.bus_info); 427 pr_debug("\tversion: %u.%u.%u\n", 428 (cap.version >> 16) & 0xFF, 429 (cap.version >> 8) & 0xFF, 430 cap.version & 0xFF); 431 pr_debug("\tcapabilities: 0x%08x\n", cap.capabilities); 432 433 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 434 fprintf(stderr, "%s is no video capture device\n", 435 dev_name); 436 exit(EXIT_FAILURE); 437 } 438 439 440 441 442 /* Select video input, video standard and tune here. */ 443 444 445 CLEAR(cropcap); 446 447 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 448 449 if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { 450 pr_debug("\tcropcap.type: %d\n", cropcap.type); 451 pr_debug("\tcropcap.bounds.left: %d\n", cropcap.bounds.left); 452 pr_debug("\tcropcap.bounds.top: %d\n", cropcap.bounds.top); 453 pr_debug("\tcropcap.bounds.width: %d\n", cropcap.bounds.width); 454 pr_debug("\tcropcap.bounds.height: %d\n", cropcap.bounds.height); 455 456 pr_debug("\tcropcap.defrect.left: %d\n", cropcap.defrect.left); 457 pr_debug("\tcropcap.defrect.top: %d\n", cropcap.defrect.top); 458 pr_debug("\tcropcap.defrect.width: %d\n", cropcap.defrect.width); 459 pr_debug("\tcropcap.defrect.height: %d\n", cropcap.defrect.height); 460 461 pr_debug("\tcropcap.pixelaspect.numerator: %d\n", cropcap.pixelaspect.numerator); 462 pr_debug("\tcropcap.pixelaspect.denominator: %d\n", cropcap.pixelaspect.denominator); 463 pr_debug("\n"); 464 465 CLEAR(crop); 466 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 467 crop.c = cropcap.defrect; /* reset to default */ 468 469 if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { 470 switch (errno) { 471 case EINVAL: 472 /* Cropping not supported. */ 473 break; 474 default: 475 /* Errors ignored. */ 476 pr_debug("\tcropping not supported\n"); 477 break; 478 } 479 } 480 } else { 481 /* Errors ignored. */ 482 } 483 484 485 CLEAR(fmt); 486 487 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 488 if (force_format) { 489 fmt.fmt.pix.width = FORCED_WIDTH; 490 fmt.fmt.pix.height = FORCED_HEIGHT; 491 fmt.fmt.pix.pixelformat = FORCED_FORMAT; 492 fmt.fmt.pix.field = FORCED_FIELD; 493 494 pr_debug("\tfmt.fmt.pix.pixelformat: %c,%c,%c,%c\n", 495 fmt.fmt.pix.pixelformat & 0xFF, 496 (fmt.fmt.pix.pixelformat >> 8) & 0xFF, 497 (fmt.fmt.pix.pixelformat >> 16) & 0xFF, 498 (fmt.fmt.pix.pixelformat >> 24) & 0xFF 499 ); 500 pr_debug("\n"); 501 502 if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) 503 errno_exit("VIDIOC_S_FMT"); 504 505 /* Note VIDIOC_S_FMT may change width and height. */ 506 } else { 507 /* Preserve original settings as set by v4l2-ctl for example */ 508 if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) 509 errno_exit("VIDIOC_G_FMT"); 510 511 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 512 pr_debug("\tfmt.fmt.pix.pixelformat: %c,%c,%c,%c\n", 513 fmt.fmt.pix.pixelformat & 0xFF, 514 (fmt.fmt.pix.pixelformat >> 8) & 0xFF, 515 (fmt.fmt.pix.pixelformat >> 16) & 0xFF, 516 (fmt.fmt.pix.pixelformat >> 24) & 0xFF 517 ); 518 519 } 520 521 /* Buggy driver paranoia. */ 522 min = fmt.fmt.pix.width * 2; 523 if (fmt.fmt.pix.bytesperline < min) 524 fmt.fmt.pix.bytesperline = min; 525 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; 526 if (fmt.fmt.pix.sizeimage < min) 527 fmt.fmt.pix.sizeimage = min; 528 529 switch (io) { 530 case IO_METHOD_READ: 531 init_read(fmt.fmt.pix.sizeimage); 532 break; 533 534 case IO_METHOD_MMAP: 535 init_mmap(); 536 break; 537 538 case IO_METHOD_USERPTR: 539 540 break; 541 } 542 } 543 544 static void close_device(void) 545 { 546 pr_debug("%s: called!\n", __func__); 547 548 if (-1 == close(fd))//==可以防止你写成=(相当与赋值)这样条件始终成立 549 errno_exit("close"); 550 551 fd = -1; 552 } 553 554 static void open_device(void)// 555 { 556 struct stat st; 557 558 pr_debug("%s: called!\n", __func__);//_func_表示此时在那个函数里面 559 560 if (-1 == stat(dev_name, &st)) {//获取文件信息 561 fprintf(stderr, "Cannot identify '%s': %d, %s\n", 562 dev_name, errno, strerror(errno)); 563 exit(EXIT_FAILURE);//非正常退出 EXIT_FAILURE=1, EXIT_SUCCSES=0 正常退出 564 } 565 566 if (!S_ISCHR(st.st_mode)) { 567 fprintf(stderr, "%s is no device\n", dev_name); 568 exit(EXIT_FAILURE); 569 } 570 571 fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);//非阻塞模式打开文件 572 573 if (-1 == fd) { 574 fprintf(stderr, "Cannot open '%s': %d, %s\n", 575 dev_name, errno, strerror(errno)); 576 exit(EXIT_FAILURE); 577 } 578 } 579 580 static void usage(FILE *fp, int argc, char **argv) 581 { 582 fprintf(fp, 583 "Usage: %s [options]\n\n" 584 "Version 1.3\n" 585 "Options:\n" 586 "-d | --device name Video device name [%s]\n" 587 "-h | --help Print this message\n" 588 "-m | --mmap Use memory mapped buffers [default]\n" 589 "-r | --read Use read() calls\n" 590 "-u | --userp Use application allocated buffers\n" 591 "-o | --output Outputs stream to stdout\n" 592 "-f | --format Force format to 640x480 YUYV\n" 593 "-c | --count Number of frames to grab [%i]\n" 594 "-v | --verbose Verbose output\n" 595 "", 596 argv[0], dev_name, frame_count); 597 } 598 //关于命令解析见百度getopt-long; 599 static const char short_options[] = "d:hmruofc:v";// 短选项 :表示带参数 600 601 static const struct option //长选项 602 long_options[] = { 603 { "device", required_argument, NULL, 'd' }, 604 { "help", no_argument, NULL, 'h' }, 605 { "mmap", no_argument, NULL, 'm' }, 606 { "read", no_argument, NULL, 'r' }, 607 { "userp", no_argument, NULL, 'u' }, 608 { "output", no_argument, NULL, 'o' }, 609 { "format", no_argument, NULL, 'f' }, 610 { "count", required_argument, NULL, 'c' }, 611 { "verbose", no_argument, NULL, 'v' }, 612 { 0, 0, 0, 0 } 613 }; 614 615 int main(int argc, char **argv) 616 { 617 dev_name = "/dev/video0"; 618 619 for (;;) { 620 int idx; 621 int c; 622 623 c = getopt_long(argc, argv, 624 short_options, long_options, &idx); 625 626 if (-1 == c) 627 break; 628 629 switch (c) { 630 case 0: /* getopt_long() flag */ 631 break; 632 633 case 'd': 634 dev_name = optarg; 635 break; 636 637 case 'h': 638 usage(stdout, argc, argv); 639 exit(EXIT_SUCCESS); 640 641 case 'm': 642 io = IO_METHOD_MMAP; 643 break; 644 645 case 'r': 646 io = IO_METHOD_READ; 647 break; 648 649 case 'u': 650 io = IO_METHOD_USERPTR; 651 break; 652 653 case 'o': 654 out_buf++; 655 break; 656 657 case 'f': 658 force_format++; 659 break; 660 661 case 'c': 662 errno = 0; 663 frame_count = strtol(optarg, NULL, 0); 664 if (errno) 665 errno_exit(optarg); 666 break; 667 668 case 'v': 669 verbose = 1; 670 break; 671 672 default: 673 usage(stderr, argc, argv); 674 exit(EXIT_FAILURE); 675 } 676 } 677 678 if((fp = fopen(video_name,"wa+")) == NULL) 679 { 680 perror("Fail to fopen"); 681 exit(EXIT_FAILURE); 682 } 683 684 open_device(); 685 init_device(); 686 start_capturing(); 687 mainloop(); 688 stop_capturing(); 689 uninit_device(); 690 close_device(); 691 fprintf(stderr, "\n"); 692 return 0; 693 }