00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifdef HAVE_LINUX_VIDEODEV2_H
00039
00040 #include "Devices/V4L2grabber.H"
00041
00042 #include "Component/OptionManager.H"
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
00060 #define MYLOGID itsFd
00061
00062 namespace
00063 {
00064 struct V4L2Palette
00065 {
00066 VideoFormat vidformat;
00067 uint32 v4l2format;
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
00092
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
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
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
00122 struct ControlMapping {
00123 uint v4l2;
00124 const ModelOptionDef *opt;
00125 };
00126
00127
00128 ControlMapping controlmapping[] = {
00129
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 },
00156 { V4L2_CID_COLOR_KILLER, NULL },
00157
00158 #ifdef V4L2_CID_COLORFX
00159 { V4L2_CID_COLORFX, NULL },
00160 #endif
00161
00162 #ifdef V4L2_CID_AUTOBRIGHTNESS
00163 { V4L2_CID_AUTOBRIGHTNESS, NULL },
00164 #endif
00165
00166 #ifdef V4L2_CID_BAND_STOP_FILTER
00167 { V4L2_CID_BAND_STOP_FILTER, NULL },
00168 #endif
00169
00170
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 },
00175 { V4L2_CID_TILT_RELATIVE, NULL },
00176 { V4L2_CID_PAN_RESET, NULL },
00177 { V4L2_CID_TILT_RESET, NULL },
00178 { V4L2_CID_PAN_ABSOLUTE, NULL },
00179 { V4L2_CID_TILT_ABSOLUTE, NULL },
00180 { V4L2_CID_FOCUS_ABSOLUTE, &OPT_FrameGrabberFocus },
00181 { V4L2_CID_FOCUS_RELATIVE, NULL },
00182 { V4L2_CID_FOCUS_AUTO, &OPT_FrameGrabberFocusAuto },
00183 { V4L2_CID_ZOOM_ABSOLUTE, &OPT_FrameGrabberZoom },
00184 { V4L2_CID_ZOOM_RELATIVE, NULL },
00185 { V4L2_CID_ZOOM_CONTINUOUS, NULL },
00186 { V4L2_CID_PRIVACY, NULL },
00187
00188 { -1, NULL }
00189 };
00190
00191 }
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
00198
00199
00200
00201
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
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
00243
00244
00245
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
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
00258
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
00287
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
00294 addControl(ctrl);
00295
00296
00297 ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
00298 } while (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0);
00299
00300
00301
00302
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);
00327
00328 for (ctrl.id = V4L2_CID_PRIVATE_BASE; ; ++ctrl.id)
00329 if (ioctl_nointr(itsFd, VIDIOC_QUERYCTRL, &ctrl) == 0)
00330 addControl(ctrl);
00331 else break;
00332 }
00333 }
00334
00335
00336 void V4L2grabber::addControl(const struct v4l2_queryctrl& ctrl)
00337 {
00338
00339
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
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
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);
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
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:
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
00466 IDLINFO("%s", info.c_str());
00467 for (size_t ii = 0; ii < infomenu.size(); ++ii) IDLINFO("%s", infomenu[ii].c_str());
00468
00469
00470
00471
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
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
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
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
00538
00539
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
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;
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
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
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
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
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
00648 itsReadBuf = Image<byte>();
00649 } else {
00650
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
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
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
00697 if (valueChanged)
00698 for (size_t i = 0; i < itsControls.size(); ++i) {
00699 int val = 0; const char *name = NULL;
00700
00701
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
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;
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;
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
00767 } else IDLDEBUG("Changed control --%s to value %d", name, val);
00768 } else {
00769
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
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
00789
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)
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
00845
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
00854 if (ioctl_nointr(itsFd, VIDIOC_DQBUF, &buf) == -1)
00855 IDPLFATAL("VIDIOC_DQBUF (frame %d)", itsCurrentFrame);
00856 itsGrabbing[itsCurrentFrame] = false;
00857
00858
00859 itsGrabbing[itsCurrentFrame] = true;
00860 if (ioctl_nointr(itsFd, VIDIOC_QBUF, &buf) < 0)
00861 IDPLFATAL("VIDIOC_QBUF (frame %d)", itsCurrentFrame);
00862 }
00863 else
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
00875 ++itsCurrentFrame;
00876 if (itsCurrentFrame >= itsNbuf.getVal()) itsCurrentFrame = 0;
00877
00878
00879
00880
00881
00882 ASSERT(result != 0);
00883 VideoFrame frame(result, siz, itsDims.getVal(),
00884 itsGrabMode.getVal(), itsByteSwap.getVal(),
00885 false);
00886 return frame;
00887 }
00888
00889
00890 VideoFrame V4L2grabber::grabSingleRaw()
00891 {
00892 byte* result = 0; int siz = 0;
00893 if (itsMmapBuf)
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
00905
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
00914 if (ioctl_nointr(itsFd, VIDIOC_DQBUF, &buf) == -1)
00915 IDPLFATAL("VIDIOC_DQBUF (frame %d)", itsCurrentFrame);
00916 itsGrabbing[itsCurrentFrame] = false;
00917 }
00918 else
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
00930 VideoFrame frame(result, siz, itsDims.getVal(),
00931 itsGrabMode.getVal(), itsByteSwap.getVal(),
00932 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
00953
00954
00955
00956
00957
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
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
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
00987
00988
00989