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 }