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_VIDEODEV_H
00039
00040 #include "Devices/V4Lgrabber.H"
00041
00042 #include "Component/OptionManager.H"
00043 #include "Devices/DeviceOpts.H"
00044 #include "Image/ColorOps.H"
00045 #include "Raster/GenericFrame.H"
00046 #include "Util/Assert.H"
00047 #include "Util/log.H"
00048 #include "Video/VideoFrame.H"
00049
00050 #include <cerrno>
00051 #include <fcntl.h>
00052 #include <sys/ioctl.h>
00053 #include <sys/mman.h>
00054 #include <sys/stat.h>
00055 #include <unistd.h>
00056
00057
00058 #define MYLOGID itsFd
00059
00060 namespace
00061 {
00062
00063 struct V4LPalette
00064 {
00065 int pal;
00066 int depth;
00067 const char* name;
00068 VideoFormat vidformat;
00069 };
00070
00071
00072
00073 struct V4LPalette palettes[12] =
00074 {
00075 { VIDEO_PALETTE_RGB24, 24, "bgr24", VIDFMT_RGB24 },
00076 { VIDEO_PALETTE_RGB24 | 0x80, 24, "rgb24", VIDFMT_RGB24 },
00077 { VIDEO_PALETTE_RGB32, 32, "bgr32", VIDFMT_RGB32 },
00078 { VIDEO_PALETTE_RGB32 | 0x80, 32, "rgb32", VIDFMT_RGB32 },
00079 { VIDEO_PALETTE_YUYV, 24, "yuyv 16bpp", VIDFMT_YUYV },
00080 { VIDEO_PALETTE_YUV422, 24, "yuv422 16bpp", VIDFMT_YUV422 },
00081 { VIDEO_PALETTE_RGB565, 16, "rgb565", VIDFMT_RGB565 },
00082 { VIDEO_PALETTE_RGB555, 15, "rgb555", VIDFMT_RGB555 },
00083 { VIDEO_PALETTE_YUV420, 24, "yuv420 12bpp", VIDFMT_YUV420 },
00084 { VIDEO_PALETTE_YUV420P, 24, "yuv420 planar 12bpp", VIDFMT_YUV420P },
00085 { VIDEO_PALETTE_GREY, 8, "grayscale 8bpp", VIDFMT_GREY },
00086 { -1, -1, 0, VideoFormat(-1) }
00087 };
00088
00089 int ioctl_nointr(int d, int req, void* mem)
00090 {
00091 int result = 0;
00092 do { result = ioctl(d, req, mem); }
00093 while ((result < 0) && (errno == EINTR));
00094 return result;
00095 }
00096 }
00097
00098
00099 V4Lgrabber::V4Lgrabber(OptionManager& mgr, const std::string& descrName,
00100 const std::string& tagName,
00101 const ParamFlag flags) :
00102 FrameIstream(mgr, descrName, tagName),
00103
00104
00105
00106
00107
00108 itsDevName(&OPT_FrameGrabberDevice, this, "/dev/video0", flags),
00109 itsChannel(&OPT_FrameGrabberChannel, this, 1, flags),
00110 itsDims(&OPT_FrameGrabberDims, this, Dims(320, 240), flags),
00111 itsGrabMode(&OPT_FrameGrabberMode, this, VIDFMT_RGB24, flags),
00112 itsByteSwap(&OPT_FrameGrabberByteSwap, this, true, flags),
00113 itsBrightness(&OPT_FrameGrabberBrightness, this, 32768, flags | ALLOW_ONLINE_CHANGES),
00114 itsHue(&OPT_FrameGrabberHue, this, 32768, flags | ALLOW_ONLINE_CHANGES),
00115 itsColour(&OPT_FrameGrabberColour, this, 32768, flags | ALLOW_ONLINE_CHANGES),
00116 itsContrast(&OPT_FrameGrabberContrast, this, 32768, flags | ALLOW_ONLINE_CHANGES),
00117 itsWhiteness(&OPT_FrameGrabberWhiteness, this, 32768, flags | ALLOW_ONLINE_CHANGES),
00118 itsStreamingMode(&OPT_FrameGrabberStreaming, this),
00119 itsFd(-1),
00120 itsMmapBuf(NULL),
00121 itsReadBuf(),
00122 itsTotalBufSize(0),
00123 itsNumBufFrames(0),
00124 itsCurrentFrame(0),
00125 itsGrabbing(NULL),
00126 itsFrameTime(SimTime::ZERO()),
00127 itsListener(),
00128 itsStreamStarted(false)
00129 {
00130
00131 mgr.requestOptionAlias(&OPT_ALIAScamBttv);
00132 }
00133
00134
00135 void V4Lgrabber::start1()
00136 {
00137 itsFd = open(itsDevName.getVal().c_str(), O_RDWR | O_NONBLOCK);
00138 if (itsFd == -1) PLFATAL("Cannot open V4L device %s",
00139 itsDevName.getVal().c_str());
00140
00141
00142 struct video_capability vc;
00143 if (ioctl_nointr(itsFd, VIDIOCGCAP, &vc) < 0)
00144 IDPLFATAL("Cannot get V4L device capabilities");
00145 IDLINFO("FrameGrabber board name is: %s", vc.name);
00146 IDLINFO("maxwidth = %d, maxheight = %d", vc.maxwidth, vc.maxheight);
00147 if (itsDims.getVal().w() > vc.maxwidth ||
00148 itsDims.getVal().h() > vc.maxheight)
00149 IDLFATAL("Requested grab size %dx%d too large",
00150 itsDims.getVal().w(), itsDims.getVal().h());
00151
00152
00153 struct video_channel vch;
00154 vch.channel = itsChannel.getVal();
00155 if (ioctl_nointr(itsFd, VIDIOCGCHAN, &vch) < 0)
00156 IDPLERROR("Cannot get V4L device channel information");
00157 vch.norm = VIDEO_MODE_NTSC;
00158 vch.type = VIDEO_TYPE_CAMERA;
00159 IDLINFO("Channel %d is '%s' [norm %d]",
00160 vch.channel, vch.name, vch.norm);
00161 if (ioctl_nointr(itsFd, VIDIOCSCHAN, &vch) < 0)
00162 IDPLERROR("Cannot set V4L device channel information");
00163
00164 switch (vch.norm)
00165 {
00166 case VIDEO_MODE_PAL:
00167 itsFrameTime = SimTime::HERTZ(25.0);
00168 break;
00169
00170 case VIDEO_MODE_NTSC:
00171 itsFrameTime = SimTime::HERTZ(29.97);
00172 break;
00173
00174 case VIDEO_MODE_SECAM:
00175 itsFrameTime = SimTime::HERTZ(25.0);
00176 break;
00177
00178 default:
00179 itsFrameTime = SimTime::ZERO();
00180 break;
00181 }
00182
00183
00184 struct video_mbuf vmb;
00185 if (ioctl_nointr(itsFd, VIDIOCGMBUF, &vmb) < 0)
00186 IDPLFATAL("Cannot get V4L device buffer");
00187
00188 IDLINFO("video Mbuf: 0x%x bytes, %d frames", vmb.size, vmb.frames);
00189 for (int i = 0; i < vmb.frames; ++i)
00190 IDLINFO("buffer offset[%d] = %d", i, vmb.offsets[i]);
00191
00192
00193 struct video_picture vp;
00194 if (ioctl_nointr(itsFd, VIDIOCGPICT, &vp) != 0)
00195 IDPLFATAL("ioctl(VIDIOCSPICT) get picture properties failed");
00196
00197
00198 switch (itsGrabMode.getVal()) {
00199 case VIDFMT_GREY: itsVmmInfo.format = VIDEO_PALETTE_GREY; break;
00200 case VIDFMT_RAW: itsVmmInfo.format = VIDEO_PALETTE_RAW; break;
00201 case VIDFMT_RGB555: itsVmmInfo.format = VIDEO_PALETTE_RGB555; break;
00202 case VIDFMT_RGB565: itsVmmInfo.format = VIDEO_PALETTE_RGB565; break;
00203 case VIDFMT_RGB24: itsVmmInfo.format = VIDEO_PALETTE_RGB24; break;
00204 case VIDFMT_RGB32: itsVmmInfo.format = VIDEO_PALETTE_RGB32; break;
00205 case VIDFMT_YUYV: itsVmmInfo.format = VIDEO_PALETTE_YUYV; break;
00206 case VIDFMT_UYVY: itsVmmInfo.format = VIDEO_PALETTE_UYVY; break;
00207 case VIDFMT_YUV422: itsVmmInfo.format = VIDEO_PALETTE_YUV422; break;
00208 case VIDFMT_YUV411: itsVmmInfo.format = VIDEO_PALETTE_YUV411; break;
00209 case VIDFMT_YUV420: itsVmmInfo.format = VIDEO_PALETTE_YUV420; break;
00210 case VIDFMT_YUV422P: itsVmmInfo.format = VIDEO_PALETTE_YUV422P; break;
00211 case VIDFMT_YUV411P: itsVmmInfo.format = VIDEO_PALETTE_YUV411P; break;
00212 case VIDFMT_YUV420P: itsVmmInfo.format = VIDEO_PALETTE_YUV420P; break;
00213 case VIDFMT_YUV410P: itsVmmInfo.format = VIDEO_PALETTE_YUV410P; break;
00214 case VIDFMT_AUTO:
00215
00216 struct V4LPalette* pal;
00217 LINFO("Probing for supported palettes:");
00218
00219 #define CHECK_PALETTE(p) \
00220 { \
00221 vp.palette = p; \
00222 vp.depth = 32; \
00223 ioctl_nointr(itsFd, VIDIOCSPICT, &vp);\
00224 ioctl_nointr(itsFd, VIDIOCGPICT, &vp);\
00225 if (vp.palette == p) \
00226 LINFO(" %-22s supported", #p); \
00227 else \
00228 LINFO(" %-22s NOT supported", #p); \
00229 }
00230
00231 CHECK_PALETTE(VIDEO_PALETTE_GREY);
00232 CHECK_PALETTE(VIDEO_PALETTE_HI240);
00233 CHECK_PALETTE(VIDEO_PALETTE_RGB565);
00234 CHECK_PALETTE(VIDEO_PALETTE_RGB24);
00235 CHECK_PALETTE(VIDEO_PALETTE_RGB32);
00236 CHECK_PALETTE(VIDEO_PALETTE_RGB555);
00237 CHECK_PALETTE(VIDEO_PALETTE_YUV422);
00238 CHECK_PALETTE(VIDEO_PALETTE_YUYV);
00239 CHECK_PALETTE(VIDEO_PALETTE_UYVY);
00240 CHECK_PALETTE(VIDEO_PALETTE_YUV420);
00241 CHECK_PALETTE(VIDEO_PALETTE_YUV411);
00242 CHECK_PALETTE(VIDEO_PALETTE_RAW);
00243 CHECK_PALETTE(VIDEO_PALETTE_YUV422P);
00244 CHECK_PALETTE(VIDEO_PALETTE_YUV411P);
00245 CHECK_PALETTE(VIDEO_PALETTE_YUV420P);
00246 CHECK_PALETTE(VIDEO_PALETTE_YUV410P);
00247
00248 #undef CHECK_PALETTE
00249
00250
00251 for (pal = &palettes[0]; pal->pal >= 0; ++pal)
00252 {
00253 vp.palette = pal->pal;
00254 vp.depth = pal->depth;
00255 ioctl_nointr(itsFd, VIDIOCSPICT, &vp);
00256 ioctl_nointr(itsFd, VIDIOCGPICT, &vp);
00257 if (vp.palette == pal->pal)
00258 {
00259 LINFO(" Using palette \"%s\" with depth %u",
00260 pal->name, vp.depth);
00261
00262 itsGrabMode.setVal(pal->vidformat);
00263 itsVmmInfo.format = vp.palette;
00264 break;
00265 }
00266 else
00267 LINFO(" Palette \"%s\" not supported", pal->name);
00268 }
00269
00270 if (pal->pal < 0)
00271 IDLFATAL("Auto palette selection failed - try setting manually.");
00272 break;
00273 default:
00274 LFATAL("Unsupported grab mode");
00275 }
00276
00277
00278 itsNumBufFrames = vmb.frames;
00279 itsTotalBufSize = vmb.size;
00280 itsCurrentFrame = 0;
00281 itsGrabbing = new bool[itsNumBufFrames];
00282 for (int i = 0; i < itsNumBufFrames; ++i) itsGrabbing[i] = false;
00283 itsVmmInfo.width = itsDims.getVal().w();
00284 itsVmmInfo.height = itsDims.getVal().h();
00285 itsVmmInfo.frame = 0;
00286
00287
00288 if (ioctl_nointr(itsFd, VIDIOCGMBUF, &vmb) != -1)
00289 {
00290 IDLINFO("Using mmap'ed image capture");
00291
00292 itsMmapBuf =
00293 static_cast<byte*>(mmap((void*)0, vmb.size,
00294 PROT_READ|PROT_WRITE,
00295 MAP_SHARED, itsFd, 0));
00296
00297 if (itsMmapBuf == MAP_FAILED)
00298 IDPLFATAL("mmap failed");
00299
00300 itsReadBuf = Image<byte>();
00301 }
00302 else
00303 {
00304 IDLINFO("Using read() image capture");
00305 itsMmapBuf = NULL;
00306
00307 itsReadBuf = Image<byte>(getFrameSize(itsGrabMode.getVal(),
00308 itsDims.getVal()),
00309 1, NO_INIT);
00310 }
00311
00312
00313 vp.brightness = itsBrightness.getVal();
00314 vp.hue = itsHue.getVal();
00315 vp.colour = itsColour.getVal();
00316 vp.contrast = itsContrast.getVal();
00317 vp.whiteness = itsWhiteness.getVal();
00318 vp.palette = itsVmmInfo.format;
00319 LINFO("bright=%u hue=%u color=%u contrast=%u white=%u depth=%u palette=%u",
00320 vp.brightness, vp.hue, vp.colour, vp.contrast,
00321 vp.whiteness, vp.depth, vp.palette);
00322 if (ioctl_nointr(itsFd, VIDIOCSPICT, &vp) != 0)
00323 IDPLERROR("ioctl(VIDIOCSPICT) set picture properties failed");
00324 }
00325
00326
00327 void V4Lgrabber::stop2()
00328 {
00329 if (itsMmapBuf) { munmap(itsMmapBuf, itsTotalBufSize); itsMmapBuf = NULL; }
00330 if (itsFd >= 0) { close(itsFd); itsFd = -1; }
00331 if (itsGrabbing) { delete [] itsGrabbing; itsGrabbing = NULL; }
00332 itsReadBuf = Image<byte>();
00333 itsStreamStarted = false;
00334 }
00335
00336
00337 V4Lgrabber::~V4Lgrabber()
00338 { }
00339
00340
00341 void V4Lgrabber::setListener(rutz::shared_ptr<FrameListener> listener)
00342 {
00343 itsListener = listener;
00344 }
00345
00346
00347 void V4Lgrabber::startStream()
00348 {
00349
00350
00351 itsStreamStarted = false;
00352
00353 this->restartStream();
00354 }
00355
00356
00357 SimTime V4Lgrabber::getNaturalFrameTime() const
00358 {
00359 return itsFrameTime;
00360 }
00361
00362
00363 GenericFrameSpec V4Lgrabber::peekFrameSpec()
00364 {
00365 GenericFrameSpec result;
00366
00367 result.nativeType = GenericFrame::VIDEO;
00368 result.videoFormat = itsGrabMode.getVal();
00369 result.videoByteSwap = itsByteSwap.getVal();
00370 result.dims = itsDims.getVal();
00371 result.floatFlags = 0;
00372
00373 return result;
00374 }
00375
00376
00377 GenericFrame V4Lgrabber::readFrame()
00378 {
00379 const GenericFrame frame =
00380 itsStreamingMode.getVal()
00381 ? GenericFrame(this->grabRaw())
00382 : GenericFrame(this->grabSingleRaw());
00383
00384 if (itsListener.get() != 0)
00385 itsListener->onRawFrame(frame);
00386
00387 return frame;
00388 }
00389
00390
00391 VideoFrame V4Lgrabber::grabRaw()
00392 {
00393 byte* result = 0;
00394 if (itsMmapBuf)
00395 {
00396 this->restartStream();
00397
00398 result = itsMmapBuf + itsCurrentFrame * (itsTotalBufSize / itsNumBufFrames);
00399
00400
00401
00402 if (itsGrabbing[itsCurrentFrame] == false)
00403 {
00404 itsVmmInfo.frame = itsCurrentFrame;
00405 itsGrabbing[itsCurrentFrame] = true;
00406 if (ioctl_nointr(itsFd, VIDIOCMCAPTURE, &itsVmmInfo) < 0)
00407 IDPLFATAL("VIDIOCMCAPTURE (frame %d)", itsCurrentFrame);
00408 }
00409
00410
00411 if (ioctl_nointr(itsFd, VIDIOCSYNC, &itsCurrentFrame) < 0)
00412 IDPLFATAL("VIDIOCSYNC (frame %d)", itsCurrentFrame);
00413 itsGrabbing[itsCurrentFrame] = false;
00414
00415
00416 itsVmmInfo.frame = itsCurrentFrame;
00417 itsGrabbing[itsCurrentFrame] = true;
00418 if (ioctl_nointr(itsFd, VIDIOCMCAPTURE, &itsVmmInfo) < 0)
00419 IDPLFATAL("VIDIOCMCAPTURE (frame %d)", itsCurrentFrame);
00420 }
00421 else
00422 {
00423 ASSERT(itsReadBuf.initialized());
00424 const ssize_t nbytes =
00425 read(itsFd, itsReadBuf.getArrayPtr(), itsReadBuf.getSize());
00426 if (nbytes < 0)
00427 IDPLFATAL("read() failed");
00428 PLDEBUG("got %zd bytes", nbytes);
00429 result = itsReadBuf.getArrayPtr();
00430 }
00431
00432
00433 ++itsCurrentFrame;
00434 if (itsCurrentFrame >= itsNumBufFrames) itsCurrentFrame = 0;
00435
00436
00437
00438
00439
00440 ASSERT(result != 0);
00441 VideoFrame frame(result, (itsTotalBufSize / itsNumBufFrames),
00442 itsDims.getVal(),
00443 itsGrabMode.getVal(), itsByteSwap.getVal(),
00444 false);
00445
00446 return frame;
00447 }
00448
00449
00450 void V4Lgrabber::paramChanged(ModelParamBase* const param,
00451 const bool valueChanged,
00452 ParamClient::ChangeStatus* status)
00453 {
00454
00455 FrameIstream::paramChanged(param, valueChanged, status);
00456
00457
00458 if (valueChanged)
00459 {
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 }
00477
00478
00479
00480 }
00481
00482
00483
00484 VideoFrame V4Lgrabber::grabSingleRaw()
00485 {
00486 byte* result = 0;
00487 if (itsMmapBuf)
00488 {
00489 itsCurrentFrame = 0;
00490 result = itsMmapBuf;
00491
00492
00493
00494 if (itsGrabbing[itsCurrentFrame] == false)
00495 {
00496 itsVmmInfo.frame = itsCurrentFrame;
00497 itsGrabbing[itsCurrentFrame] = true;
00498 if (ioctl_nointr(itsFd, VIDIOCMCAPTURE, &itsVmmInfo) < 0)
00499 IDPLFATAL("VIDIOCMCAPTURE (frame %d)", itsCurrentFrame);
00500 }
00501
00502
00503 if (ioctl_nointr(itsFd, VIDIOCSYNC, &itsCurrentFrame) < 0)
00504 IDPLFATAL("VIDIOCSYNC (frame %d)", itsCurrentFrame);
00505 itsGrabbing[itsCurrentFrame] = false;
00506 }
00507 else
00508 {
00509 ASSERT(itsReadBuf.initialized());
00510 const ssize_t nbytes =
00511 read(itsFd, itsReadBuf.getArrayPtr(), itsReadBuf.getSize());
00512 if (nbytes < 0)
00513 IDPLFATAL("read() failed (frame %d)", itsCurrentFrame);
00514 PLDEBUG("got %zd bytes", nbytes);
00515 result = itsReadBuf.getArrayPtr();
00516 }
00517
00518
00519 VideoFrame frame(result, (itsTotalBufSize / itsNumBufFrames),
00520 itsDims.getVal(),
00521 itsGrabMode.getVal(), itsByteSwap.getVal(),
00522 false);
00523
00524 return frame;
00525 }
00526
00527
00528 void V4Lgrabber::restartStream()
00529 {
00530 if (itsStreamingMode.getVal()
00531 && itsMmapBuf
00532 && !itsStreamStarted)
00533 {
00534 for (int i = 0; i < itsNumBufFrames; ++i)
00535 {
00536
00537
00538
00539
00540
00541
00542 if (itsGrabbing[i] == true)
00543 {
00544 if (ioctl_nointr(itsFd, VIDIOCSYNC, &i) < 0)
00545 IDPLFATAL("VIDIOCSYNC (frame %d)", i);
00546 LINFO("flushed buffer %d", i);
00547 itsGrabbing[i] = false;
00548 }
00549 }
00550
00551 for (int i = 0; i < itsNumBufFrames; ++i)
00552 {
00553
00554 itsVmmInfo.frame = i; itsGrabbing[i] = true;
00555 if (ioctl_nointr(itsFd, VIDIOCMCAPTURE, &itsVmmInfo) < 0)
00556 IDPLFATAL("VIDIOCMCAPTURE (frame %d)", i);
00557 }
00558
00559 itsCurrentFrame = 0;
00560
00561 itsStreamStarted = true;
00562 }
00563 }
00564
00565 #endif // HAVE_LINUX_VIDEODEV_H
00566
00567
00568
00569
00570
00571