libeblearntools
|
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_ */