00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "v4l.h"
00030
00031 #include <iostream>
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <assert.h>
00036
00037 #include <fcntl.h>
00038 #include <unistd.h>
00039 #include <errno.h>
00040 #include <malloc.h>
00041 #include <sys/stat.h>
00042 #include <sys/types.h>
00043 #include <sys/time.h>
00044 #include <sys/mman.h>
00045 #include <sys/ioctl.h>
00046
00047 #define CLEAR(x) memset (&(x), 0, sizeof (x))
00048
00049 using namespace std;
00050
00051 namespace pangolin
00052 {
00053
00054 static int xioctl(int fd, int request, void* arg)
00055 {
00056 int r;
00057 do r = ioctl (fd, request, arg);
00058 while (-1 == r && EINTR == errno);
00059 return r;
00060 }
00061
00062 V4lVideo::V4lVideo(const char* dev_name, io_method io)
00063 : io(io), fd(-1), buffers(0), n_buffers(0), running(false)
00064 {
00065 open_device(dev_name);
00066 init_device(dev_name,640,480,30);
00067 Start();
00068 }
00069
00070 V4lVideo::~V4lVideo()
00071 {
00072 if(running)
00073 {
00074 Stop();
00075 }
00076
00077 uninit_device();
00078 close_device();
00079 }
00080
00081 unsigned V4lVideo::Width() const
00082 {
00083 return width;
00084 }
00085
00086 unsigned V4lVideo::Height() const
00087 {
00088 return height;
00089 }
00090
00091 size_t V4lVideo::SizeBytes() const
00092 {
00093 return image_size;
00094 }
00095
00096 std::string V4lVideo::PixFormat() const
00097 {
00098
00099 return "YUYV422";
00100 }
00101
00102 bool V4lVideo::GrabNext( unsigned char* image, bool wait )
00103 {
00104 for (;;)
00105 {
00106 fd_set fds;
00107 struct timeval tv;
00108 int r;
00109
00110 FD_ZERO (&fds);
00111 FD_SET (fd, &fds);
00112
00113
00114 tv.tv_sec = 2;
00115 tv.tv_usec = 0;
00116
00117 r = select (fd + 1, &fds, NULL, NULL, &tv);
00118
00119 if (-1 == r)
00120 {
00121 if (EINTR == errno)
00122 continue;
00123
00124 throw VideoException ("select", strerror(errno));
00125 }
00126
00127 if (0 == r)
00128 {
00129 throw VideoException("select Timeout", strerror(errno));
00130 }
00131
00132 if (ReadFrame(image))
00133 break;
00134
00135
00136 }
00137 return true;
00138 }
00139
00140 bool V4lVideo::GrabNewest( unsigned char* image, bool wait )
00141 {
00142
00143 return GrabNext(image,wait);
00144 }
00145
00146 int V4lVideo::ReadFrame(unsigned char* image)
00147 {
00148 struct v4l2_buffer buf;
00149 unsigned int i;
00150
00151 switch (io)
00152 {
00153 case IO_METHOD_READ:
00154 if (-1 == read (fd, buffers[0].start, buffers[0].length))
00155 {
00156 switch (errno)
00157 {
00158 case EAGAIN:
00159 return 0;
00160
00161 case EIO:
00162
00163
00164
00165
00166 default:
00167 throw VideoException("read", strerror(errno));
00168 }
00169 }
00170
00171
00172 memcpy(image,buffers[0].start,buffers[0].length);
00173
00174 break;
00175
00176 case IO_METHOD_MMAP:
00177 CLEAR (buf);
00178
00179 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00180 buf.memory = V4L2_MEMORY_MMAP;
00181
00182 if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf))
00183 {
00184 switch (errno)
00185 {
00186 case EAGAIN:
00187 return 0;
00188
00189 case EIO:
00190
00191
00192
00193
00194 default:
00195 throw VideoException("VIDIOC_DQBUF", strerror(errno));
00196 }
00197 }
00198
00199 assert (buf.index < n_buffers);
00200
00201
00202 memcpy(image,buffers[buf.index].start,buffers[buf.index].length);
00203
00204
00205 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
00206 throw VideoException("VIDIOC_QBUF", strerror(errno));
00207
00208 break;
00209
00210 case IO_METHOD_USERPTR:
00211 CLEAR (buf);
00212
00213 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00214 buf.memory = V4L2_MEMORY_USERPTR;
00215
00216 if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf))
00217 {
00218 switch (errno)
00219 {
00220 case EAGAIN:
00221 return 0;
00222
00223 case EIO:
00224
00225
00226
00227
00228 default:
00229 throw VideoException("VIDIOC_DQBUF", strerror(errno));
00230 }
00231 }
00232
00233 for (i = 0; i < n_buffers; ++i)
00234 if (buf.m.userptr == (unsigned long) buffers[i].start
00235 && buf.length == buffers[i].length)
00236 break;
00237
00238 assert (i < n_buffers);
00239
00240
00241 memcpy(image,(void *)buf.m.userptr,buf.length);
00242
00243
00244 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
00245 throw VideoException("VIDIOC_QBUF", strerror(errno));
00246
00247 break;
00248 }
00249
00250 return 1;
00251 }
00252
00253 void V4lVideo::Stop()
00254 {
00255 enum v4l2_buf_type type;
00256
00257 switch (io)
00258 {
00259 case IO_METHOD_READ:
00260
00261 break;
00262
00263 case IO_METHOD_MMAP:
00264 case IO_METHOD_USERPTR:
00265 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00266
00267 if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
00268 throw VideoException("VIDIOC_STREAMOFF", strerror(errno));
00269
00270 break;
00271 }
00272
00273 running = false;
00274 }
00275
00276 void V4lVideo::Start()
00277 {
00278 unsigned int i;
00279 enum v4l2_buf_type type;
00280
00281 switch (io)
00282 {
00283 case IO_METHOD_READ:
00284
00285 break;
00286
00287 case IO_METHOD_MMAP:
00288 for (i = 0; i < n_buffers; ++i)
00289 {
00290 struct v4l2_buffer buf;
00291
00292 CLEAR (buf);
00293
00294 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00295 buf.memory = V4L2_MEMORY_MMAP;
00296 buf.index = i;
00297
00298 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
00299 throw VideoException("VIDIOC_QBUF", strerror(errno));
00300 }
00301
00302 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00303
00304 if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
00305 throw VideoException("VIDIOC_STREAMON", strerror(errno));
00306
00307 break;
00308
00309 case IO_METHOD_USERPTR:
00310 for (i = 0; i < n_buffers; ++i)
00311 {
00312 struct v4l2_buffer buf;
00313
00314 CLEAR (buf);
00315
00316 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00317 buf.memory = V4L2_MEMORY_USERPTR;
00318 buf.index = i;
00319 buf.m.userptr = (unsigned long) buffers[i].start;
00320 buf.length = buffers[i].length;
00321
00322 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
00323 throw VideoException("VIDIOC_QBUF", strerror(errno));
00324 }
00325
00326 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00327
00328 if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
00329 throw VideoException ("VIDIOC_STREAMON", strerror(errno));
00330
00331 break;
00332 }
00333
00334 running = true;
00335 }
00336
00337 void V4lVideo::uninit_device()
00338 {
00339 unsigned int i;
00340
00341 switch (io)
00342 {
00343 case IO_METHOD_READ:
00344 free (buffers[0].start);
00345 break;
00346
00347 case IO_METHOD_MMAP:
00348 for (i = 0; i < n_buffers; ++i)
00349 if (-1 == munmap (buffers[i].start, buffers[i].length))
00350 throw VideoException ("munmap");
00351 break;
00352
00353 case IO_METHOD_USERPTR:
00354 for (i = 0; i < n_buffers; ++i)
00355 free (buffers[i].start);
00356 break;
00357 }
00358
00359 free (buffers);
00360 }
00361
00362 void V4lVideo::init_read(unsigned int buffer_size)
00363 {
00364 buffers = (buffer*)calloc (1, sizeof (buffer));
00365
00366 if (!buffers)
00367 {
00368 throw VideoException("Out of memory\n");
00369 }
00370
00371 buffers[0].length = buffer_size;
00372 buffers[0].start = malloc (buffer_size);
00373
00374 if (!buffers[0].start)
00375 {
00376 throw VideoException("Out of memory\n");
00377 }
00378 }
00379
00380 void V4lVideo::init_mmap(const char* dev_name)
00381 {
00382 struct v4l2_requestbuffers req;
00383
00384 CLEAR (req);
00385
00386 req.count = 4;
00387 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00388 req.memory = V4L2_MEMORY_MMAP;
00389
00390 if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req))
00391 {
00392 if (EINVAL == errno)
00393 {
00394 throw VideoException("does not support memory mapping", strerror(errno));
00395 }
00396 else
00397 {
00398 throw VideoException ("VIDIOC_REQBUFS", strerror(errno));
00399 }
00400 }
00401
00402 if (req.count < 2)
00403 {
00404 throw VideoException("Insufficient buffer memory");
00405 }
00406
00407 buffers = (buffer*)calloc(req.count, sizeof(buffer));
00408
00409 if (!buffers)
00410 {
00411 throw VideoException( "Out of memory\n");
00412 }
00413
00414 for (n_buffers = 0; n_buffers < req.count; ++n_buffers)
00415 {
00416 struct v4l2_buffer buf;
00417
00418 CLEAR (buf);
00419
00420 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00421 buf.memory = V4L2_MEMORY_MMAP;
00422 buf.index = n_buffers;
00423
00424 if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
00425 throw VideoException ("VIDIOC_QUERYBUF", strerror(errno));
00426
00427 buffers[n_buffers].length = buf.length;
00428 buffers[n_buffers].start =
00429 mmap (NULL ,
00430 buf.length,
00431 PROT_READ | PROT_WRITE ,
00432 MAP_SHARED ,
00433 fd, buf.m.offset);
00434
00435 if (MAP_FAILED == buffers[n_buffers].start)
00436 throw VideoException ("mmap");
00437 }
00438 }
00439
00440 void V4lVideo::init_userp(const char* dev_name, unsigned int buffer_size)
00441 {
00442 struct v4l2_requestbuffers req;
00443 unsigned int page_size;
00444
00445 page_size = getpagesize ();
00446 buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);
00447
00448 CLEAR (req);
00449
00450 req.count = 4;
00451 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00452 req.memory = V4L2_MEMORY_USERPTR;
00453
00454 if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req))
00455 {
00456 if (EINVAL == errno)
00457 {
00458 throw VideoException( "Does not support user pointer i/o", strerror(errno));
00459 }
00460 else
00461 {
00462 throw VideoException ("VIDIOC_REQBUFS", strerror(errno));
00463 }
00464 }
00465
00466 buffers = (buffer*)calloc(4, sizeof(buffer));
00467
00468 if (!buffers)
00469 {
00470 throw VideoException( "Out of memory\n");
00471 }
00472
00473 for (n_buffers = 0; n_buffers < 4; ++n_buffers)
00474 {
00475 buffers[n_buffers].length = buffer_size;
00476 buffers[n_buffers].start = memalign ( page_size,
00477 buffer_size);
00478
00479 if (!buffers[n_buffers].start)
00480 {
00481 throw VideoException( "Out of memory\n");
00482 }
00483 }
00484 }
00485
00486 void V4lVideo::init_device(const char* dev_name, unsigned iwidth, unsigned iheight, unsigned ifps, unsigned v4l_format, v4l2_field field)
00487 {
00488 struct v4l2_capability cap;
00489 struct v4l2_cropcap cropcap;
00490 struct v4l2_crop crop;
00491 struct v4l2_format fmt;
00492 struct v4l2_streamparm strm;
00493
00494 unsigned int min;
00495
00496 if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap))
00497 {
00498 if (EINVAL == errno)
00499 {
00500 throw VideoException("Not a V4L2 device", strerror(errno));
00501 }
00502 else
00503 {
00504 throw VideoException ("VIDIOC_QUERYCAP", strerror(errno));
00505 }
00506 }
00507
00508 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
00509 {
00510 throw VideoException("Not a video capture device");
00511 }
00512
00513 switch (io)
00514 {
00515 case IO_METHOD_READ:
00516 if (!(cap.capabilities & V4L2_CAP_READWRITE))
00517 {
00518 throw VideoException("Does not support read i/o");
00519 }
00520
00521 break;
00522
00523 case IO_METHOD_MMAP:
00524 case IO_METHOD_USERPTR:
00525 if (!(cap.capabilities & V4L2_CAP_STREAMING))
00526 {
00527 throw VideoException("Does not support streaming i/o");
00528 }
00529
00530 break;
00531 }
00532
00533
00534
00535
00536 CLEAR (cropcap);
00537
00538 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00539
00540 if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap))
00541 {
00542 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00543 crop.c = cropcap.defrect;
00544
00545 if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop))
00546 {
00547 switch (errno)
00548 {
00549 case EINVAL:
00550
00551 break;
00552 default:
00553
00554 break;
00555 }
00556 }
00557 }
00558 else
00559 {
00560
00561 }
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584 CLEAR (fmt);
00585
00586 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00587 fmt.fmt.pix.width = iwidth;
00588 fmt.fmt.pix.height = iheight;
00589 fmt.fmt.pix.pixelformat = v4l_format;
00590 fmt.fmt.pix.field = field;
00591
00592 if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
00593 throw VideoException("VIDIOC_S_FMT", strerror(errno));
00594
00595
00596 min = fmt.fmt.pix.width * 2;
00597 if (fmt.fmt.pix.bytesperline < min)
00598 fmt.fmt.pix.bytesperline = min;
00599 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
00600 if (fmt.fmt.pix.sizeimage < min)
00601 fmt.fmt.pix.sizeimage = min;
00602
00603
00604 width = fmt.fmt.pix.width;
00605 height = fmt.fmt.pix.height;
00606 image_size = fmt.fmt.pix.sizeimage;
00607
00608
00609 CLEAR(strm);
00610 strm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00611 strm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
00612 strm.parm.capture.timeperframe.numerator = 1;
00613 strm.parm.capture.timeperframe.denominator = ifps;
00614
00615 if (-1 == xioctl (fd, VIDIOC_S_PARM, &fmt))
00616 throw VideoException("VIDIOC_S_PARM", strerror(errno));
00617
00618 fps = (float)strm.parm.capture.timeperframe.denominator / strm.parm.capture.timeperframe.numerator;
00619
00620 switch (io)
00621 {
00622 case IO_METHOD_READ:
00623 init_read (fmt.fmt.pix.sizeimage);
00624 break;
00625
00626 case IO_METHOD_MMAP:
00627 init_mmap (dev_name );
00628 break;
00629
00630 case IO_METHOD_USERPTR:
00631 init_userp (dev_name, fmt.fmt.pix.sizeimage);
00632 break;
00633 }
00634 }
00635
00636 void V4lVideo::close_device()
00637 {
00638 if (-1 == close (fd))
00639 throw VideoException("close");
00640
00641 fd = -1;
00642 }
00643
00644 void V4lVideo::open_device(const char* dev_name)
00645 {
00646 struct stat st;
00647
00648 if (-1 == stat (dev_name, &st))
00649 {
00650 throw VideoException("Cannot stat device", strerror(errno));
00651 }
00652
00653 if (!S_ISCHR (st.st_mode))
00654 {
00655 throw VideoException("Not device");
00656 }
00657
00658 fd = open (dev_name, O_RDWR | O_NONBLOCK, 0);
00659
00660 if (-1 == fd)
00661 {
00662 throw VideoException("Cannot open device");
00663 }
00664 }
00665
00666 }
00667