// video4linux.cc // robert - programme du robot 2005. {{{ // // Copyright (C) 2005 Nicolas Schodet // Modified by Olivier Gaillard // // Robot APB Team/Efrei 2005. // Web: http://assos.efrei.fr/robot/ // Email: robot AT efrei DOT fr // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // }}} #include "video4linux.hh" #include "utils/errno_exception.hh" #include "utils/fd_set.hh" #include #include #include #include #include /// Constructeur. Video4Linux::Video4Linux (const char *dev, int width, int height, Image::PixelFormat pixelFormat, int brightness /*58000*/) : width_ (width), height_ (height), fd_ (-1), pixelFormat_ (pixelFormat), rgb_ (pixelFormat == Image::rgb || pixelFormat == Image::bgr), brightness_ (brightness), contrast_ (32768) { if (pixelFormat == Image::hsi) throw std::invalid_argument ("hsi not supported by Video4Linux"); open (dev); } /// Destructeur. Video4Linux::~Video4Linux (void) { // Munmap. munmap (map_, bufSize_); close (); } /// Lit une image, lance une exception en cas d'erreur. void Video4Linux::read (uint8_t *buf, unsigned size) { static const unsigned nbPixels = 3; if (buf && size != width_ * height_ * nbPixels) throw std::invalid_argument ("Video4Linux::read"); static const unsigned widthHeight = width_*height_; static const unsigned widthHeightDiv4 = widthHeight/4; // Capture. ioctl (fd_, VIDIOCMCAPTURE, &mmap_); // Sync. ioctl (fd_, VIDIOCSYNC, &mmap_.frame); // Copy. if (buf) { unsigned char *srcY = reinterpret_cast (map_); unsigned char *srcY2 = srcY + width_; unsigned char *srcU = srcY + widthHeight; unsigned char *srcV = srcU + widthHeightDiv4; unsigned char *dst = reinterpret_cast (buf); unsigned char *dst2 = dst + width_*3; switch (pixelFormat_) { case Image::rgb: memcpy (buf, map_, size); break; case Image::yuv: for (unsigned i = 0; i < height_/2; ++i) { for (unsigned j = 0; j < width_/2; ++j) { *dst++ = *srcY++; *dst2++ = *srcY2++; *dst++ = *srcU; *dst2++ = *srcU; *dst++ = *srcV; *dst2++ = *srcV; *dst++ = *srcY++; *dst2++ = *srcY2++; *dst++ = *srcU; *dst2++ = *srcU; *dst++ = *srcV; *dst2++ = *srcV; srcU++; srcV++; } dst += width_*3; dst2 += width_*3; srcY += width_; srcY2 += width_; } break; case Image::bgr: case Image::yuv422: case Image::hsi: memcpy (buf, map_, size); break; } } } /// Active/désactive la calibration automatique de la luminosité /*void Video4Linux::setAdaptive (int a) { if (fd_ != -1) { if (ioctl(fd_, VIDIOCQCSADAPTIVE, &a) != 0) throw errno_exception ("dev ", errno); } }*/ /// Calibre la caméra. void Video4Linux::calibrate (void) { for (int i = 0; i < 30; ++i) read (0, 0); } /// Attend qu'une image soit disponible, retourne true si oui. bool Video4Linux::wait (int timeout/*-1*/) { FdSet fds; fds.set (fd_); return fds.wait (timeout); } /// Ouvre le périphérique. void Video4Linux::open (const char *dev) { if (fd_ != -1) close (); fd_ = ::open (dev, O_RDWR); if (fd_ == -1) throw errno_exception (dev, errno); // Récupères les infos sur la camera. video_window win; win.width = width_; win.height = height_; ioctl (fd_, VIDIOCSWIN, &win); ioctl (fd_, VIDIOCGWIN, &win); width_ = win.width; height_ = win.height; // Initialisation mmap. video_mbuf mbuf; ioctl (fd_, VIDIOCGMBUF, &mbuf); bufSize_ = mbuf.size; // On récupère les cap. video_capability cap; if (ioctl (fd_, VIDIOCGCAP, &cap) == -1) throw errno_exception (dev, errno); // On veut du RGB24. video_picture pic; ioctl (fd_, VIDIOCGPICT, &pic); pic.palette = VIDEO_PALETTE_YUV420P; bpp_ = 1.5; pic.brightness = brightness_; pic.contrast = contrast_; ioctl (fd_, VIDIOCSPICT, &pic); // Channel. video_channel chn; ioctl (fd_, VIDIOCGCHAN, &chn); chn.channel = 1; chn.norm = VIDEO_MODE_NTSC; ioctl (fd_, VIDIOCSCHAN, &chn); // Mmap. map_ = mmap (0, bufSize_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); // Init du video_mmap mmap_.format = VIDEO_PALETTE_YUV420P; mmap_.frame = 0; mmap_.width = width_; mmap_.height = height_; } /// Ferme le périphérique. void Video4Linux::close (void) { ::close (fd_); }