libeblearntools
/home/rex/ebltrunk/tools/libeblearntools/include/camera_v4l2.hpp
00001 /***************************************************************************
00002  *   Copyright (C) 2010 by Pierre Sermanet   *
00003  *   pierre.sermanet@gmail.com   *
00004  *   All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Redistribution under a license not approved by the Open Source 
00014  *       Initiative (http://www.opensource.org) must display the 
00015  *       following acknowledgement in all advertising material:
00016  *        This product includes software developed at the Courant
00017  *        Institute of Mathematical Sciences (http://cims.nyu.edu).
00018  *     * The names of the authors may not be used to endorse or promote products
00019  *       derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00024  * DISCLAIMED. IN NO EVENT SHALL ThE AUTHORS BE LIABLE FOR ANY
00025  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00026  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00028  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  ***************************************************************************/
00032 
00033 #ifndef CAMERA_V4L2_HPP_
00034 #define CAMERA_V4L2_HPP_
00035 
00036 #ifdef __LINUX__
00037 
00038 #include <fcntl.h>              /* low-level i/o */
00039 #include <unistd.h>
00040 #include <errno.h>
00041 #include <malloc.h>
00042 #include <sys/stat.h>
00043 #include <sys/types.h>
00044 #include <sys/time.h>
00045 #include <sys/mman.h>
00046 #include <sys/ioctl.h>
00047 #include <asm/types.h>          /* for videodev2.h */
00048 #include <linux/videodev2.h>
00049 
00050 #endif
00051 
00052 namespace ebl {
00053 
00055   // constructors & initializations
00056 
00057   template <typename Tdata>
00058   camera_v4l2<Tdata>::camera_v4l2(const char *device, int height_, int width_,
00059                                   bool grayscale_, bool mode_rgb_)
00060     : camera<Tdata>(height_, width_), started(false),
00061       nbuffers(grayscale_ ? 1 : 3), buffers(new void*[nbuffers]),
00062       sizes(new int[nbuffers]), mode_rgb(mode_rgb_) {
00063     cout << "Initializing V4l2 camera from device " << device
00064          << " to " << height_ << "x" << width_ << endl;
00065     if (grayscale_)
00066       cout << "V4l2 output is set to grayscale." << endl;
00067 #ifndef __LINUX__
00068     eblerror("V4l2 is for linux only");
00069 #else
00070     int fps = 30;
00071     int height1 = -1; // height returned by camera
00072     int width1 = -1; // width returned by camera 
00073     
00074     fd = open(device, O_RDWR);
00075     if (fd == -1) eblerror("could not open v4l2 device: " << device);
00076     struct v4l2_capability cap;
00077     struct v4l2_cropcap cropcap;
00078     struct v4l2_crop crop;
00079     struct v4l2_format fmt;
00080     memset((void*) &cap, 0, sizeof(struct v4l2_capability));
00081     int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
00082     if (ret < 0) {
00083       //      (==> this cleanup)
00084       eblerror("could not query v4l2 device");
00085     }
00086     if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
00087       // (==> this cleanup)
00088       eblerror("v4l2 device does not support video capture");
00089     }
00090     if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
00091       // (==> this cleanup)
00092       eblerror("v4l2 device does not support streaming i/o");
00093     }
00094     // resetting cropping to full frame
00095     memset((void*) &cropcap, 0, sizeof(struct v4l2_cropcap));
00096     cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00097     if (0 == ioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
00098       crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00099       crop.c = cropcap.defrect; 
00100       ioctl(fd, VIDIOC_S_CROP, &crop);
00101     }
00102     // get list of supported image formats
00103         memset((void*) &fmt, 0, sizeof(struct v4l2_format));
00104         ioctl(fd, VIDIOC_G_FMT, &fmt);
00105     
00106     // set format
00107     memset((void*) &fmt, 0, sizeof(struct v4l2_format));
00108     fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00109     // TODO: error when ratio not correct
00110     fmt.fmt.pix.width       = width_; 
00111     fmt.fmt.pix.height      = height_;
00112     // Looks like most cams dont support RGB output, converting it by hand
00113     //    if(mode_rgb)
00114     //      fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB;//V4L2_PIX_FMT_RGB32;
00115     // else
00116       fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
00117     fmt.fmt.pix.field       = V4L2_FIELD_ANY;
00118     if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
00119       // (==> this cleanup)
00120       eblerror("unable to set v4l2 format");
00121     }
00122     height1 = fmt.fmt.pix.height;
00123     width1 = fmt.fmt.pix.width;
00124     if (height != height1 || width != width1) {
00125       cout << "Warning: requested resolution " << height << "x" << width
00126            << " but camera changed it to " << height1 << "x" << width1 << endl;
00127       // enabling resizing as postprocessing
00128       bresize = true;
00129     } else // already resized to our target, disable resizing
00130       bresize = false;
00131 
00132     // set framerate
00133     struct v4l2_streamparm setfps;  
00134     memset((void*) &setfps, 0, sizeof(struct v4l2_streamparm));
00135     setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00136     setfps.parm.capture.timeperframe.numerator = 1;
00137     setfps.parm.capture.timeperframe.denominator = fps;
00138     ioctl(fd, VIDIOC_S_PARM, &setfps); 
00139     // allocate and map the buffers
00140     struct v4l2_requestbuffers rb;
00141     rb.count = nbuffers;
00142     rb.type =  V4L2_BUF_TYPE_VIDEO_CAPTURE;
00143     rb.memory = V4L2_MEMORY_MMAP;
00144     ret = ioctl(fd, VIDIOC_REQBUFS, &rb);
00145     if (ret < 0) {
00146       // (==> this cleanup)
00147       eblerror("could not allocate v4l2 buffers");
00148     }
00149     ret = 0;
00150     for (int i = 0; i < nbuffers; i++) { 
00151       struct v4l2_buffer buf;
00152       int r;
00153       memset((void*) &buf, 0, sizeof(struct v4l2_buffer));
00154       buf.index = i;
00155       buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00156       buf.memory = V4L2_MEMORY_MMAP;
00157       r = ioctl(fd, VIDIOC_QUERYBUF, &buf);
00158       //       printf("i=%u, length: %u, offset: %u, r=%d\n", i, buf.length, buf.m.offset, r); 
00159       if (r < 0)
00160         ret = -(i+1);
00161       if (ret == 0) {
00162         buffers[i] = mmap(0, buf.length, PROT_READ + PROT_WRITE, MAP_SHARED,
00163                           fd, buf.m.offset);
00164         sizes[i] = buf.length;
00165         if (buffers[i] == MAP_FAILED)
00166           ret = -(i+1000);
00167       }
00168     }
00169     if (ret < 0) {
00170       cout << "ret = " << ret << endl;
00171       if (ret > -1000) {
00172         cout << "query buffer " << - (1 + ret) << endl;
00173         //(==> this cleanup)
00174         eblerror("could not query v4l2 buffer");
00175       } else {
00176         cout << "map buffer " << - (1000 + ret) << endl;
00177         //(==> this cleanup)
00178         eblerror("could not map v4l2 buffer");
00179       }
00180     }
00181     frame = idx<Tdata>(height1, width1, nbuffers);
00182     print_controls();
00183     set_boolean_control(V4L2_CID_AUTOGAIN, false);
00184     set_boolean_control(V4L2_CID_AUTO_WHITE_BALANCE, false);
00185     //    set_integer_control(V4L2_CID_BACKLIGHT_COMPENSATION, 0);
00186     print_controls();
00187 #endif
00188   }
00189   
00190   template <typename Tdata>
00191   camera_v4l2<Tdata>::~camera_v4l2() {
00192     if (buffers)
00193       delete buffers;
00194     if (sizes)
00195       delete sizes;
00196   }
00197 
00198 #ifdef __LINUX__
00199   
00200   static void
00201   enumerate_menu(int fd, struct v4l2_queryctrl &queryctrl,
00202                  struct v4l2_querymenu &querymenu) {
00203     printf ("  Menu items:\n");
00204     memset (&querymenu, 0, sizeof (querymenu));
00205     querymenu.id = queryctrl.id;
00206   
00207     for (querymenu.index = queryctrl.minimum;
00208          (int) querymenu.index <= queryctrl.maximum; querymenu.index++) {
00209       if (0 == ioctl (fd, VIDIOC_QUERYMENU, &querymenu)) {
00210         printf ("  %s\n", querymenu.name);
00211       } else {
00212         eblerror ("VIDIOC_QUERYMENU");
00213       }
00214     }
00215   }
00216 
00217   template <typename Tdata>
00218   void camera_v4l2<Tdata>::print_controls() {
00219     cout << "__V4l2 camera controls___________________________________" << endl;
00220 
00221     struct v4l2_queryctrl queryctrl;
00222     struct v4l2_querymenu querymenu;
00223 
00224     memset (&queryctrl, 0, sizeof (queryctrl));
00225     for (queryctrl.id = V4L2_CID_BASE;
00226          queryctrl.id < V4L2_CID_LASTP1;
00227          queryctrl.id++) {
00228       if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
00229         if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
00230           continue;
00231         
00232         cout << queryctrl.name << " (" << queryctrl.id << "): "
00233              << get_control(queryctrl.id) << endl;
00234         
00235         if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
00236           enumerate_menu (fd, queryctrl, querymenu);
00237       } else {
00238         if (errno == EINVAL)
00239           continue;
00240         eblerror ("VIDIOC_QUERYCTRL");
00241       }
00242     }
00243 
00244     for (queryctrl.id = V4L2_CID_PRIVATE_BASE;;
00245          queryctrl.id++) {
00246       if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
00247         if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
00248           continue;
00249         
00250         printf ("Control %s\n", queryctrl.name);
00251         
00252         if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
00253           enumerate_menu (fd, queryctrl, querymenu);
00254       } else {
00255         if (errno == EINVAL)
00256           break;
00257         eblerror ("VIDIOC_QUERYCTRL");
00258       }
00259     }    
00260     cout << "_________________________________________________________" << endl;
00261   }
00262 
00263   template <typename Tdata>
00264   int camera_v4l2<Tdata>::get_control(int id) {
00265     struct v4l2_queryctrl queryctrl;
00266     struct v4l2_control control;
00267     
00268     memset (&queryctrl, 0, sizeof (queryctrl));
00269     queryctrl.id = id;
00270     if (-1 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
00271       if (errno != EINVAL) {
00272         eblerror("VIDIOC_QUERYCTRL");
00273       } else {
00274         printf ("V4L2_CID_BRIGHTNESS is not supported\n");
00275       }
00276     } else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
00277       printf ("V4L2_CID_BRIGHTNESS is not supported\n");
00278     } else {
00279       memset (&control, 0, sizeof (control));
00280       control.id = id;
00281       if (-1 == ioctl (fd, VIDIOC_G_CTRL, &control)) {
00282         eblerror ("VIDIOC_S_CTRL");
00283       }
00284     }
00285     return control.value;
00286   }
00287   
00288   template <typename Tdata>
00289   void camera_v4l2<Tdata>::set_integer_control(int id, int val) {
00290     struct v4l2_queryctrl queryctrl;
00291     struct v4l2_control control;
00292     
00293     memset (&queryctrl, 0, sizeof (queryctrl));
00294     queryctrl.id = id;
00295     if (-1 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
00296       if (errno != EINVAL) {
00297         eblerror("VIDIOC_QUERYCTRL");
00298       } else {
00299         printf ("V4L2_CID_BRIGHTNESS is not supported\n");
00300       }
00301     } else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
00302       printf ("V4L2_CID_BRIGHTNESS is not supported\n");
00303     } else {
00304       memset (&control, 0, sizeof (control));
00305       control.id = id;
00306       control.value = val;
00307       if (-1 == ioctl (fd, VIDIOC_S_CTRL, &control)) {
00308         eblerror ("VIDIOC_S_CTRL");
00309       }
00310     }
00311   }
00312   
00313   template <typename Tdata>
00314   void camera_v4l2<Tdata>::set_boolean_control(int id, bool val) {
00315     struct v4l2_control control;
00316 
00317     memset (&control, 0, sizeof (control));
00318     control.id = id;
00319     if (0 == ioctl (fd, VIDIOC_G_CTRL, &control)) {
00320       control.value += 1;
00321       /* The driver may clamp the value or return ERANGE, ignored here */
00322       if (-1 == ioctl (fd, VIDIOC_S_CTRL, &control)
00323           && errno != ERANGE) {
00324         eblerror ("VIDIOC_S_CTRL");
00325       }
00326       /* Ignore if V4L2_CID_CONTRAST is unsupported */
00327     } else if (errno != EINVAL) {
00328       eblerror ("VIDIOC_G_CTRL");
00329       exit (EXIT_FAILURE);
00330     }
00331     control.id = id;
00332     control.value = val;
00333     /* Errors ignored */
00334     ioctl (fd, VIDIOC_S_CTRL, &control);
00335   }
00336 
00338   // frame grabbing
00339 
00340   template <typename Tdata>
00341   void camera_v4l2<Tdata>::start() {
00342     int ret = 0;
00343     for (int i = 0; i < nbuffers; ++i) {
00344       struct v4l2_buffer buf;
00345       memset((void*) &buf, 0, sizeof(struct v4l2_buffer));
00346       buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00347       buf.memory      = V4L2_MEMORY_MMAP;
00348       buf.index       = i;
00349       ret += ioctl(fd, VIDIOC_QBUF, &buf);
00350     }
00351     if (ret < 0)
00352       cout << "WARNING: could not enqueue v4l2 buffers" << endl;
00353     enum v4l2_buf_type type;
00354     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00355     ret = ioctl(fd, VIDIOC_STREAMON, &type);
00356     if (ret < 0) {
00357       cout << "WARNING: could not start v4l2 capture" << endl;
00358       started = false;
00359     } else 
00360       started = true;
00361   }
00362   
00363 #endif
00364 
00365   template <typename Tdata>
00366   idx<Tdata> camera_v4l2<Tdata>::grab() {
00367 #ifdef __LINUX__
00368     if (!started)
00369       this->start();
00370     int ret = 0;
00371     int m0 = frame.mod(0);
00372     int m1 = frame.mod(1);
00373     struct v4l2_buffer buf;
00374     unsigned char *src;
00375     Tdata *dst = frame.idx_ptr();
00376     int i, j, k;
00377     memset((void*) &buf, 0, sizeof(struct v4l2_buffer));
00378     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00379     buf.memory = V4L2_MEMORY_MMAP;
00380     ret = ioctl(fd, VIDIOC_DQBUF, &buf);
00381     src = (unsigned char *)(buffers[buf.index]);
00382     for (i=0; i < height; i++) {
00383       for (j=0, k=0; j < width; j++, k+=m1) {
00384         int j2;
00385         j2 = j<<1;
00386         dst[k] = (Tdata) src[j2]; 
00387         if (j & 1) {
00388           dst[k+1] = (Tdata) src[j2-1];
00389           dst[k+2] = (Tdata) src[j2+1];
00390         } else {
00391           dst[k+1] = (Tdata) src[j2+1];
00392           dst[k+2] = (Tdata) src[j2+3];
00393         }
00394       }
00395       src += width << 1;
00396       dst += m0;
00397     }
00398     ret += ioctl(fd, VIDIOC_QBUF, &buf);
00399     if(mode_rgb) {
00400       // convert yuv to rgb
00401       idx<float>  fyuv(height,width,nbuffers);
00402       idx_copy(frame,fyuv);
00403       idx<float> frame_frgb(height,width,nbuffers);
00404       yuv_to_rgb(fyuv ,frame_frgb);
00405       idx<Tdata> frame_rgb(height,width,nbuffers);
00406       idx_copy(frame_frgb,frame_rgb);
00407       frame = frame_rgb;
00408     }
00409     // todo: resize in postprocessing if different size than expected
00410     //        e.g.: illegal ratio
00411 #endif
00412     frame_id_++;
00413     return this->postprocess();
00414   }
00415 
00416 } // end namespace ebl
00417 
00418 #endif /* CAMERA_V4L2_HPP_ */