core/vidl/vidl_v4l2_device.cxx
Go to the documentation of this file.
00001 #include "vidl_v4l2_device.h"
00002 //:
00003 // \file
00004 //
00005 // \author Antonio Garrido
00006 // \verbatim
00007 //  Modifications
00008 //   15 Apr 2009 Created (A. Garrido)
00009 //\endverbatim
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 // ----------------- local functions ---------------
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   // To set frame rate
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 // --------------- end local functions -------------------
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)) { // error ignored
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)) {// error ignored
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   // Now, add extended controls
00082   ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
00083   while (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) {
00084     if (!get_control_id(ctrl.id)) { // If not already included
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   // Now we should consider all possibilities
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   //try_formats();
00141   //update_controls();
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; // format not set
00184   fmt.fmt.pix.height =0;
00185   //if (try_some_formats) try_formats();
00186 
00187   // inputs already updated
00188   update_controls();//perhaps, we need to iterate over all inputs and reset controls, but probably, user is interested in current input
00189   for (int i=0;i<n_controls();++i)
00190     get_control(i)->reset();
00191   // Set default values?
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; //exit (EXIT_FAILURE);
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; // exit(EXIT_FAILURE);
00213   }
00214 
00215   fd = ::open(dev_name_.c_str(), O_RDWR /* required */ | 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; // exit(EXIT_FAILURE);
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)) { // Right now, only MMAP method
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   // change order
00266   // better select formats implemented in vidl
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   // add other formats...
00285 
00286   fmt.fmt.pix.width = 0; // not success
00287   fmt.fmt.pix.height =0;
00288 
00289   return false;
00290 }
00291 
00292 
00293 // Width and height could be changed by driver
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; // add to parameters?
00313     if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) {
00314       if (errno==EBUSY) { // try to recover the device
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; // add to parameters?
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     // Now we can set frame rate
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       // Convert the frame rate into a fraction for V4L2
00340       frame_rate=0;
00341       int n = 0, d = 0;
00342       double2fraction(fps,n,d);
00343       sfrate.parm.capture.timeperframe.numerator = d; // timeperframe instead of fps
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) { // Confirm frame rate
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)) // add to parameters?;
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) { // n_buffers is member
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, /*&buf*/&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 /* start anywhere */,
00426                   buffers[n_buffers].buf.length,
00427                   PROT_READ | PROT_WRITE /* required */,
00428                   MAP_SHARED /* recommended */,
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)  // enqueue again the last read buffer
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     /* Timeout. */
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) { // if EAGAIN -> iterate
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; //     errno_exit ("close");
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; // format unknown
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