diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 2df80524..698d828f 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -20,797 +20,802 @@ static inline uint8_t clamp(int x) { - return (x<0) ? 0 : ((x>255) ? 255 : uint8_t(x)); + return (x<0) ? 0 : ((x>255) ? 255 : uint8_t(x)); } static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, uint8_t & b) { - // see: http://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion - int c = y - 16; - int d = u - 128; - int e = v - 128; + // see: http://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion + int c = y - 16; + int d = u - 128; + int e = v - 128; - r = clamp((298 * c + 409 * e + 128) >> 8); - g = clamp((298 * c - 100 * d - 208 * e + 128) >> 8); - b = clamp((298 * c + 516 * d + 128) >> 8); + r = clamp((298 * c + 409 * e + 128) >> 8); + g = clamp((298 * c - 100 * d - 208 * e + 128) >> 8); + b = clamp((298 * c + 516 * d + 128) >> 8); } V4L2Grabber::V4L2Grabber(const std::string & device, - int input, - VideoStandard videoStandard, - PixelFormat pixelFormat, - int width, - int height, - int frameDecimation, - int horizontalPixelDecimation, - int verticalPixelDecimation) : - _deviceName(device), - _ioMethod(IO_METHOD_MMAP), - _fileDescriptor(-1), - _buffers(), - _pixelFormat(pixelFormat), - _width(width), - _height(height), - _frameByteSize(-1), - _cropLeft(0), - _cropRight(0), - _cropTop(0), - _cropBottom(0), - _frameDecimation(std::max(1, frameDecimation)), - _horizontalPixelDecimation(std::max(1, horizontalPixelDecimation)), - _verticalPixelDecimation(std::max(1, verticalPixelDecimation)), - _noSignalCounterThreshold(50), - _noSignalThresholdColor(ColorRgb{0,0,0}), - _mode3D(VIDEO_2D), - _currentFrame(0), - _noSignalCounter(0), - _streamNotifier(nullptr) + int input, + VideoStandard videoStandard, + PixelFormat pixelFormat, + int width, + int height, + int frameDecimation, + int horizontalPixelDecimation, + int verticalPixelDecimation) : + _deviceName(device), + _ioMethod(IO_METHOD_MMAP), + _fileDescriptor(-1), + _buffers(), + _pixelFormat(pixelFormat), + _width(width), + _height(height), + _frameByteSize(-1), + _cropLeft(0), + _cropRight(0), + _cropTop(0), + _cropBottom(0), + _frameDecimation(std::max(1, frameDecimation)), + _horizontalPixelDecimation(std::max(1, horizontalPixelDecimation)), + _verticalPixelDecimation(std::max(1, verticalPixelDecimation)), + _noSignalCounterThreshold(50), + _noSignalThresholdColor(ColorRgb{0,0,0}), + _mode3D(VIDEO_2D), + _currentFrame(0), + _noSignalCounter(0), + _streamNotifier(nullptr) { - open_device(); - init_device(videoStandard, input); + open_device(); + init_device(videoStandard, input); } V4L2Grabber::~V4L2Grabber() { - // stop if the grabber was not stopped - stop(); - uninit_device(); - close_device(); + // stop if the grabber was not stopped + stop(); + uninit_device(); + close_device(); } void V4L2Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) { - _cropLeft = cropLeft; - _cropRight = cropRight; - _cropTop = cropTop; - _cropBottom = cropBottom; + _cropLeft = cropLeft; + _cropRight = cropRight; + _cropTop = cropTop; + _cropBottom = cropBottom; } void V4L2Grabber::set3D(VideoMode mode) { - _mode3D = mode; + _mode3D = mode; } void V4L2Grabber::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold) { - _noSignalThresholdColor.red = uint8_t(255*redSignalThreshold); - _noSignalThresholdColor.green = uint8_t(255*greenSignalThreshold); - _noSignalThresholdColor.blue = uint8_t(255*blueSignalThreshold); - _noSignalCounterThreshold = std::max(1, noSignalCounterThreshold); + _noSignalThresholdColor.red = uint8_t(255*redSignalThreshold); + _noSignalThresholdColor.green = uint8_t(255*greenSignalThreshold); + _noSignalThresholdColor.blue = uint8_t(255*blueSignalThreshold); + _noSignalCounterThreshold = std::max(1, noSignalCounterThreshold); - std::cout << "V4L2 grabber signal threshold set to: " << _noSignalThresholdColor << std::endl; + std::cout << "V4L2 grabber signal threshold set to: " << _noSignalThresholdColor << std::endl; } void V4L2Grabber::start() { - if (_streamNotifier != nullptr && !_streamNotifier->isEnabled()) - { - _streamNotifier->setEnabled(true); - start_capturing(); - std::cout << "V4L2 grabber started" << std::endl; - } + if (_streamNotifier != nullptr && !_streamNotifier->isEnabled()) + { + _streamNotifier->setEnabled(true); + start_capturing(); + std::cout << "V4L2 grabber started" << std::endl; + } } void V4L2Grabber::stop() { - if (_streamNotifier != nullptr && _streamNotifier->isEnabled()) - { - stop_capturing(); - _streamNotifier->setEnabled(false); - std::cout << "V4L2 grabber stopped" << std::endl; - } + if (_streamNotifier != nullptr && _streamNotifier->isEnabled()) + { + stop_capturing(); + _streamNotifier->setEnabled(false); + std::cout << "V4L2 grabber stopped" << std::endl; + } } void V4L2Grabber::open_device() { - struct stat st; + struct stat st; - if (-1 == stat(_deviceName.c_str(), &st)) - { - std::ostringstream oss; - oss << "Cannot identify '" << _deviceName << "'"; - throw_errno_exception(oss.str()); - } + if (-1 == stat(_deviceName.c_str(), &st)) + { + std::ostringstream oss; + oss << "Cannot identify '" << _deviceName << "'"; + throw_errno_exception(oss.str()); + } - if (!S_ISCHR(st.st_mode)) - { - std::ostringstream oss; - oss << "'" << _deviceName << "' is no device"; - throw_exception(oss.str()); - } + if (!S_ISCHR(st.st_mode)) + { + std::ostringstream oss; + oss << "'" << _deviceName << "' is no device"; + throw_exception(oss.str()); + } - _fileDescriptor = open(_deviceName.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0); + _fileDescriptor = open(_deviceName.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0); - if (-1 == _fileDescriptor) - { - std::ostringstream oss; - oss << "Cannot open '" << _deviceName << "'"; - throw_errno_exception(oss.str()); - } + if (-1 == _fileDescriptor) + { + std::ostringstream oss; + oss << "Cannot open '" << _deviceName << "'"; + throw_errno_exception(oss.str()); + } - // create the notifier for when a new frame is available - _streamNotifier = new QSocketNotifier(_fileDescriptor, QSocketNotifier::Read); - _streamNotifier->setEnabled(false); - connect(_streamNotifier, SIGNAL(activated(int)), this, SLOT(read_frame())); + // create the notifier for when a new frame is available + _streamNotifier = new QSocketNotifier(_fileDescriptor, QSocketNotifier::Read); + _streamNotifier->setEnabled(false); + connect(_streamNotifier, SIGNAL(activated(int)), this, SLOT(read_frame())); } void V4L2Grabber::close_device() { - if (-1 == close(_fileDescriptor)) - throw_errno_exception("close"); + if (-1 == close(_fileDescriptor)) + throw_errno_exception("close"); - _fileDescriptor = -1; + _fileDescriptor = -1; - if (_streamNotifier != nullptr) - { - delete _streamNotifier; - _streamNotifier = nullptr; - } + if (_streamNotifier != nullptr) + { + delete _streamNotifier; + _streamNotifier = nullptr; + } } void V4L2Grabber::init_read(unsigned int buffer_size) { - _buffers.resize(1); + _buffers.resize(1); - _buffers[0].length = buffer_size; - _buffers[0].start = malloc(buffer_size); + _buffers[0].length = buffer_size; + _buffers[0].start = malloc(buffer_size); - if (!_buffers[0].start) { - throw_exception("Out of memory"); - } + if (!_buffers[0].start) { + throw_exception("Out of memory"); + } } void V4L2Grabber::init_mmap() { - struct v4l2_requestbuffers req; + struct v4l2_requestbuffers req; - CLEAR(req); + CLEAR(req); - req.count = 4; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; - if (-1 == xioctl(VIDIOC_REQBUFS, &req)) { - if (EINVAL == errno) { - std::ostringstream oss; - oss << "'" << _deviceName << "' does not support memory mapping"; - throw_exception(oss.str()); - } else { - throw_errno_exception("VIDIOC_REQBUFS"); - } - } + if (-1 == xioctl(VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + std::ostringstream oss; + oss << "'" << _deviceName << "' does not support memory mapping"; + throw_exception(oss.str()); + } else { + throw_errno_exception("VIDIOC_REQBUFS"); + } + } - if (req.count < 2) { - std::ostringstream oss; - oss << "Insufficient buffer memory on " << _deviceName; - throw_exception(oss.str()); - } + if (req.count < 2) { + std::ostringstream oss; + oss << "Insufficient buffer memory on " << _deviceName; + throw_exception(oss.str()); + } - _buffers.resize(req.count); + _buffers.resize(req.count); - for (size_t n_buffers = 0; n_buffers < req.count; ++n_buffers) { - struct v4l2_buffer buf; + for (size_t n_buffers = 0; n_buffers < req.count; ++n_buffers) { + struct v4l2_buffer buf; - CLEAR(buf); + CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = n_buffers; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; - if (-1 == xioctl(VIDIOC_QUERYBUF, &buf)) - throw_errno_exception("VIDIOC_QUERYBUF"); + if (-1 == xioctl(VIDIOC_QUERYBUF, &buf)) + throw_errno_exception("VIDIOC_QUERYBUF"); - _buffers[n_buffers].length = buf.length; - _buffers[n_buffers].start = - mmap(NULL /* start anywhere */, - buf.length, - PROT_READ | PROT_WRITE /* required */, - MAP_SHARED /* recommended */, - _fileDescriptor, buf.m.offset); + _buffers[n_buffers].length = buf.length; + _buffers[n_buffers].start = + mmap(NULL /* start anywhere */, + buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, + _fileDescriptor, buf.m.offset); - if (MAP_FAILED == _buffers[n_buffers].start) - throw_errno_exception("mmap"); - } + if (MAP_FAILED == _buffers[n_buffers].start) + throw_errno_exception("mmap"); + } } void V4L2Grabber::init_userp(unsigned int buffer_size) { - struct v4l2_requestbuffers req; + struct v4l2_requestbuffers req; - CLEAR(req); + CLEAR(req); - req.count = 4; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_USERPTR; + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; - if (-1 == xioctl(VIDIOC_REQBUFS, &req)) { - if (EINVAL == errno) - { - std::ostringstream oss; - oss << "'" << _deviceName << "' does not support user pointer"; - throw_exception(oss.str()); - } else { - throw_errno_exception("VIDIOC_REQBUFS"); - } - } + if (-1 == xioctl(VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) + { + std::ostringstream oss; + oss << "'" << _deviceName << "' does not support user pointer"; + throw_exception(oss.str()); + } else { + throw_errno_exception("VIDIOC_REQBUFS"); + } + } - _buffers.resize(4); + _buffers.resize(4); - for (size_t n_buffers = 0; n_buffers < 4; ++n_buffers) { - _buffers[n_buffers].length = buffer_size; - _buffers[n_buffers].start = malloc(buffer_size); + for (size_t n_buffers = 0; n_buffers < 4; ++n_buffers) { + _buffers[n_buffers].length = buffer_size; + _buffers[n_buffers].start = malloc(buffer_size); - if (!_buffers[n_buffers].start) { - throw_exception("Out of memory"); - } - } + if (!_buffers[n_buffers].start) { + throw_exception("Out of memory"); + } + } } void V4L2Grabber::init_device(VideoStandard videoStandard, int input) { - struct v4l2_capability cap; - if (-1 == xioctl(VIDIOC_QUERYCAP, &cap)) - { - if (EINVAL == errno) { - std::ostringstream oss; - oss << "'" << _deviceName << "' is no V4L2 device"; - throw_exception(oss.str()); - } else { - throw_errno_exception("VIDIOC_QUERYCAP"); - } - } + struct v4l2_capability cap; + if (-1 == xioctl(VIDIOC_QUERYCAP, &cap)) + { + if (EINVAL == errno) { + std::ostringstream oss; + oss << "'" << _deviceName << "' is no V4L2 device"; + throw_exception(oss.str()); + } else { + throw_errno_exception("VIDIOC_QUERYCAP"); + } + } - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) - { - std::ostringstream oss; - oss << "'" << _deviceName << "' is no video capture device"; - throw_exception(oss.str()); - } + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) + { + std::ostringstream oss; + oss << "'" << _deviceName << "' is no video capture device"; + throw_exception(oss.str()); + } - switch (_ioMethod) { - case IO_METHOD_READ: - if (!(cap.capabilities & V4L2_CAP_READWRITE)) - { - std::ostringstream oss; - oss << "'" << _deviceName << "' does not support read i/o"; - throw_exception(oss.str()); - } - break; + switch (_ioMethod) { + case IO_METHOD_READ: + if (!(cap.capabilities & V4L2_CAP_READWRITE)) + { + std::ostringstream oss; + oss << "'" << _deviceName << "' does not support read i/o"; + throw_exception(oss.str()); + } + break; - case IO_METHOD_MMAP: - case IO_METHOD_USERPTR: - if (!(cap.capabilities & V4L2_CAP_STREAMING)) - { - std::ostringstream oss; - oss << "'" << _deviceName << "' does not support streaming i/o"; - throw_exception(oss.str()); - } - break; - } + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + if (!(cap.capabilities & V4L2_CAP_STREAMING)) + { + std::ostringstream oss; + oss << "'" << _deviceName << "' does not support streaming i/o"; + throw_exception(oss.str()); + } + break; + } - /* Select video input, video standard and tune here. */ + /* Select video input, video standard and tune here. */ - struct v4l2_cropcap cropcap; - CLEAR(cropcap); + struct v4l2_cropcap cropcap; + CLEAR(cropcap); - cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (0 == xioctl(VIDIOC_CROPCAP, &cropcap)) { - struct v4l2_crop crop; - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c = cropcap.defrect; /* reset to default */ + if (0 == xioctl(VIDIOC_CROPCAP, &cropcap)) { + struct v4l2_crop crop; + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = cropcap.defrect; /* reset to default */ - if (-1 == xioctl(VIDIOC_S_CROP, &crop)) { - switch (errno) { - case EINVAL: - /* Cropping not supported. */ - break; - default: - /* Errors ignored. */ - break; - } - } - } else { - /* Errors ignored. */ - } + if (-1 == xioctl(VIDIOC_S_CROP, &crop)) { + switch (errno) { + case EINVAL: + /* Cropping not supported. */ + break; + default: + /* Errors ignored. */ + break; + } + } + } else { + /* Errors ignored. */ + } - // set input if needed - if (input >= 0) - { - if (-1 == xioctl(VIDIOC_S_INPUT, &input)) - { - throw_errno_exception("VIDIOC_S_INPUT"); - } - } + // set input if needed + if (input >= 0) + { + if (-1 == xioctl(VIDIOC_S_INPUT, &input)) + { + throw_errno_exception("VIDIOC_S_INPUT"); + } + } - // set the video standard if needed - switch (videoStandard) - { - case VIDEOSTANDARD_PAL: - { - v4l2_std_id std_id = V4L2_STD_PAL; - if (-1 == xioctl(VIDIOC_S_STD, &std_id)) - { - throw_errno_exception("VIDIOC_S_STD"); - } - } - break; - case VIDEOSTANDARD_NTSC: - { - v4l2_std_id std_id = V4L2_STD_NTSC; - if (-1 == xioctl(VIDIOC_S_STD, &std_id)) - { - throw_errno_exception("VIDIOC_S_STD"); - } - } - break; - case VIDEOSTANDARD_NO_CHANGE: - default: - // No change to device settings - break; - } + // set the video standard if needed + switch (videoStandard) + { + case VIDEOSTANDARD_PAL: + { + v4l2_std_id std_id = V4L2_STD_PAL; + if (-1 == xioctl(VIDIOC_S_STD, &std_id)) + { + throw_errno_exception("VIDIOC_S_STD"); + } + } + break; + case VIDEOSTANDARD_NTSC: + { + v4l2_std_id std_id = V4L2_STD_NTSC; + if (-1 == xioctl(VIDIOC_S_STD, &std_id)) + { + throw_errno_exception("VIDIOC_S_STD"); + } + } + break; + case VIDEOSTANDARD_NO_CHANGE: + default: + // No change to device settings + break; + } - // get the current settings - struct v4l2_format fmt; - CLEAR(fmt); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(VIDIOC_G_FMT, &fmt)) - { - throw_errno_exception("VIDIOC_G_FMT"); - } + // get the current settings + struct v4l2_format fmt; + CLEAR(fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(VIDIOC_G_FMT, &fmt)) + { + throw_errno_exception("VIDIOC_G_FMT"); + } - // set the requested pixel format - switch (_pixelFormat) - { - case PIXELFORMAT_UYVY: - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - break; - case PIXELFORMAT_YUYV: - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - break; - case PIXELFORMAT_RGB32: - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; - break; - case PIXELFORMAT_NO_CHANGE: - default: - // No change to device settings - break; - } + // set the requested pixel format + switch (_pixelFormat) + { + case PIXELFORMAT_UYVY: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + break; + case PIXELFORMAT_YUYV: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case PIXELFORMAT_RGB32: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; + break; + case PIXELFORMAT_NO_CHANGE: + default: + // No change to device settings + break; + } - // set the requested withd and height - if (_width > 0 || _height > 0) - { - if (_width > 0) - { - fmt.fmt.pix.width = _width; - } + // set the requested withd and height + if (_width > 0 || _height > 0) + { + if (_width > 0) + { + fmt.fmt.pix.width = _width; + } - if (fmt.fmt.pix.height > 0) - { - fmt.fmt.pix.height = _height; - } - } + if (fmt.fmt.pix.height > 0) + { + fmt.fmt.pix.height = _height; + } + } - // set the settings - if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) - { - throw_errno_exception("VIDIOC_S_FMT"); - } + // set the settings + if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) + { + throw_errno_exception("VIDIOC_S_FMT"); + } - // get the format settings again - // (the size may not have been accepted without an error) - if (-1 == xioctl(VIDIOC_G_FMT, &fmt)) - { - throw_errno_exception("VIDIOC_G_FMT"); - } + // get the format settings again + // (the size may not have been accepted without an error) + if (-1 == xioctl(VIDIOC_G_FMT, &fmt)) + { + throw_errno_exception("VIDIOC_G_FMT"); + } - // store width & height - _width = fmt.fmt.pix.width; - _height = fmt.fmt.pix.height; + // store width & height + _width = fmt.fmt.pix.width; + _height = fmt.fmt.pix.height; - // print the eventually used width and height - std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl; + // print the eventually used width and height + std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl; - // check pixel format and frame size - switch (fmt.fmt.pix.pixelformat) - { - case V4L2_PIX_FMT_UYVY: - _pixelFormat = PIXELFORMAT_UYVY; - _frameByteSize = _width * _height * 2; - std::cout << "V4L2 pixel format=UYVY" << std::endl; - break; - case V4L2_PIX_FMT_YUYV: - _pixelFormat = PIXELFORMAT_YUYV; - _frameByteSize = _width * _height * 2; - std::cout << "V4L2 pixel format=YUYV" << std::endl; - break; - case V4L2_PIX_FMT_RGB32: - _pixelFormat = PIXELFORMAT_RGB32; - _frameByteSize = _width * _height * 4; - std::cout << "V4L2 pixel format=RGB32" << std::endl; - break; - default: - throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported"); - } + // check pixel format and frame size + switch (fmt.fmt.pix.pixelformat) + { + case V4L2_PIX_FMT_UYVY: + _pixelFormat = PIXELFORMAT_UYVY; + _frameByteSize = _width * _height * 2; + std::cout << "V4L2 pixel format=UYVY" << std::endl; + break; + case V4L2_PIX_FMT_YUYV: + _pixelFormat = PIXELFORMAT_YUYV; + _frameByteSize = _width * _height * 2; + std::cout << "V4L2 pixel format=YUYV" << std::endl; + break; + case V4L2_PIX_FMT_RGB32: + _pixelFormat = PIXELFORMAT_RGB32; + _frameByteSize = _width * _height * 4; + std::cout << "V4L2 pixel format=RGB32" << std::endl; + break; + default: + throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported"); + } - switch (_ioMethod) { - case IO_METHOD_READ: - init_read(fmt.fmt.pix.sizeimage); - break; + switch (_ioMethod) { + case IO_METHOD_READ: + init_read(fmt.fmt.pix.sizeimage); + break; - case IO_METHOD_MMAP: - init_mmap(); - break; + case IO_METHOD_MMAP: + init_mmap(); + break; - case IO_METHOD_USERPTR: - init_userp(fmt.fmt.pix.sizeimage); - break; - } + case IO_METHOD_USERPTR: + init_userp(fmt.fmt.pix.sizeimage); + break; + } } void V4L2Grabber::uninit_device() { - switch (_ioMethod) { - case IO_METHOD_READ: - free(_buffers[0].start); - break; + switch (_ioMethod) { + case IO_METHOD_READ: + free(_buffers[0].start); + break; - case IO_METHOD_MMAP: - for (size_t i = 0; i < _buffers.size(); ++i) - if (-1 == munmap(_buffers[i].start, _buffers[i].length)) - throw_errno_exception("munmap"); - break; + case IO_METHOD_MMAP: + for (size_t i = 0; i < _buffers.size(); ++i) + if (-1 == munmap(_buffers[i].start, _buffers[i].length)) + throw_errno_exception("munmap"); + break; - case IO_METHOD_USERPTR: - for (size_t i = 0; i < _buffers.size(); ++i) - free(_buffers[i].start); - break; - } + case IO_METHOD_USERPTR: + for (size_t i = 0; i < _buffers.size(); ++i) + free(_buffers[i].start); + break; + } - _buffers.resize(0); + _buffers.resize(0); } void V4L2Grabber::start_capturing() { - switch (_ioMethod) { - case IO_METHOD_READ: - /* Nothing to do. */ - break; + switch (_ioMethod) { + case IO_METHOD_READ: + /* Nothing to do. */ + break; - case IO_METHOD_MMAP: - { - for (size_t i = 0; i < _buffers.size(); ++i) { - struct v4l2_buffer buf; + case IO_METHOD_MMAP: + { + for (size_t i = 0; i < _buffers.size(); ++i) { + struct v4l2_buffer buf; - CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = i; + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; - if (-1 == xioctl(VIDIOC_QBUF, &buf)) - throw_errno_exception("VIDIOC_QBUF"); - } - v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(VIDIOC_STREAMON, &type)) - throw_errno_exception("VIDIOC_STREAMON"); - break; - } - case IO_METHOD_USERPTR: - { - for (size_t i = 0; i < _buffers.size(); ++i) { - struct v4l2_buffer buf; + if (-1 == xioctl(VIDIOC_QBUF, &buf)) + throw_errno_exception("VIDIOC_QBUF"); + } + v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(VIDIOC_STREAMON, &type)) + throw_errno_exception("VIDIOC_STREAMON"); + break; + } + case IO_METHOD_USERPTR: + { + for (size_t i = 0; i < _buffers.size(); ++i) { + struct v4l2_buffer buf; - CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_USERPTR; - buf.index = i; - buf.m.userptr = (unsigned long)_buffers[i].start; - buf.length = _buffers[i].length; + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.index = i; + buf.m.userptr = (unsigned long)_buffers[i].start; + buf.length = _buffers[i].length; - if (-1 == xioctl(VIDIOC_QBUF, &buf)) - throw_errno_exception("VIDIOC_QBUF"); - } - v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(VIDIOC_STREAMON, &type)) - throw_errno_exception("VIDIOC_STREAMON"); - break; - } - } + if (-1 == xioctl(VIDIOC_QBUF, &buf)) + throw_errno_exception("VIDIOC_QBUF"); + } + v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(VIDIOC_STREAMON, &type)) + throw_errno_exception("VIDIOC_STREAMON"); + break; + } + } } void V4L2Grabber::stop_capturing() { - enum v4l2_buf_type type; + enum v4l2_buf_type type; - switch (_ioMethod) { - case IO_METHOD_READ: - /* Nothing to do. */ - break; + switch (_ioMethod) { + case IO_METHOD_READ: + /* Nothing to do. */ + break; - case IO_METHOD_MMAP: - case IO_METHOD_USERPTR: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(VIDIOC_STREAMOFF, &type)) - throw_errno_exception("VIDIOC_STREAMOFF"); - break; - } + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(VIDIOC_STREAMOFF, &type)) + throw_errno_exception("VIDIOC_STREAMOFF"); + break; + } } int V4L2Grabber::read_frame() { - bool rc = false; + bool rc = false; - struct v4l2_buffer buf; + struct v4l2_buffer buf; - switch (_ioMethod) { - case IO_METHOD_READ: - int size; - if ((size = read(_fileDescriptor, _buffers[0].start, _buffers[0].length)) == -1) - { - switch (errno) - { - case EAGAIN: - return 0; + switch (_ioMethod) { + case IO_METHOD_READ: + int size; + if ((size = read(_fileDescriptor, _buffers[0].start, _buffers[0].length)) == -1) + { + switch (errno) + { + case EAGAIN: + return 0; - case EIO: - /* Could ignore EIO, see spec. */ + case EIO: + /* Could ignore EIO, see spec. */ - /* fall through */ + /* fall through */ - default: - throw_errno_exception("read"); - } - } + default: + throw_errno_exception("read"); + } + } - rc = process_image(_buffers[0].start, size); - break; + rc = process_image(_buffers[0].start, size); + break; - case IO_METHOD_MMAP: - CLEAR(buf); + case IO_METHOD_MMAP: + CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; - if (-1 == xioctl(VIDIOC_DQBUF, &buf)) - { - switch (errno) - { - case EAGAIN: - return 0; + if (-1 == xioctl(VIDIOC_DQBUF, &buf)) + { + switch (errno) + { + case EAGAIN: + return 0; - case EIO: - /* Could ignore EIO, see spec. */ + case EIO: + /* Could ignore EIO, see spec. */ - /* fall through */ + /* fall through */ - default: - throw_errno_exception("VIDIOC_DQBUF"); - } - } + default: + throw_errno_exception("VIDIOC_DQBUF"); + } + } - assert(buf.index < _buffers.size()); + assert(buf.index < _buffers.size()); - rc = process_image(_buffers[buf.index].start, buf.bytesused); + rc = process_image(_buffers[buf.index].start, buf.bytesused); - if (-1 == xioctl(VIDIOC_QBUF, &buf)) - { - throw_errno_exception("VIDIOC_QBUF"); - } + if (-1 == xioctl(VIDIOC_QBUF, &buf)) + { + throw_errno_exception("VIDIOC_QBUF"); + } - break; + break; - case IO_METHOD_USERPTR: - CLEAR(buf); + case IO_METHOD_USERPTR: + CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_USERPTR; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; - if (-1 == xioctl(VIDIOC_DQBUF, &buf)) - { - switch (errno) - { - case EAGAIN: - return 0; + if (-1 == xioctl(VIDIOC_DQBUF, &buf)) + { + switch (errno) + { + case EAGAIN: + return 0; - case EIO: - /* Could ignore EIO, see spec. */ + case EIO: + /* Could ignore EIO, see spec. */ - /* fall through */ + /* fall through */ - default: - throw_errno_exception("VIDIOC_DQBUF"); - } - } + default: + throw_errno_exception("VIDIOC_DQBUF"); + } + } - for (size_t i = 0; i < _buffers.size(); ++i) - { - if (buf.m.userptr == (unsigned long)_buffers[i].start && buf.length == _buffers[i].length) - { - break; - } - } + for (size_t i = 0; i < _buffers.size(); ++i) + { + if (buf.m.userptr == (unsigned long)_buffers[i].start && buf.length == _buffers[i].length) + { + break; + } + } - rc = process_image((void *)buf.m.userptr, buf.bytesused); + rc = process_image((void *)buf.m.userptr, buf.bytesused); - if (-1 == xioctl(VIDIOC_QBUF, &buf)) - { - throw_errno_exception("VIDIOC_QBUF"); - } - break; - } + if (-1 == xioctl(VIDIOC_QBUF, &buf)) + { + throw_errno_exception("VIDIOC_QBUF"); + } + break; + } - return rc ? 1 : 0; + return rc ? 1 : 0; } bool V4L2Grabber::process_image(const void *p, int size) { - if (++_currentFrame >= _frameDecimation) - { - // We do want a new frame... + if (++_currentFrame >= _frameDecimation) + { + // We do want a new frame... - if (size != _frameByteSize) - { - std::cout << "Frame too small: " << size << " != " << _frameByteSize << std::endl; - } - else - { - process_image(reinterpret_cast(p)); - _currentFrame = 0; // restart counting - return true; - } - } + if (size != _frameByteSize) + { + std::cout << "Frame too small: " << size << " != " << _frameByteSize << std::endl; + } + else + { + process_image(reinterpret_cast(p)); + _currentFrame = 0; // restart counting + return true; + } + } - return false; + return false; } void V4L2Grabber::process_image(const uint8_t * data) { - int width = _width; - int height = _height; + int width = _width; + int height = _height; - switch (_mode3D) - { - case VIDEO_3DSBS: - width = _width/2; - break; - case VIDEO_3DTAB: - height = _height/2; - break; - default: - break; - } + switch (_mode3D) + { + case VIDEO_3DSBS: + width = _width/2; + break; + case VIDEO_3DTAB: + height = _height/2; + break; + default: + break; + } - // create output structure - int outputWidth = (width - _cropLeft - _cropRight + _horizontalPixelDecimation/2) / _horizontalPixelDecimation; - int outputHeight = (height - _cropTop - _cropBottom + _verticalPixelDecimation/2) / _verticalPixelDecimation; - Image image(outputWidth, outputHeight); + // create output structure + int outputWidth = (width - _cropLeft - _cropRight + _horizontalPixelDecimation/2) / _horizontalPixelDecimation; + int outputHeight = (height - _cropTop - _cropBottom + _verticalPixelDecimation/2) / _verticalPixelDecimation; - for (int ySource = _cropTop + _verticalPixelDecimation/2, yDest = 0; ySource < height - _cropBottom; ySource += _verticalPixelDecimation, ++yDest) - { - for (int xSource = _cropLeft + _horizontalPixelDecimation/2, xDest = 0; xSource < width - _cropRight; xSource += _horizontalPixelDecimation, ++xDest) - { - ColorRgb & rgb = image(xDest, yDest); + // TODO: should this be the following (like X11): + //int outputWidth = (width - _cropLeft - _cropRight + _horizontalPixelDecimation/2 - 1) / _horizontalPixelDecimation + 1; + //int outputHeight = (height - _cropTop - _cropBottom + _verticalPixelDecimation/2 - 1) / _verticalPixelDecimation + 1; - switch (_pixelFormat) - { - case PIXELFORMAT_UYVY: - { - int index = (_width * ySource + xSource) * 2; - uint8_t y = data[index+1]; - uint8_t u = (xSource%2 == 0) ? data[index ] : data[index-2]; - uint8_t v = (xSource%2 == 0) ? data[index+2] : data[index ]; - yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); - } - break; - case PIXELFORMAT_YUYV: - { - int index = (_width * ySource + xSource) * 2; - uint8_t y = data[index]; - uint8_t u = (xSource%2 == 0) ? data[index+1] : data[index-1]; - uint8_t v = (xSource%2 == 0) ? data[index+3] : data[index+1]; - yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); - } - break; - case PIXELFORMAT_RGB32: - { - int index = (_width * ySource + xSource) * 4; - rgb.red = data[index ]; - rgb.green = data[index+1]; - rgb.blue = data[index+2]; - } - break; - default: - // this should not be possible - break; - } - } - } + Image image(outputWidth, outputHeight); - // check signal (only in center of the resulting image, because some grabbers have noise values along the borders) - bool noSignal = true; - for (unsigned x = 0; noSignal && x < (image.width()>>1); ++x) - { - int xImage = (image.width()>>2) + x; + for (int ySource = _cropTop + _verticalPixelDecimation/2, yDest = 0; ySource < height - _cropBottom; ySource += _verticalPixelDecimation, ++yDest) + { + for (int xSource = _cropLeft + _horizontalPixelDecimation/2, xDest = 0; xSource < width - _cropRight; xSource += _horizontalPixelDecimation, ++xDest) + { + ColorRgb & rgb = image(xDest, yDest); - for (unsigned y = 0; noSignal && y < (image.height()>>1); ++y) - { - int yImage = (image.height()>>2) + y; + switch (_pixelFormat) + { + case PIXELFORMAT_UYVY: + { + int index = (_width * ySource + xSource) * 2; + uint8_t y = data[index+1]; + uint8_t u = (xSource%2 == 0) ? data[index ] : data[index-2]; + uint8_t v = (xSource%2 == 0) ? data[index+2] : data[index ]; + yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); + } + break; + case PIXELFORMAT_YUYV: + { + int index = (_width * ySource + xSource) * 2; + uint8_t y = data[index]; + uint8_t u = (xSource%2 == 0) ? data[index+1] : data[index-1]; + uint8_t v = (xSource%2 == 0) ? data[index+3] : data[index+1]; + yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); + } + break; + case PIXELFORMAT_RGB32: + { + int index = (_width * ySource + xSource) * 4; + rgb.red = data[index ]; + rgb.green = data[index+1]; + rgb.blue = data[index+2]; + } + break; + default: + // this should not be possible + break; + } + } + } - ColorRgb & rgb = image(xImage, yImage); - noSignal &= rgb <= _noSignalThresholdColor; - } - } + // check signal (only in center of the resulting image, because some grabbers have noise values along the borders) + bool noSignal = true; + for (unsigned x = 0; noSignal && x < (image.width()>>1); ++x) + { + int xImage = (image.width()>>2) + x; - if (noSignal) - { - ++_noSignalCounter; - } - else - { - if (_noSignalCounter >= _noSignalCounterThreshold) - { - std::cout << "V4L2 Grabber: " << "Signal detected" << std::endl; - } + for (unsigned y = 0; noSignal && y < (image.height()>>1); ++y) + { + int yImage = (image.height()>>2) + y; - _noSignalCounter = 0; - } + ColorRgb & rgb = image(xImage, yImage); + noSignal &= rgb <= _noSignalThresholdColor; + } + } - if (_noSignalCounter < _noSignalCounterThreshold) - { - emit newFrame(image); - } - else if (_noSignalCounter == _noSignalCounterThreshold) - { - std::cout << "V4L2 Grabber: " << "Signal lost" << std::endl; - } + if (noSignal) + { + ++_noSignalCounter; + } + else + { + if (_noSignalCounter >= _noSignalCounterThreshold) + { + std::cout << "V4L2 Grabber: " << "Signal detected" << std::endl; + } + + _noSignalCounter = 0; + } + + if (_noSignalCounter < _noSignalCounterThreshold) + { + emit newFrame(image); + } + else if (_noSignalCounter == _noSignalCounterThreshold) + { + std::cout << "V4L2 Grabber: " << "Signal lost" << std::endl; + } } int V4L2Grabber::xioctl(int request, void *arg) { - int r; + int r; - do - { - r = ioctl(_fileDescriptor, request, arg); - } - while (-1 == r && EINTR == errno); + do + { + r = ioctl(_fileDescriptor, request, arg); + } + while (-1 == r && EINTR == errno); - return r; + return r; } void V4L2Grabber::throw_exception(const std::string & error) { - std::ostringstream oss; - oss << error << " error"; - throw std::runtime_error(oss.str()); + std::ostringstream oss; + oss << error << " error"; + throw std::runtime_error(oss.str()); } void V4L2Grabber::throw_errno_exception(const std::string & error) { - std::ostringstream oss; - oss << error << " error " << errno << ", " << strerror(errno); - throw std::runtime_error(oss.str()); + std::ostringstream oss; + oss << error << " error " << errno << ", " << strerror(errno); + throw std::runtime_error(oss.str()); } diff --git a/libsrc/grabber/x11/X11Grabber.cpp b/libsrc/grabber/x11/X11Grabber.cpp index 93f6e434..dd991aa6 100644 --- a/libsrc/grabber/x11/X11Grabber.cpp +++ b/libsrc/grabber/x11/X11Grabber.cpp @@ -64,8 +64,11 @@ Image & X11Grabber::grab() // Copy the capture XImage to the local image (and apply required decimation) ColorRgb * outputPtr = _image.memptr(); + int width = 0; + int height = 0; for (int iY=(_pixelDecimation/2); iY & X11Grabber::grab() // Move to the next output pixel ++outputPtr; + ++width; } + ++height; } + + std::cout << "decimated X11 message: " << width << " x " << height << std::endl; + // Cleanup allocated resources of the X11 grab XDestroyImage(xImage); @@ -107,8 +115,9 @@ int X11Grabber::updateScreenDimensions() std::cout << "[" << _screenWidth << "x" << _screenHeight <<"]" << std::endl; // Update the size of the buffer used to transfer the screenshot - int width = (_screenWidth - 2 * _cropWidth + _pixelDecimation/2) / _pixelDecimation; - int height = (_screenHeight - 2 * _cropHeight + _pixelDecimation/2) / _pixelDecimation; + int width = (_screenWidth - 2 * _cropWidth - _pixelDecimation/2 - 1) / _pixelDecimation + 1; + int height = (_screenHeight - 2 * _cropHeight - _pixelDecimation/2 - 1) / _pixelDecimation + 1; + _image.resize(width, height); return 0;