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