00001 #include "vidl_v4l2_device.h"
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "vidl_pixel_format.h"
00012 #include "vidl_v4l2_pixel_format.h"
00013
00014 extern "C" {
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <sys/mman.h>
00018 #include <sys/ioctl.h>
00019 #include <vcl_cerrno.h>
00020 #include <fcntl.h>
00021 #include <unistd.h>
00022 };
00023
00024 #include <vcl_cstring.h>
00025 #include <vcl_cstdlib.h>
00026 #include <vcl_sstream.h>
00027 #include <vcl_iostream.h>
00028
00029
00030 namespace {
00031 inline int xioctl(int fd, int request, void * arg)
00032 {
00033 int r;
00034 do { r = ioctl(fd, request, arg); }
00035 while (-1 == r && EINTR == errno);
00036 return r;
00037 }
00038
00039
00040 void double2fraction(double value, int& n, int& d) {
00041 if (value < 0.0) value = -value;
00042 int a= n= (int)(value*10000+0.5);
00043 int b= d= 10000;
00044 int resto= a%b;
00045 while (resto!=0) {
00046 a=b;
00047 b=resto;
00048 resto= a% b;
00049 }
00050 n/=b;
00051 d/=b;
00052 }
00053 }
00054
00055
00056 void vidl_v4l2_device::update_controls()
00057 {
00058 for (unsigned int i=0;i<controls_.size();++i) delete controls_[i];
00059 controls_.clear();
00060
00061 struct v4l2_queryctrl ctrl;
00062 for (int indice = V4L2_CID_BASE;indice < V4L2_CID_LASTP1;indice++) {
00063 ctrl.id= indice;
00064 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) {
00065 vidl_v4l2_control *pc= vidl_v4l2_control::new_control(ctrl, fd);
00066 if (pc) controls_.push_back(pc);
00067 }
00068 }
00069
00070 for (int indice = V4L2_CID_PRIVATE_BASE;;indice++) {
00071 ctrl.id= indice;
00072 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) {
00073 vidl_v4l2_control *pc= vidl_v4l2_control::new_control(ctrl, fd);
00074 if (pc) controls_.push_back(pc);
00075 }
00076 else
00077 break;
00078 }
00079
00080 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL // apparently, not all versions of V4L2 support extended controls ... (PVr)
00081
00082 ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
00083 while (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) {
00084 if (!get_control_id(ctrl.id)) {
00085 vidl_v4l2_control *pc= vidl_v4l2_control::new_control(ctrl, fd);
00086 if (pc) controls_.push_back(pc);
00087 }
00088 ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
00089 }
00090 #endif // V4L2_CTRL_FLAG_NEXT_CTRL
00091 }
00092
00093 void vidl_v4l2_device::reset_controls()
00094 {
00095 if (is_open()) {
00096 if (n_controls()==0)
00097 update_controls();
00098 for (int i=0;i<n_controls();++i)
00099 get_control(i)->reset();
00100 }
00101 }
00102
00103 vidl_v4l2_device::vidl_v4l2_device(const char *file)
00104 {
00105 pre_nbuffers= 4;
00106 ref_count_ = 0;
00107 dev_name_= file;
00108 fd= -1;
00109 buffers= NULL;
00110 n_buffers= 0;
00111 capturing= false;
00112 last_error="";
00113
00114 if (!open()) {
00115 vcl_cerr << "Error creating device: " << last_error << vcl_endl;
00116 return;
00117 }
00118 if (!initialize_device()) {
00119 vcl_cerr << "Error initializing device: " << last_error << vcl_endl;
00120 close();
00121 return;
00122 }
00123
00124
00125 struct v4l2_input inp;
00126
00127 #ifdef DEBUG
00128 vcl_cerr << "Looking for inputs..." << fd << vcl_endl;
00129 #endif
00130 for (inp.index=0;-1!=xioctl(fd,VIDIOC_ENUMINPUT,&inp); inp.index++) {
00131 #ifdef DEBUG
00132 vcl_cerr << "Inserting input..." << vcl_endl;
00133 #endif
00134 inputs_.push_back(vidl_v4l2_input(inp));
00135 }
00136
00137 fmt.fmt.pix.width = 0;
00138 fmt.fmt.pix.height =0;
00139
00140
00141
00142 close();
00143 }
00144
00145 vidl_v4l2_device::~vidl_v4l2_device()
00146 {
00147 #if 0
00148 if (is_open()) {
00149 if (capturing)
00150 stop_capturing();
00151 if (buffers)
00152 uninit_mmap();
00153 close();
00154 }
00155 #endif
00156 close();
00157 for (unsigned int i=0;i<controls_.size();++i) delete controls_[i];
00158 }
00159
00160 void vidl_v4l2_device::reset()
00161 {
00162 #if 0
00163 if (is_open()) {
00164 if (capturing)
00165 stop_capturing();
00166 if (buffers)
00167 uninit_mmap();
00168 close();
00169 }
00170 #endif
00171 close();
00172 last_error="";
00173 if (!open()) {
00174 vcl_cerr << "Error creating device: " << last_error << '\n';
00175 return;
00176 }
00177 if (!initialize_device()) {
00178 vcl_cerr << "Error initializing device: " << last_error << '\n';
00179 close();
00180 return;
00181 }
00182
00183 fmt.fmt.pix.width = 0;
00184 fmt.fmt.pix.height =0;
00185
00186
00187
00188 update_controls();
00189 for (int i=0;i<n_controls();++i)
00190 get_control(i)->reset();
00191
00192 }
00193
00194 bool vidl_v4l2_device::open()
00195 {
00196 if (is_open()) if (!close()) return false;
00197
00198 struct stat st;
00199
00200 if (-1 == stat(dev_name_.c_str(), &st)) {
00201 vcl_ostringstream f;
00202 f << "Cannot identify " << dev_name_ << ": " << vcl_strerror(errno);
00203 last_error=f.str();
00204
00205 return false;
00206 }
00207
00208 if (!S_ISCHR(st.st_mode)) {
00209 vcl_ostringstream f;
00210 f << dev_name_ << "is not a valid video device";
00211 last_error=f.str();
00212 return false;
00213 }
00214
00215 fd = ::open(dev_name_.c_str(), O_RDWR | O_NONBLOCK, 0);
00216
00217 if (-1 == fd) {
00218 vcl_ostringstream f;
00219 f << "Cannot open " << dev_name_ << ": "<< vcl_strerror(errno);
00220 last_error=f.str();
00221 return false;
00222 }
00223 return true;
00224 }
00225
00226 bool vidl_v4l2_device::initialize_device()
00227 {
00228 struct v4l2_capability cap;
00229
00230 if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
00231 vcl_ostringstream f;
00232
00233 if (EINVAL == errno) {
00234 f << dev_name_ << " is not a valid V4L2 video device";
00235 }
00236 else
00237 f << "v4l2_device -> Error in VIDIOC_QUERYCAP";
00238 close();
00239 last_error=f.str();
00240 return false;
00241 }
00242
00243 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
00244 vcl_ostringstream f;
00245 f << dev_name_ << " is not a valid video capture device";
00246 close();
00247 last_error=f.str();
00248 return false;
00249 }
00250
00251 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
00252 vcl_ostringstream f;
00253 f << dev_name_ << " does not support streaming i/o";
00254 close();
00255 last_error=f.str();
00256 return false;
00257 }
00258
00259 card_name_= (const char *)cap.card;
00260 return true;
00261 }
00262
00263 bool vidl_v4l2_device::try_formats(int width, int height)
00264 {
00265
00266
00267
00268 if (set_v4l2_format(V4L2_PIX_FMT_BGR24,width,height)) return true;
00269 if (set_v4l2_format(V4L2_PIX_FMT_BGR32,width,height)) return true;
00270 if (set_v4l2_format(V4L2_PIX_FMT_RGB565,width,height)) return true;
00271 if (set_v4l2_format(V4L2_PIX_FMT_RGB555,width,height)) return true;
00272
00273 if (set_v4l2_format(V4L2_PIX_FMT_YUYV,width,height)) return true;
00274 if (set_v4l2_format(V4L2_PIX_FMT_UYVY,width,height)) return true;
00275
00276 if (set_v4l2_format(V4L2_PIX_FMT_YUV422P,width,height)) return true;
00277 if (set_v4l2_format(V4L2_PIX_FMT_YVU420,width,height)) return true;
00278 if (set_v4l2_format(V4L2_PIX_FMT_YUV420,width,height)) return true;
00279 if (set_v4l2_format(V4L2_PIX_FMT_YUV411P,width,height)) return true;
00280 if (set_v4l2_format(V4L2_PIX_FMT_YVU410,width,height)) return true;
00281
00282 if (set_v4l2_format(V4L2_PIX_FMT_GREY,width,height)) return true;
00283
00284
00285
00286 fmt.fmt.pix.width = 0;
00287 fmt.fmt.pix.height =0;
00288
00289 return false;
00290 }
00291
00292
00293
00294 bool vidl_v4l2_device::set_v4l2_format(unsigned int fourcode, int width, int height,
00295 double fps)
00296 {
00297 fmt.fmt.pix.width = 0;
00298 fmt.fmt.pix.height= 0;
00299
00300 if (!is_open()) reset();
00301 if (is_open()) {
00302 if (capturing)
00303 stop_capturing();
00304 if (buffers)
00305 uninit_mmap();
00306 vcl_memset(&fmt, 0, sizeof(fmt));
00307
00308 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00309 fmt.fmt.pix.width = width;
00310 fmt.fmt.pix.height = height;
00311 fmt.fmt.pix.pixelformat = fourcode;
00312 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
00313 if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) {
00314 if (errno==EBUSY) {
00315 reset();
00316 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00317 fmt.fmt.pix.width = width;
00318 fmt.fmt.pix.height = height;
00319 fmt.fmt.pix.pixelformat = fourcode;
00320 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
00321 if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) {
00322 fmt.fmt.pix.width = 0;
00323 fmt.fmt.pix.height= 0;
00324 return false;
00325 }
00326 }
00327 else {
00328 fmt.fmt.pix.width = 0;
00329 fmt.fmt.pix.height= 0;
00330 return false;
00331 }
00332 }
00333
00334 if (fps!=0.0) {
00335 struct v4l2_streamparm sfrate;
00336 vcl_memset(&sfrate, 0, sizeof(sfrate));
00337 sfrate.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00338
00339
00340 frame_rate=0;
00341 int n = 0, d = 0;
00342 double2fraction(fps,n,d);
00343 sfrate.parm.capture.timeperframe.numerator = d;
00344 sfrate.parm.capture.timeperframe.denominator = n;
00345
00346 if ( xioctl(fd, VIDIOC_S_PARM, &sfrate)== 0) {
00347 if ( xioctl(fd, VIDIOC_G_PARM, &sfrate)== 0) {
00348 frame_rate = (double)sfrate.parm.capture.timeperframe.denominator
00349 / (double)sfrate.parm.capture.timeperframe.numerator;
00350 }
00351 }
00352 }
00353 if (init_mmap(pre_nbuffers))
00354 return true;
00355 else {
00356 fmt.fmt.pix.width = 0;
00357 fmt.fmt.pix.height =0;
00358 return false;
00359 }
00360 }
00361 else return false;
00362 }
00363
00364
00365 bool vidl_v4l2_device::init_mmap(int reqbuf)
00366 {
00367 if (!format_is_set())
00368 if (!try_formats()) return false;
00369
00370 struct v4l2_requestbuffers req;
00371
00372 vcl_memset(&req, 0, sizeof(req));
00373
00374 req.count = reqbuf;
00375 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00376 req.memory = V4L2_MEMORY_MMAP;
00377
00378 if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
00379 if (EINVAL == errno) {
00380 vcl_ostringstream f;
00381 f << dev_name_ << " does not support memory mapping";
00382 last_error=f.str();
00383 return false;
00384 }
00385 else {
00386 last_error = "v4l2_device -> VIDEOC_REQBUFS";
00387 return false;
00388 }
00389 }
00390
00391 if (req.count < 1) {
00392 vcl_ostringstream f;
00393 f<< "Insufficient buffer memory on " << dev_name_ ;
00394 last_error=f.str();
00395 return false;
00396 }
00397
00398 buffers = (struct buffer*)vcl_calloc(req.count, sizeof(*buffers));
00399
00400 if (!buffers) {
00401 last_error= "Out of memory reserving buffers";
00402 return false;
00403 }
00404
00405 for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
00406 #if 0
00407 struct v4l2_buffer buf; vcl_memset(&buf, 0, sizeof(buf));
00408 #endif
00409 vcl_memset(&(buffers[n_buffers].buf), 0, sizeof(struct v4l2_buffer) );
00410
00411 buffers[n_buffers].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00412 buffers[n_buffers].buf.memory = V4L2_MEMORY_MMAP;
00413 buffers[n_buffers].buf.index = n_buffers;
00414
00415 if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buffers[n_buffers].buf)) {
00416 last_error= "v4l2_device -> VIDIOC_QUERYBUF";
00417 vcl_free(buffers); buffers=NULL;
00418 return false;
00419 }
00420
00421 #if 0
00422 buffers[n_buffers].length = buf.length;
00423 #endif
00424 buffers[n_buffers].start =
00425 mmap(NULL ,
00426 buffers[n_buffers].buf.length,
00427 PROT_READ | PROT_WRITE ,
00428 MAP_SHARED ,
00429 fd, buffers[n_buffers].buf.m.offset);
00430
00431 if (MAP_FAILED == buffers[n_buffers].start) {
00432 last_error= "v4l2_device -> mmap";
00433 vcl_free(buffers); buffers=NULL;
00434 return false;
00435 }
00436 }
00437 last_buffer= -1;
00438 return true;
00439 }
00440
00441
00442 bool vidl_v4l2_device::set_number_of_buffers(unsigned int nb){
00443 if (nb==0) return false;
00444 if (pre_nbuffers==nb) return true;
00445 pre_nbuffers= nb;
00446 if (capturing) stop_capturing();
00447 if (buffers) {
00448 uninit_mmap();
00449 return init_mmap(pre_nbuffers);
00450 }
00451 return true;
00452 }
00453
00454
00455 bool vidl_v4l2_device::start_capturing()
00456 {
00457 if (capturing) return true;
00458 if (!buffers)
00459 if (!init_mmap(pre_nbuffers))
00460 return false;
00461
00462 enum v4l2_buf_type type;
00463
00464 for (unsigned int i = 0; i < n_buffers; ++i) {
00465 struct v4l2_buffer buf;
00466 vcl_memset(&buf, 0, sizeof(buf));
00467
00468 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00469 buf.memory = V4L2_MEMORY_MMAP;
00470 buf.index = i;
00471
00472 if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)){
00473 last_error= "v4l2_device -> VIDIOC_QBUF";
00474 return false;
00475 }
00476 }
00477
00478 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00479
00480 if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)){
00481 last_error= "v4l2_device -> VIDIOC_STREAMON";
00482 return false;
00483 }
00484 capturing= true;
00485 last_buffer= -1;
00486 return true;
00487 }
00488
00489
00490 bool vidl_v4l2_device::read_frame()
00491 {
00492 if (!capturing) return false;
00493
00494 if (last_buffer!=-1)
00495 if (-1 == xioctl(fd, VIDIOC_QBUF, &(buffers[last_buffer].buf) ) ) {
00496 last_error= "read_frame: VIDIOC_QBUF";
00497 return false;
00498 }
00499
00500 struct v4l2_buffer buf;
00501 bool completed= false;
00502 do {
00503 fd_set fds;
00504 struct timeval tv;
00505 int r;
00506
00507 FD_ZERO(&fds);
00508 FD_SET(fd, &fds);
00509
00510
00511 tv.tv_sec = 5;
00512 tv.tv_usec = 0;
00513
00514 r = select(fd + 1, &fds, NULL, NULL, &tv);
00515 if (-1 == r) {
00516 if (EINTR == errno)
00517 continue;
00518
00519 last_error= "read_frame: error in select";
00520 return false;
00521 }
00522 if (0 == r) {
00523 last_error= "read_frame: select timeout";
00524 return false;
00525 }
00526
00527 vcl_memset(&buf, 0, sizeof(buf));
00528
00529 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00530 buf.memory = V4L2_MEMORY_MMAP;
00531
00532 if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
00533 if (errno!= EAGAIN) {
00534 last_error= "read_frame: VIDIOC_DQBUF";
00535 return false;
00536 }
00537 }
00538 else completed= true;
00539 }
00540 while (!completed);
00541
00542 buffers[buf.index].buf= buf;
00543 last_buffer=buf.index;
00544
00545 return true;
00546 }
00547
00548 bool vidl_v4l2_device::stop_capturing()
00549 {
00550 if (!capturing) return true;
00551
00552 enum v4l2_buf_type type;
00553
00554 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00555
00556 if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)){
00557 last_error= "v4l2_device -> VIDIOC_STREAMOFF";
00558 return false;
00559 }
00560 capturing= false;
00561 last_buffer= -1;
00562 return true;
00563 }
00564
00565
00566 bool vidl_v4l2_device::uninit_mmap()
00567 {
00568 for (unsigned int i = 0; i < n_buffers; ++i)
00569 if (-1 == munmap(buffers[i].start, buffers[i].buf.length)) {
00570 last_error= "v4l2_device -> munmap";
00571 return false;
00572 }
00573 vcl_free(buffers);
00574 buffers= NULL;
00575 n_buffers=0;
00576 return true;
00577 }
00578
00579 bool vidl_v4l2_device::close()
00580 {
00581 if (is_open()) {
00582 if (capturing)
00583 stop_capturing();
00584 if (buffers)
00585 uninit_mmap();
00586 for (unsigned int i=0;i<controls_.size();++i) delete controls_[i];
00587 controls_.clear();
00588
00589 last_error="";
00590 if (-1 == ::close (fd)) {
00591 last_error= "Error closing device";
00592 return false;
00593 }
00594 fd = -1;
00595 }
00596 return true;
00597 }
00598
00599 unsigned int vidl_v4l2_device::current_input() const
00600 {
00601 if (!is_open())
00602 return n_inputs();
00603
00604 if (n_inputs()==0) return 0;
00605
00606 int index;
00607 if (-1==xioctl(fd,VIDIOC_G_INPUT,&index)) {
00608 last_error= "error getting current input (VIDIOC_G_INPUT)";
00609 return n_inputs();
00610 }
00611 return index;
00612 }
00613
00614 bool vidl_v4l2_device::set_input(unsigned int i)
00615 {
00616 if (current_input()==i)
00617 return true;
00618 if (!is_open()) reset();
00619 if (!is_open() || i>=n_inputs())
00620 return false;
00621
00622 if (capturing)
00623 stop_capturing();
00624 if (buffers)
00625 uninit_mmap();
00626
00627 if (-1==xioctl(fd,VIDIOC_S_INPUT,&i))
00628 return false;
00629
00630 fmt.fmt.pix.width = 0;
00631 fmt.fmt.pix.height =0;
00632 #if 0
00633 try_formats();
00634 #endif
00635 update_controls();
00636
00637 return true;
00638 }
00639
00640
00641 vcl_ostream &
00642 operator<<(vcl_ostream &os, const vidl_v4l2_device & dev)
00643 {
00644 os << dev.device_file() << " -> " << dev.card_name()<< vcl_endl
00645 << " " << dev.n_inputs() << " inputs:"<< vcl_endl;
00646 for (unsigned int j=0;j<dev.n_inputs();++j){
00647 os << " " << j << ": " << dev.input(j).name();
00648 if (dev.input(j).is_tuner())
00649 os << " is tuner" << vcl_endl;
00650 else
00651 os << " is camera" << vcl_endl;
00652 }
00653 os << " Current input: " << dev.current_input() << vcl_endl
00654 << " Current format: " << v4l2_to_vidl(dev.get_v4l2_format())
00655 << " width: "<< dev.get_width()<< " height: " << dev.get_height() << vcl_endl;
00656 return os;
00657 }
00658