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

Generated on Mon Mar 8 05:10:35 2010 for core/vidl by  doxygen 1.5.1