00001 /*!@file Devices/V4L2grabber.C Interface with a video4linux2 frame grabber */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Devices/V4L2grabber.C $ 00035 // $Id: V4L2grabber.C 14753 2011-04-27 23:11:33Z itti $ 00036 // 00037 00038 #ifdef HAVE_LINUX_VIDEODEV2_H 00039 00040 #include "Devices/V4L2grabber.H" 00041 00042 #include "Component/OptionManager.H" // for option alias requests 00043 #include "Component/ModelOptionDef.H" 00044 #include "Devices/DeviceOpts.H" 00045 #include "Image/ColorOps.H" 00046 #include "Raster/GenericFrame.H" 00047 #include "Util/Assert.H" 00048 #include "Util/log.H" 00049 #include "Util/sformat.H" 00050 #include "Video/VideoFrame.H" 00051 00052 #include <cerrno> 00053 #include <fcntl.h> 00054 #include <sys/ioctl.h> 00055 #include <sys/mman.h> 00056 #include <sys/stat.h> 00057 #include <unistd.h> 00058 00059 //! for id-logging; see log.H: 00060 #define MYLOGID itsFd 00061 00062 namespace 00063 { 00064 struct V4L2Palette 00065 { 00066 VideoFormat vidformat; 00067 uint32 v4l2format; // V4L2_PIX_FMT_... 00068 }; 00069 00070 const V4L2Palette v4l2tab[VIDFMT_AUTO] = 00071 { 00072 { VIDFMT_GREY , V4L2_PIX_FMT_GREY }, 00073 { VIDFMT_RGB555 , V4L2_PIX_FMT_RGB555 }, 00074 { VIDFMT_RGB565 , V4L2_PIX_FMT_RGB565 }, 00075 { VIDFMT_RGB24 , V4L2_PIX_FMT_BGR24 }, 00076 { VIDFMT_RGB32 , V4L2_PIX_FMT_BGR32 }, 00077 { VIDFMT_YUYV , V4L2_PIX_FMT_YUYV }, 00078 { VIDFMT_UYVY , V4L2_PIX_FMT_UYVY }, 00079 { VIDFMT_YUV422 , V4L2_PIX_FMT_UYVY }, 00080 { VIDFMT_YUV411 , V4L2_PIX_FMT_Y41P }, 00081 { VIDFMT_YUV420 , V4L2_PIX_FMT_YUV420 }, 00082 { VIDFMT_YUV410 , V4L2_PIX_FMT_YUV410 }, 00083 { VIDFMT_YUV422P , V4L2_PIX_FMT_YUV422P }, 00084 { VIDFMT_YUV411P , V4L2_PIX_FMT_YUV411P }, 00085 { VIDFMT_YUV420P , V4L2_PIX_FMT_YUV420 }, 00086 { VIDFMT_YUV410P , V4L2_PIX_FMT_YUV410 }, 00087 { VIDFMT_MJPEG , V4L2_PIX_FMT_MJPEG }, 00088 { VIDFMT_HM12 , V4L2_PIX_FMT_HM12 } 00089 }; 00090 00091 //! Mapping between our VideoFormat and V4L2 pixfmt: 00092 // See Video/VideoFormat.H 00093 uint32 VideoFormat_to_V4L2Format(const VideoFormat vidformat) 00094 { 00095 for (size_t i = 0; i < sizeof(v4l2tab) / sizeof(v4l2tab[0]); ++i) 00096 if (vidformat == v4l2tab[i].vidformat) 00097 return v4l2tab[i].v4l2format; 00098 00099 // not found 00100 return 0xffffffff; 00101 } 00102 00103 VideoFormat V4L2Format_to_VideoFormat(const uint32 v4l2format) 00104 { 00105 for (size_t i = 0; i < sizeof(v4l2tab) / sizeof(v4l2tab[0]); ++i) 00106 if (v4l2format == v4l2tab[i].v4l2format) 00107 return v4l2tab[i].vidformat; 00108 00109 // not found 00110 return VIDFMT_AUTO; 00111 } 00112 00113 int ioctl_nointr(int d, int req, void* mem) 00114 { 00115 int result = 0; 00116 do { result = ioctl(d, req, mem); } 00117 while ((result < 0) && (errno == EINTR)); 00118 return result; 00119 } 00120 00121 // mapping of V4L2 controls to our command-line options: 00122 struct ControlMapping { 00123 uint v4l2; 00124 const ModelOptionDef *opt; 00125 }; 00126 00127 // see here for definitions: http://v4l2spec.bytesex.org/spec/x542.htm 00128 ControlMapping controlmapping[] = { 00129 // standard controls, in V4L2_CID_USER_CLASS 00130 { V4L2_CID_BRIGHTNESS, &OPT_FrameGrabberBrightness }, 00131 { V4L2_CID_CONTRAST, &OPT_FrameGrabberContrast }, 00132 { V4L2_CID_SATURATION, &OPT_FrameGrabberSaturation }, 00133 { V4L2_CID_HUE, &OPT_FrameGrabberHue }, 00134 { V4L2_CID_AUDIO_VOLUME, &OPT_FrameGrabberAudioVolume }, 00135 { V4L2_CID_AUDIO_BALANCE, &OPT_FrameGrabberAudioBalance }, 00136 { V4L2_CID_AUDIO_BASS, &OPT_FrameGrabberAudioBass }, 00137 { V4L2_CID_AUDIO_TREBLE, &OPT_FrameGrabberAudioTreble }, 00138 { V4L2_CID_AUDIO_MUTE, &OPT_FrameGrabberAudioMute }, 00139 { V4L2_CID_AUDIO_LOUDNESS, &OPT_FrameGrabberAudioLoudness }, 00140 { V4L2_CID_AUTO_WHITE_BALANCE, &OPT_FrameGrabberWhiteBalTempAuto }, // ?? 00141 { V4L2_CID_DO_WHITE_BALANCE, &OPT_FrameGrabberDoWhiteBal }, 00142 { V4L2_CID_RED_BALANCE, &OPT_FrameGrabberWhiteBalBU }, //? 00143 { V4L2_CID_BLUE_BALANCE, &OPT_FrameGrabberWhiteBalRV }, //? 00144 { V4L2_CID_GAMMA, &OPT_FrameGrabberGamma }, 00145 { V4L2_CID_EXPOSURE, &OPT_FrameGrabberExposure }, 00146 { V4L2_CID_AUTOGAIN, &OPT_FrameGrabberAutoGain }, 00147 { V4L2_CID_GAIN, &OPT_FrameGrabberGain }, 00148 { V4L2_CID_HFLIP, &OPT_FrameGrabberHFlip }, 00149 { V4L2_CID_VFLIP, &OPT_FrameGrabberVFlip }, 00150 { V4L2_CID_POWER_LINE_FREQUENCY, &OPT_FrameGrabberPowerLineFreq }, 00151 { V4L2_CID_HUE_AUTO, &OPT_FrameGrabberHueAuto }, 00152 { V4L2_CID_WHITE_BALANCE_TEMPERATURE, &OPT_FrameGrabberWhiteBalTemp }, 00153 { V4L2_CID_SHARPNESS, &OPT_FrameGrabberSharpness }, 00154 { V4L2_CID_BACKLIGHT_COMPENSATION, &OPT_FrameGrabberBacklightComp }, 00155 { V4L2_CID_CHROMA_AGC, NULL }, //FIXME 00156 { V4L2_CID_COLOR_KILLER, NULL }, //FIXME 00157 00158 #ifdef V4L2_CID_COLORFX 00159 { V4L2_CID_COLORFX, NULL }, //FIXME 00160 #endif 00161 00162 #ifdef V4L2_CID_AUTOBRIGHTNESS 00163 { V4L2_CID_AUTOBRIGHTNESS, NULL }, //FIXME 00164 #endif 00165 00166 #ifdef V4L2_CID_BAND_STOP_FILTER 00167 { V4L2_CID_BAND_STOP_FILTER, NULL }, //FIXME 00168 #endif 00169 00170 // extended controls in V4L2_CID_CAMERA_CLASS 00171 { V4L2_CID_EXPOSURE_AUTO, &OPT_FrameGrabberExposureMode }, 00172 { V4L2_CID_EXPOSURE_ABSOLUTE, &OPT_FrameGrabberExposureAbs }, 00173 { V4L2_CID_EXPOSURE_AUTO_PRIORITY, &OPT_FrameGrabberExposureAutoPri }, 00174 { V4L2_CID_PAN_RELATIVE, NULL }, //FIXME 00175 { V4L2_CID_TILT_RELATIVE, NULL }, //FIXME 00176 { V4L2_CID_PAN_RESET, NULL }, //FIXME 00177 { V4L2_CID_TILT_RESET, NULL }, //FIXME 00178 { V4L2_CID_PAN_ABSOLUTE, NULL }, //FIXME 00179 { V4L2_CID_TILT_ABSOLUTE, NULL }, //FIXME 00180 { V4L2_CID_FOCUS_ABSOLUTE, &OPT_FrameGrabberFocus }, 00181 { V4L2_CID_FOCUS_RELATIVE, NULL }, //FIXME 00182 { V4L2_CID_FOCUS_AUTO, &OPT_FrameGrabberFocusAuto }, 00183 { V4L2_CID_ZOOM_ABSOLUTE, &OPT_FrameGrabberZoom }, 00184 { V4L2_CID_ZOOM_RELATIVE, NULL }, //FIXME 00185 { V4L2_CID_ZOOM_CONTINUOUS, NULL }, //FIXME 00186 { V4L2_CID_PRIVACY, NULL }, //FIXME 00187 00188 { -1, NULL } // keep this as the last entry 00189 }; 00190 00191 } // namespace 00192 00193 // ###################################################################### 00194 V4L2grabber::V4L2grabber(OptionManager& mgr, const std::string& descrName, 00195 const std::string& tagName, const ParamFlag flags) : 00196 FrameIstream(mgr, descrName, tagName), 00197 // NOTE that contrary to the common case, we may give (by default 00198 // value of 'flags') USE_MY_VAL here when we construct the 00199 // OModelParam objects; that means that we push our values into the 00200 // ModelManager as the new default values, rather than having our 00201 // param take its value from the ModelManager's default 00202 itsDevName(&OPT_FrameGrabberDevice, this, "/dev/video0", flags), 00203 itsChannel(&OPT_FrameGrabberChannel, this, 1, flags | ALLOW_ONLINE_CHANGES), 00204 itsDims(&OPT_FrameGrabberDims, this, Dims(320, 240), flags), 00205 itsGrabMode(&OPT_FrameGrabberMode, this, VIDFMT_RGB24, flags), 00206 itsByteSwap(&OPT_FrameGrabberByteSwap, this, true, flags), 00207 itsStreamingMode(&OPT_FrameGrabberStreaming, this), 00208 itsNbuf(&OPT_FrameGrabberNbuf, this, 4, flags), 00209 itsFd(-1), 00210 itsMmapBuf(NULL), 00211 itsMmapBufSize(NULL), 00212 itsReadBuf(), 00213 itsCurrentFrame(0), 00214 itsGrabbing(NULL), 00215 itsFrameTime(SimTime::ZERO()), 00216 itsListener(), 00217 itsStreamStarted(false), 00218 itsCanMMap(false), 00219 itsCanRW(false), 00220 itsOptionFlags(flags) 00221 { 00222 openDevice(); 00223 00224 // request a bunch of camera aliases which work with V4L2: 00225 mgr.requestOptionAlias(&OPT_ALIAScamBttv); 00226 mgr.requestOptionAlias(&OPT_ALIAScamMacbook); 00227 mgr.requestOptionAlias(&OPT_ALIAScamLifeCam); 00228 mgr.requestOptionAlias(&OPT_ALIAScamLifeCamManual); 00229 mgr.requestOptionAlias(&OPT_ALIAScamHPpremAF); 00230 mgr.requestOptionAlias(&OPT_ALIAScamHPpremAFmanual); 00231 mgr.requestOptionAlias(&OPT_ALIAScamC910); 00232 mgr.requestOptionAlias(&OPT_ALIAScamC910manual); 00233 mgr.requestOptionAlias(&OPT_ALIAScamC910turntable); 00234 } 00235 00236 // ###################################################################### 00237 void V4L2grabber::openDevice() 00238 { 00239 if (itsFd != -1) closeDevice(); 00240 00241 itsFd = open(itsDevName.getVal().c_str(), O_RDWR); 00242 // since we will be called upon construction of the grabber with whichever default device name, do not LFATAL here if 00243 // the device does not open. We may be called again with a different device name if that name is changed at the 00244 // command line. In the end, during start1() we will check that the final selected device did open and that it has the 00245 // right capabilities (itsCanRW or itsCanMMap at the minimum): 00246 if (itsFd == -1) { PLERROR("Failed to open V4L2 device %s", itsDevName.getVal().c_str()); return; } 00247 00248 LINFO("Opened V4L2 device named: %s", itsDevName.getVal().c_str()); 00249 00250 // get frame grabber capabilities: 00251 struct v4l2_capability vc; itsCanRW = false; itsCanMMap = false; 00252 if (ioctl_nointr(itsFd, VIDIOC_QUERYCAP, &vc) < 0) IDPLFATAL("Cannot get V4L2 device capabilities"); 00253 IDLINFO("V4L2 kernel driver: %s", vc.driver); 00254 IDLINFO("FrameGrabber board name is: %s", vc.card); 00255 IDLINFO("FrameGrabber board bus info: %s", vc.bus_info); 00256 00257 // note: these defs from /usr/include/linux/videodev2.h as of kernel 2.6.32, some may not be defined in older kernels 00258 // and should then be commented out: 00259 if (vc.capabilities & V4L2_CAP_VIDEO_CAPTURE) IDLINFO(" > Supports video capture"); 00260 else IDLFATAL("Not a video capture device."); 00261 if (vc.capabilities & V4L2_CAP_VIDEO_OUTPUT) IDLINFO(" > Supports video output"); 00262 if (vc.capabilities & V4L2_CAP_VIDEO_OVERLAY) IDLINFO(" > Supports video overlay"); 00263 if (vc.capabilities & V4L2_CAP_VBI_CAPTURE) IDLINFO(" > Supports raw VBI capture"); 00264 if (vc.capabilities & V4L2_CAP_VBI_OUTPUT) IDLINFO(" > Supports raw VBI output"); 00265 if (vc.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) IDLINFO(" > Supports sliced VBI capture"); 00266 if (vc.capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) IDLINFO(" > Supports sliced VBI_OUTPUT"); 00267 if (vc.capabilities & V4L2_CAP_RDS_CAPTURE) IDLINFO(" > Supports RDS capture"); 00268 if (vc.capabilities & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) IDLINFO(" > Supports video output overlay"); 00269 if (vc.capabilities & V4L2_CAP_HW_FREQ_SEEK) IDLINFO(" > Supports hardware frequency seek"); 00270 #ifdef V4L2_CAP_RDS_OUTPUT 00271 if (vc.capabilities & V4L2_CAP_RDS_OUTPUT) IDLINFO(" > Supports RDS output"); 00272 #endif 00273 if (vc.capabilities & V4L2_CAP_TUNER) IDLINFO(" > Has an RF tuner and/or modulator"); 00274 if (vc.capabilities & V4L2_CAP_AUDIO) IDLINFO(" > Supports audio input and/or output"); 00275 if (vc.capabilities & V4L2_CAP_RADIO) IDLINFO(" > Supports radio"); 00276 #ifdef V4L2_CAP_MODULATOR 00277 if (vc.capabilities & V4L2_CAP_MODULATOR) IDLINFO(" > Supports a modulator"); 00278 #endif 00279 00280 if (vc.capabilities & V4L2_CAP_READWRITE) { IDLINFO(" > Supports read/write I/O method"); itsCanRW = true; } 00281 else itsCanRW = false; 00282 if (vc.capabilities & V4L2_CAP_ASYNCIO) IDLINFO(" > Supports asynchronous I/O method"); 00283 if (vc.capabilities & V4L2_CAP_STREAMING) { IDLINFO(" > Supports streaming I/O (MMAP) method"); itsCanMMap=true; } 00284 else itsCanMMap = false; 00285 00286 // List available controls and their current settings. First try to do this using the new method, for devices that 00287 // support the new extended controls API. If that fails we will fall back to the older method for standard controls: 00288 struct v4l2_queryctrl ctrl; memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; 00289 00290 if (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0) { 00291 IDLINFO(" > Supports Extended Controls API"); 00292 do { 00293 // add a command-line option for the control, and describe it: 00294 addControl(ctrl); 00295 00296 // get ready to query the next control: 00297 ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; 00298 } while (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0); 00299 00300 // on some cameras (e.g., Microsoft LifeCam), we just got all the controls and we are done. On others, more controls 00301 // are available by excplicitly setting the class (e.g., HP Premium AF). So let's try that too, addControl() will 00302 // need to make sure we don't try to add a control twice: 00303 memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_CLASS_MPEG; 00304 if (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0) 00305 do { addControl(ctrl); ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; } 00306 while (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0); 00307 00308 memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_CLASS_CAMERA; 00309 if (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0) 00310 do { addControl(ctrl); ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; } 00311 while (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0); 00312 00313 #ifdef V4L2_CTRL_CLASS_FM_TX 00314 memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_CLASS_FM_TX; 00315 if (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0) 00316 do { addControl(ctrl); ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; } 00317 while (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0); 00318 #endif 00319 00320 } else { 00321 IDLINFO(" > Does not support Extended Controls API"); 00322 memset(&ctrl, 0, sizeof(ctrl)); 00323 00324 for (ctrl.id = V4L2_CID_BASE; ctrl.id < V4L2_CID_LASTP1; ++ctrl.id) 00325 if (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0) 00326 addControl(ctrl); // add a command-line option for the control, and describe it: 00327 00328 for (ctrl.id = V4L2_CID_PRIVATE_BASE; ; ++ctrl.id) 00329 if (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0) 00330 addControl(ctrl); // add a command-line option for the control, and describe it: 00331 else break; 00332 } 00333 } 00334 00335 // ###################################################################### 00336 void V4L2grabber::addControl(const struct v4l2_queryctrl& ctrl) 00337 { 00338 // make sure we have not already added the control. This is because on some cameras the enumeration of extended 00339 // controls is broken, see comments in openDevice(): 00340 for (size_t i = 0; i < itsControls.size(); ++i) if (itsControls[i]->cid == ctrl.id) return; 00341 00342 std::string info = "CONTROL: "; std::vector<std::string> infomenu; 00343 00344 // find a command-line option for this control: 00345 int ii = 0; const ModelOptionDef *option = NULL; 00346 while (controlmapping[ii].v4l2) 00347 if (controlmapping[ii].v4l2 == ctrl.id) { option = controlmapping[ii].opt; break; } else ++ ii; 00348 00349 // display some info about this control: 00350 switch(ctrl.type) 00351 { 00352 case V4L2_CTRL_TYPE_INTEGER: 00353 info += sformat("%s [int] Def=%d, Rng=[%d ..(%d).. %d]", 00354 ctrl.name, ctrl.default_value, ctrl.minimum, ctrl.step, ctrl.maximum); 00355 if (option) { 00356 rutz::shared_ptr<V4L2grabberControl<int> > 00357 param(new V4L2grabberControl<int>(option, this, ctrl.default_value, itsOptionFlags, ctrl.id, ctrl.type)); 00358 itsControls.push_back(param); 00359 getManager().requestOption(param->param, true); // always use my val (the default) so change is not rejected 00360 info += " (use --" + std::string(option->longoptname) + "=<int>)"; 00361 } else info = "UNSUPPORTED " + info; 00362 00363 break; 00364 00365 case V4L2_CTRL_TYPE_BOOLEAN: 00366 info += sformat("%s [boolean] Def=%s", ctrl.name, ctrl.default_value ? "true" : "false"); 00367 if (option) { 00368 rutz::shared_ptr<V4L2grabberControl<bool> > 00369 param(new V4L2grabberControl<bool>(option, this, ctrl.default_value, itsOptionFlags, ctrl.id, ctrl.type)); 00370 itsControls.push_back(param); 00371 getManager().requestOption(param->param, true); 00372 info += " (use --" + std::string(option->longoptname) + "=<bool>)"; 00373 } else info = "UNSUPPORTED " + info; 00374 00375 break; 00376 00377 case V4L2_CTRL_TYPE_MENU: 00378 info += sformat("%s [menu] Def=%d", ctrl.name, ctrl.default_value); 00379 { 00380 // enumerate the menu items: 00381 struct v4l2_querymenu querymenu; memset(&querymenu, 0, sizeof(querymenu)); 00382 querymenu.id = ctrl.id; 00383 00384 for (querymenu.index = ctrl.minimum; int(querymenu.index) <= ctrl.maximum; ++querymenu.index) { 00385 if (ioctl_nointr(itsFd, VIDIOC_QUERYMENU, &querymenu) == 0) 00386 infomenu.push_back(sformat("CONTROL: - %d = %s", querymenu.index, querymenu.name)); 00387 else IDPLERROR("VIDIOC_QUERYMENU"); 00388 } 00389 } 00390 00391 if (option) { 00392 rutz::shared_ptr<V4L2grabberControl<int> > 00393 param(new V4L2grabberControl<int>(option, this, ctrl.default_value, itsOptionFlags, ctrl.id, ctrl.type)); 00394 itsControls.push_back(param); 00395 getManager().requestOption(param->param, true); 00396 info += " (use --" + std::string(option->longoptname) + "=<int>)"; 00397 } else info = "UNSUPPORTED " + info; 00398 00399 break; 00400 00401 case V4L2_CTRL_TYPE_BUTTON: 00402 info += sformat("%s [button]", ctrl.name); 00403 00404 if (option) { 00405 rutz::shared_ptr<V4L2grabberControl<bool> > 00406 param(new V4L2grabberControl<bool>(option, this, ctrl.default_value, itsOptionFlags, ctrl.id, ctrl.type)); 00407 itsControls.push_back(param); 00408 getManager().requestOption(param->param, true); 00409 info += " (use --" + std::string(option->longoptname) + "=<bool>)"; 00410 } else info = "UNSUPPORTED " + info; 00411 00412 break; 00413 00414 case V4L2_CTRL_TYPE_INTEGER64: 00415 info += sformat("%s [int64]", ctrl.name); 00416 00417 if (option) { 00418 rutz::shared_ptr<V4L2grabberControl<int64> > 00419 param(new V4L2grabberControl<int64>(option, this, ctrl.default_value, itsOptionFlags, ctrl.id, ctrl.type)); 00420 itsControls.push_back(param); 00421 getManager().requestOption(param->param, true); 00422 info += " (use --" + std::string(option->longoptname) + "=<int64>)"; 00423 } else info = "UNSUPPORTED " + info; 00424 00425 break; 00426 00427 case V4L2_CTRL_TYPE_CTRL_CLASS: // Note: this should never happen 00428 info += sformat("%s [control class]", ctrl.name); 00429 break; 00430 00431 #ifdef V4L2_CTRL_TYPE_STRING 00432 case V4L2_CTRL_TYPE_STRING: 00433 info += sformat("%s [string]", ctrl.name); 00434 00435 if (option) { 00436 rutz::shared_ptr<V4L2grabberControl<std::string> > 00437 param(new V4L2grabberControl<std::string>(option, this, "", itsOptionFlags, ctrl.id, ctrl.type)); 00438 itsControls.push_back(param); 00439 getManager().requestOption(param->param, true); 00440 info += " (use --" + std::string(option->longoptname) + "=<string>)"; 00441 } else info = "UNSUPPORTED " + info; 00442 00443 break; 00444 #endif 00445 00446 default: 00447 info += sformat("%s [unknown control 0x%x]", ctrl.name, ctrl.id); 00448 break; 00449 } 00450 00451 std::vector<std::string> infoflags; 00452 if (ctrl.flags & V4L2_CTRL_FLAG_DISABLED) infoflags.push_back("DISABLED"); 00453 if (ctrl.flags & V4L2_CTRL_FLAG_GRABBED) infoflags.push_back("GRABBED"); 00454 if (ctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) infoflags.push_back("READONLY"); 00455 if (ctrl.flags & V4L2_CTRL_FLAG_UPDATE) infoflags.push_back("UPDATE"); 00456 if (ctrl.flags & V4L2_CTRL_FLAG_INACTIVE) infoflags.push_back("INACTIVE"); 00457 if (ctrl.flags & V4L2_CTRL_FLAG_SLIDER) infoflags.push_back("SLIDER"); 00458 00459 if (infoflags.size() > 0) { 00460 info += " (" + infoflags[0]; 00461 for (size_t ii = 1; ii < infoflags.size(); ++ii) info += ", " + infoflags[ii]; 00462 info += ")"; 00463 } 00464 00465 // print the info: 00466 IDLINFO("%s", info.c_str()); 00467 for (size_t ii = 0; ii < infomenu.size(); ++ii) IDLINFO("%s", infomenu[ii].c_str()); 00468 00469 // now set the value. If it is an extended control, we will change it using the extended control interface. Note that 00470 // here, some settings may fail, e.g., setting the manual focus value when autofocus is on. So we are going to be very 00471 // quiet about errors and just show everything at the LDEBUG level: 00472 if (V4L2_CTRL_ID2CLASS(ctrl.id) != V4L2_CTRL_CLASS_USER) { 00473 struct v4l2_ext_controls extctrls; memset(&extctrls, 0, sizeof(extctrls)); 00474 extctrls.count = 1; extctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id); 00475 00476 struct v4l2_ext_control xc; memset(&xc, 0, sizeof(xc)); extctrls.controls = &xc; 00477 00478 xc.id = ctrl.id; xc.value = ctrl.default_value; 00479 00480 if (ioctl_nointr(itsFd, VIDIOC_S_EXT_CTRLS, &extctrls) == -1) 00481 IDPLDEBUG("NOTE: Failed to set '%s' to %d (VIDIOC_S_EXT_CTRLS)", ctrl.name, ctrl.default_value); 00482 else IDLDEBUG("Suceesfully set control '%s' to value %d", ctrl.name, ctrl.default_value); 00483 } else { 00484 // old-style interface: 00485 struct v4l2_control c; memset(&c, 0, sizeof(c)); 00486 c.id = ctrl.id; c.value = ctrl.default_value; 00487 00488 if (ioctl_nointr(itsFd, VIDIOC_S_CTRL, &c) == -1) 00489 IDPLDEBUG("NOTE: Failed to set '%s' to %d (VIDIOC_S_EXT_CTRLS)", ctrl.name, ctrl.default_value); 00490 else IDLDEBUG("Successfully set control '%s' to value %d", ctrl.name, ctrl.default_value); 00491 } 00492 } 00493 00494 // ###################################################################### 00495 void V4L2grabber::closeDevice() 00496 { 00497 if (itsStreamStarted) 00498 { 00499 enum v4l2_buf_type typ = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00500 if (ioctl_nointr(itsFd, VIDIOC_STREAMOFF, &typ)) IDPLERROR("VIDIOC_STREAMOFF"); 00501 itsStreamStarted = false; 00502 } 00503 00504 if (itsMmapBuf) 00505 { 00506 for (int i = 0; i < itsNbuf.getVal(); i ++) munmap(itsMmapBuf[i], itsMmapBufSize[i]); 00507 delete [] itsMmapBuf; itsMmapBuf = NULL; 00508 delete [] itsMmapBufSize; itsMmapBufSize = NULL; 00509 } 00510 00511 if (itsFd >= 0) { close(itsFd); itsFd = -1; } 00512 if (itsGrabbing) { delete [] itsGrabbing; itsGrabbing = NULL; } 00513 itsReadBuf = Image<byte>(); 00514 itsStreamStarted = false; 00515 } 00516 00517 // ###################################################################### 00518 void V4L2grabber::start1() 00519 { 00520 if (itsFd == -1) openDevice(); 00521 00522 if (itsCanMMap == false && itsCanRW == false) IDLFATAL("No known frame grabbing method supported by hardware"); 00523 00524 // list available channels: 00525 for (int i = 0; ; i++) 00526 { 00527 struct v4l2_input inp; memset(&inp, 0, sizeof(inp)); inp.index = i; 00528 if (ioctl_nointr(itsFd, VIDIOC_ENUMINPUT, &inp) != 0) break; 00529 IDLINFO("Video input %d is '%s'", i, inp.name); 00530 } 00531 00532 // select desired channel: 00533 int channel = itsChannel.getVal(); 00534 if (ioctl_nointr(itsFd, VIDIOC_S_INPUT, &channel) < 0) IDPLERROR("Cannot select video input %d", itsChannel.getVal()); 00535 else IDLINFO("Selected video input %d", itsChannel.getVal()); 00536 00537 // Reset cropping parameters and get resolution capabilities. NOTE: just open()'ing the device does not reset it, 00538 // according to the unix toolchain philosophy. Hence, although here we do not provide support for cropping, we still 00539 // need to ensure that it is properly reset: 00540 struct v4l2_cropcap cropcap; 00541 memset (&cropcap, 0, sizeof(cropcap)); 00542 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00543 if (ioctl_nointr(itsFd, VIDIOC_CROPCAP, &cropcap) == -1) IDPLERROR("Failed to get cropping capabilities -- IGNORED."); 00544 else { 00545 IDLINFO("Video capture bounds: (%d, %d) -> (%d, %d)", cropcap.bounds.left, cropcap.bounds.top, 00546 cropcap.bounds.left + cropcap.bounds.width - 1, cropcap.bounds.top + cropcap.bounds.height - 1); 00547 IDLINFO("Video default capture rectangle: (%d, %d) -> (%d, %d)", cropcap.defrect.left, cropcap.defrect.top, 00548 cropcap.defrect.left + cropcap.defrect.width - 1, cropcap.defrect.top + cropcap.defrect.height - 1); 00549 } 00550 00551 struct v4l2_crop crop; memset (&crop, 0, sizeof(crop)); 00552 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.c = cropcap.defrect; 00553 if (ioctl_nointr(itsFd, VIDIOC_S_CROP, &crop) == -1) IDPLERROR("Failed to reset cropping settings -- IGNORED."); 00554 00555 // list available video formats and see which ones would work with the requested resolution: 00556 struct v4l2_format fmt; memset(&fmt, 0, sizeof(fmt)); 00557 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00558 fmt.fmt.pix.width = itsDims.getVal().w(); 00559 fmt.fmt.pix.height = itsDims.getVal().h(); 00560 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 00561 int fmt_to_use = -1; // in case user selected "auto" format 00562 00563 for (int i = 0; ; ++ i) { 00564 struct v4l2_fmtdesc fdesc; memset(&fdesc, 0, sizeof(fdesc)); 00565 fdesc.index = i; fdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00566 if (ioctl_nointr(itsFd, VIDIOC_ENUM_FMT, &fdesc) != 0) break; 00567 00568 const VideoFormat f = V4L2Format_to_VideoFormat(fdesc.pixelformat); 00569 00570 // try to see whether that would work with our given image dims: 00571 fmt.fmt.pix.pixelformat = fdesc.pixelformat; 00572 bool worked = false; 00573 if (ioctl_nointr(itsFd, VIDIOC_TRY_FMT, &fmt) == 0) { 00574 worked = true; if (fmt_to_use == -1) fmt_to_use = fdesc.pixelformat; 00575 } 00576 IDLINFO("Video Format: Use '%s' for '%s (Fourcc: %c%c%c%c)'%s%s", 00577 f == VIDFMT_AUTO ? 00578 "????" : convertToString(f).c_str(), 00579 fdesc.description, 00580 ((char*)(&fdesc.pixelformat))[0], 00581 ((char*)(&fdesc.pixelformat))[1], 00582 ((char*)(&fdesc.pixelformat))[2], 00583 ((char*)(&fdesc.pixelformat))[3], 00584 fdesc.flags & V4L2_FMT_FLAG_COMPRESSED ? " (COMPRESSED)" : "", 00585 worked ? " [OK]" : " [FAILED TO SET]"); 00586 } 00587 00588 // Get the frame time, according to current video standard: 00589 struct v4l2_streamparm sparm; memset(&sparm, 0, sizeof(sparm)); 00590 sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00591 if (ioctl_nointr(itsFd, VIDIOC_G_PARM, &sparm) == -1) { 00592 IDPLERROR("Cannot get video standard params - ASSUMING NTSC"); 00593 itsFrameTime = SimTime::HERTZ(29.97); 00594 } 00595 else itsFrameTime = SimTime::SECS(float(sparm.parm.capture.timeperframe.numerator) / 00596 float(sparm.parm.capture.timeperframe.denominator)); 00597 IDLINFO("Capture at %.2f fps", itsFrameTime.hertz()); 00598 00599 // try to select a video mode and image format: 00600 if (itsGrabMode.getVal() == VIDFMT_AUTO) { 00601 if (fmt_to_use == -1) IDLFATAL("Could not find a working video mode for given resolution."); 00602 fmt.fmt.pix.pixelformat = fmt_to_use; 00603 } else fmt.fmt.pix.pixelformat = VideoFormat_to_V4L2Format(itsGrabMode.getVal()); 00604 00605 if (ioctl_nointr(itsFd, VIDIOC_S_FMT, &fmt) == -1) 00606 IDPLFATAL("Cannot set requested video mode/resolution %s [%dx%d], probably unsupported by hardware.", 00607 convertToString(itsGrabMode.getVal()).c_str(), itsDims.getVal().w(), itsDims.getVal().h()); 00608 00609 if (ioctl_nointr(itsFd, VIDIOC_G_FMT, &fmt) == 0) 00610 IDLINFO("Video mode/resolution set to %s [%dx%d]", convertToString(itsGrabMode.getVal()).c_str(), 00611 fmt.fmt.pix.width, fmt.fmt.pix.height); 00612 00613 if (int(fmt.fmt.pix.width) != itsDims.getVal().w() || int(fmt.fmt.pix.height) != itsDims.getVal().h()) 00614 IDLFATAL("Hardware suggests changing input dims to %dx%d instead of requested %dx%d", 00615 fmt.fmt.pix.width, fmt.fmt.pix.height, itsDims.getVal().w(), itsDims.getVal().h()); 00616 00617 // if MMAP is supported, allocate a mmap'ed buffer: 00618 if (itsCanMMap) { 00619 IDLINFO("Using mmap'ed image capture with %d buffers", itsNbuf.getVal()); 00620 struct v4l2_requestbuffers req; memset(&req, 0, sizeof(req)); 00621 req.count = itsNbuf.getVal(); 00622 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00623 req.memory = V4L2_MEMORY_MMAP; 00624 00625 if (ioctl_nointr(itsFd, VIDIOC_REQBUFS, &req) == -1) 00626 IDPLFATAL("Cannot allocate %d mmap'ed video frame buffers", itsNbuf.getVal()); 00627 if (int(req.count) != itsNbuf.getVal()) 00628 IDLFATAL("Hardware can only support %d video frame buffers (vs. %d requested)", req.count, itsNbuf.getVal()); 00629 00630 itsMmapBuf = new byte*[req.count]; 00631 itsMmapBufSize = new int[req.count]; 00632 00633 for (uint i = 0; i < req.count; ++i) { 00634 struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); 00635 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00636 buf.memory = V4L2_MEMORY_MMAP; 00637 buf.index = i; 00638 00639 if (ioctl_nointr(itsFd, VIDIOC_QUERYBUF, &buf) == -1) IDPLFATAL("Could not query for MMAP buffer"); 00640 00641 itsMmapBufSize[i] = buf.length; 00642 itsMmapBuf[i] = static_cast<byte*>(mmap(NULL, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, itsFd, buf.m.offset)); 00643 00644 if (itsMmapBuf[i] == MAP_FAILED) IDPLFATAL("Error MMAP'ing video buffer number %d", i); 00645 } 00646 00647 // we don't need the read() buffer: 00648 itsReadBuf = Image<byte>(); 00649 } else { 00650 // get ready for read() access method: 00651 IDLINFO("Using read() image capture"); 00652 itsReadBuf = Image<byte>(getFrameSize(itsGrabMode.getVal(), itsDims.getVal()), 1, NO_INIT); 00653 itsMmapBuf = NULL; 00654 itsMmapBufSize = NULL; 00655 } 00656 00657 // get ready to grab frames, starting with buffer/frame 0: 00658 IDLDEBUG("Using %d grab buffers", itsNbuf.getVal()); 00659 itsCurrentFrame = 0; 00660 itsGrabbing = new bool[itsNbuf.getVal()]; 00661 for (int i = 0; i < itsNbuf.getVal(); ++i) itsGrabbing[i] = false; 00662 } 00663 00664 // ###################################################################### 00665 void V4L2grabber::stop2() 00666 { 00667 closeDevice(); 00668 } 00669 00670 // ###################################################################### 00671 V4L2grabber::~V4L2grabber() 00672 { } 00673 00674 // ###################################################################### 00675 void V4L2grabber::setListener(rutz::shared_ptr<FrameListener> listener) 00676 { itsListener = listener; } 00677 00678 // ###################################################################### 00679 void V4L2grabber::paramChanged(ModelParamBase* const param, const bool valueChanged, 00680 ParamClient::ChangeStatus* status) 00681 { 00682 // re-open the device if its name was changed: 00683 if (valueChanged && param == &itsDevName) { 00684 if (started()) IDLFATAL("Cannot change device while started"); 00685 closeDevice(); openDevice(); 00686 } 00687 00688 if (valueChanged && param == &itsChannel) { 00689 int channel = itsChannel.getVal(); 00690 if (ioctl_nointr(itsFd, VIDIOC_S_INPUT, &channel) < 0) 00691 IDPLERROR("Cannot select video input %d", itsChannel.getVal()); 00692 else 00693 IDLINFO("Selected video input %d", itsChannel.getVal()); 00694 } 00695 00696 // is that one of our camera controls? 00697 if (valueChanged) 00698 for (size_t i = 0; i < itsControls.size(); ++i) { 00699 int val = 0; const char *name = NULL; 00700 00701 // match the param and get its value: 00702 switch(itsControls[i]->ctype) { 00703 case V4L2_CTRL_TYPE_INTEGER: 00704 { 00705 const V4L2grabberControl<int>& c = dynamic_cast<V4L2grabberControl<int>&>(*itsControls[i]); 00706 const OModelParam<int>& p = c.param; 00707 if (param == &p) { val = p.getVal(); name = p.getOptionDef()->longoptname; } 00708 } 00709 break; 00710 case V4L2_CTRL_TYPE_BOOLEAN: 00711 { 00712 const V4L2grabberControl<bool>& c = dynamic_cast<V4L2grabberControl<bool>&>(*itsControls[i]); 00713 const OModelParam<bool>& p = c.param; 00714 if (param == &p) { val = p.getVal(); name = p.getOptionDef()->longoptname; } 00715 } 00716 break; 00717 case V4L2_CTRL_TYPE_MENU: 00718 { 00719 const V4L2grabberControl<int>& c = dynamic_cast<V4L2grabberControl<int>&>(*itsControls[i]); 00720 const OModelParam<int>& p = c.param; 00721 if (param == &p) { val = p.getVal(); name = p.getOptionDef()->longoptname; } 00722 } 00723 break; 00724 case V4L2_CTRL_TYPE_BUTTON: 00725 { 00726 const V4L2grabberControl<bool>& c = dynamic_cast<V4L2grabberControl<bool>&>(*itsControls[i]); 00727 const OModelParam<bool>& p = c.param; 00728 if (param == &p) { val = p.getVal(); name = p.getOptionDef()->longoptname; } 00729 } 00730 break; 00731 case V4L2_CTRL_TYPE_INTEGER64: 00732 { 00733 const V4L2grabberControl<int64>& c = dynamic_cast<V4L2grabberControl<int64>&>(*itsControls[i]); 00734 const OModelParam<int64>& p = c.param; 00735 if (param == &p) { val = static_cast<int>(p.getVal()); name = p.getOptionDef()->longoptname; } 00736 } 00737 break; 00738 00739 #ifdef V4L2_CTRL_TYPE_STRING 00740 case V4L2_CTRL_TYPE_STRING: 00741 { 00742 const V4L2grabberControl<std::string>& c = dynamic_cast<V4L2grabberControl<std::string>&>(*itsControls[i]); 00743 const OModelParam<std::string>& p = c.param; 00744 if (param == &p) IDLFATAL("Changing value of string control not yet supported"); 00745 } 00746 break; 00747 #endif 00748 default: IDLFATAL("Changing value of control of type 0x%x not supported", itsControls[i]->ctype); break; 00749 } 00750 00751 if (name) { 00752 // ok, we got the param. If it is extended, we will change it using the extended control interface: 00753 if (V4L2_CTRL_ID2CLASS(itsControls[i]->cid) != V4L2_CTRL_CLASS_USER) { 00754 struct v4l2_ext_controls extctrls; memset(&extctrls, 0, sizeof(extctrls)); 00755 extctrls.count = 1; // we'll set one extended control at a time 00756 extctrls.ctrl_class = V4L2_CTRL_ID2CLASS(itsControls[i]->cid); 00757 00758 struct v4l2_ext_control xctrl; memset(&xctrl, 0, sizeof(xctrl)); 00759 extctrls.controls = &xctrl; // our one extended control to set 00760 00761 xctrl.id = itsControls[i]->cid; 00762 xctrl.value = val; 00763 00764 if (ioctl_nointr(itsFd, VIDIOC_S_EXT_CTRLS, &extctrls) == -1) { 00765 IDPLERROR("NOTE: Failed to set --%s to %d (VIDIOC_S_EXT_CTRLS)", name, val); 00766 //*status = ParamClient::CHANGE_REJECTED; 00767 } else IDLDEBUG("Changed control --%s to value %d", name, val); 00768 } else { 00769 // old-style interface: 00770 struct v4l2_control ctrl; memset(&ctrl, 0, sizeof(ctrl)); 00771 ctrl.id = itsControls[i]->cid; 00772 ctrl.value = val; 00773 00774 if (ioctl_nointr(itsFd, VIDIOC_S_CTRL, &ctrl) == -1) { 00775 IDPLERROR("NOTE: Failed to set --%s to %d (VIDIOC_S_EXT_CTRLS)", name, val); 00776 //*status = ParamClient::CHANGE_REJECTED; 00777 } else IDLDEBUG("Changed control --%s to value %d", name, val); 00778 } 00779 } 00780 } 00781 00782 FrameIstream::paramChanged(param, valueChanged, status); 00783 } 00784 00785 // ###################################################################### 00786 void V4L2grabber::startStream() 00787 { 00788 // reset itsStreamStarted so that we wait for any pending frames to 00789 // be grabbed, then start fresh grabbing requests: 00790 itsStreamStarted = false; 00791 00792 this->restartStream(); 00793 } 00794 00795 // ###################################################################### 00796 SimTime V4L2grabber::getNaturalFrameTime() const 00797 { 00798 return itsFrameTime; 00799 } 00800 00801 // ###################################################################### 00802 GenericFrameSpec V4L2grabber::peekFrameSpec() 00803 { 00804 GenericFrameSpec result; 00805 00806 result.nativeType = GenericFrame::VIDEO; 00807 result.videoFormat = itsGrabMode.getVal(); 00808 result.videoByteSwap = itsByteSwap.getVal(); 00809 result.dims = itsDims.getVal(); 00810 result.floatFlags = 0; 00811 00812 return result; 00813 } 00814 00815 // ###################################################################### 00816 GenericFrame V4L2grabber::readFrame() 00817 { 00818 const GenericFrame frame = 00819 itsStreamingMode.getVal() 00820 ? GenericFrame(this->grabRaw()) 00821 : GenericFrame(this->grabSingleRaw()); 00822 00823 if (itsListener.get() != 0) 00824 itsListener->onRawFrame(frame); 00825 00826 return frame; 00827 } 00828 00829 // ###################################################################### 00830 VideoFrame V4L2grabber::grabRaw() 00831 { 00832 byte* result = 0; int siz = 0; 00833 if (itsMmapBuf) // mmap interface 00834 { 00835 this->restartStream(); 00836 result = itsMmapBuf[itsCurrentFrame]; 00837 siz = itsMmapBufSize[itsCurrentFrame]; 00838 struct v4l2_buffer buf; 00839 memset(&buf, 0, sizeof(buf)); 00840 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00841 buf.memory = V4L2_MEMORY_MMAP; 00842 buf.index = itsCurrentFrame; 00843 00844 // are we already grabbing buffer 'itsCurrentFrame'? otherwise, 00845 // start the grab now: 00846 if (itsGrabbing[itsCurrentFrame] == false) 00847 { 00848 if (ioctl_nointr(itsFd, VIDIOC_QBUF, &buf)) 00849 IDPLFATAL("VIDIOC_QBUF (frame %d)", itsCurrentFrame); 00850 itsGrabbing[itsCurrentFrame] = true; 00851 } 00852 00853 // wait until buffer 'itsCurrentFrame' has been fully captured: 00854 if (ioctl_nointr(itsFd, VIDIOC_DQBUF, &buf) == -1) 00855 IDPLFATAL("VIDIOC_DQBUF (frame %d)", itsCurrentFrame); 00856 itsGrabbing[itsCurrentFrame] = false; 00857 00858 // get ready for capture of that frame again (for later): 00859 itsGrabbing[itsCurrentFrame] = true; 00860 if (ioctl_nointr(itsFd, VIDIOC_QBUF, &buf) < 0) 00861 IDPLFATAL("VIDIOC_QBUF (frame %d)", itsCurrentFrame); 00862 } 00863 else // read() interface 00864 { 00865 ASSERT(itsReadBuf.initialized()); 00866 const ssize_t nbytes = 00867 read(itsFd, itsReadBuf.getArrayPtr(), itsReadBuf.getSize()); 00868 if (nbytes < 0) IDPLFATAL("read() failed (frame %d)", itsCurrentFrame); 00869 IDLDEBUG("Got %zd bytes", nbytes); 00870 result = itsReadBuf.getArrayPtr(); 00871 siz = nbytes; 00872 } 00873 00874 // switch to another frame: 00875 ++itsCurrentFrame; 00876 if (itsCurrentFrame >= itsNbuf.getVal()) itsCurrentFrame = 0; 00877 00878 // return pointer to last-grabbed frame. You have a bit of time to 00879 // get a hold of the data but beware that an order to capture that 00880 // buffer has already been issued, so as soon as the grabber gets to 00881 // it, it will overwrite this buffer with a new frame: 00882 ASSERT(result != 0); 00883 VideoFrame frame(result, siz, itsDims.getVal(), 00884 itsGrabMode.getVal(), itsByteSwap.getVal(), 00885 /* strictLength = */ false); 00886 return frame; 00887 } 00888 00889 // ###################################################################### 00890 VideoFrame V4L2grabber::grabSingleRaw() 00891 { 00892 byte* result = 0; int siz = 0; 00893 if (itsMmapBuf) // mmap interface 00894 { 00895 itsCurrentFrame = 0; 00896 result = itsMmapBuf[itsCurrentFrame]; 00897 siz = itsMmapBufSize[itsCurrentFrame]; 00898 struct v4l2_buffer buf; 00899 memset(&buf, 0, sizeof(buf)); 00900 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00901 buf.memory = V4L2_MEMORY_MMAP; 00902 buf.index = itsCurrentFrame; 00903 00904 // are we already grabbing buffer 'itsCurrentFrame'? otherwise, 00905 // start the grab now: 00906 if (itsGrabbing[itsCurrentFrame] == false) 00907 { 00908 if (ioctl_nointr(itsFd, VIDIOC_QBUF, &buf)) 00909 IDPLFATAL("VIDIOC_QBUF (frame %d)", itsCurrentFrame); 00910 itsGrabbing[itsCurrentFrame] = true; 00911 } 00912 00913 // wait until buffer 'itsCurrentFrame' has been fully captured: 00914 if (ioctl_nointr(itsFd, VIDIOC_DQBUF, &buf) == -1) 00915 IDPLFATAL("VIDIOC_DQBUF (frame %d)", itsCurrentFrame); 00916 itsGrabbing[itsCurrentFrame] = false; 00917 } 00918 else // read() interface 00919 { 00920 ASSERT(itsReadBuf.initialized()); 00921 const ssize_t nbytes = 00922 read(itsFd, itsReadBuf.getArrayPtr(), itsReadBuf.getSize()); 00923 if (nbytes < 0) IDPLFATAL("read() failed (frame %d)", itsCurrentFrame); 00924 IDLDEBUG("Got %zd bytes", nbytes); 00925 result = itsReadBuf.getArrayPtr(); 00926 siz = nbytes; 00927 } 00928 00929 // return grabbed & converted frame: 00930 VideoFrame frame(result, siz, itsDims.getVal(), 00931 itsGrabMode.getVal(), itsByteSwap.getVal(), 00932 /* strictLength = */ false); 00933 return frame; 00934 } 00935 00936 // ###################################################################### 00937 void V4L2grabber::restartStream() 00938 { 00939 if (itsStreamingMode.getVal() 00940 && itsMmapBuf 00941 && !itsStreamStarted) 00942 { 00943 struct v4l2_buffer buf; 00944 memset(&buf, 0, sizeof(buf)); 00945 00946 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00947 buf.memory = V4L2_MEMORY_MMAP; 00948 00949 for (int i = 0; i < itsNbuf.getVal(); ++i) 00950 if (itsGrabbing[i] == true) 00951 { 00952 // are we already grabbing buffer i? if so, wait for grab 00953 // to finished if we've requested resync (if we don't do 00954 // this, then the first few frames after a restart will be 00955 // wrong -- they'll come too quickly and they'll be too old, 00956 // since the v4l driver doesn't grab new frames into buffers 00957 // until the old frames have been retrieved): 00958 buf.index = i; 00959 if (ioctl_nointr(itsFd, VIDIOC_DQBUF, &buf) == -1) 00960 IDPLFATAL("VIDIOC_DQBUF (frame %d)", i); 00961 itsGrabbing[i] = false; 00962 } 00963 00964 for (int i = 0; i < itsNbuf.getVal(); ++i) 00965 { 00966 // now start a fresh grab for buffer i: 00967 buf.index = i; 00968 if (ioctl_nointr(itsFd, VIDIOC_QBUF, &buf)) 00969 IDPLFATAL("VIDIOC_QBUF (frame %d)", i); 00970 itsGrabbing[i] = true; 00971 } 00972 00973 // tell grabber to stream: 00974 enum v4l2_buf_type typ = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00975 if (ioctl_nointr(itsFd, VIDIOC_STREAMON, &typ)) 00976 PLFATAL("VIDIOC_STREAMON"); 00977 00978 itsCurrentFrame = 0; 00979 itsStreamStarted = true; 00980 } 00981 } 00982 00983 #endif // HAVE_LINUX_VIDEODEV2_H 00984 00985 // ###################################################################### 00986 /* So things look consistent in everyone's emacs... */ 00987 /* Local Variables: */ 00988 /* indent-tabs-mode: nil */ 00989 /* End: */