|
|
|
@@ -7,12 +7,15 @@
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <linux/videodev2.h>
|
|
|
|
|
#include <QDirIterator>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
|
|
|
|
|
#include "grabber/V4L2Grabber.h"
|
|
|
|
|
|
|
|
|
@@ -26,38 +29,113 @@ V4L2Grabber::V4L2Grabber(const std::string & device,
|
|
|
|
|
int height,
|
|
|
|
|
int frameDecimation,
|
|
|
|
|
int horizontalPixelDecimation,
|
|
|
|
|
int verticalPixelDecimation) :
|
|
|
|
|
_deviceName(device),
|
|
|
|
|
_ioMethod(IO_METHOD_MMAP),
|
|
|
|
|
_fileDescriptor(-1),
|
|
|
|
|
_buffers(),
|
|
|
|
|
_pixelFormat(pixelFormat),
|
|
|
|
|
_width(width),
|
|
|
|
|
_height(height),
|
|
|
|
|
_lineLength(-1),
|
|
|
|
|
_frameByteSize(-1),
|
|
|
|
|
_frameDecimation(std::max(1, frameDecimation)),
|
|
|
|
|
_noSignalCounterThreshold(50),
|
|
|
|
|
_noSignalThresholdColor(ColorRgb{0,0,0}),
|
|
|
|
|
_currentFrame(0),
|
|
|
|
|
_noSignalCounter(0),
|
|
|
|
|
_streamNotifier(nullptr),
|
|
|
|
|
_imageResampler(),
|
|
|
|
|
_log(Logger::getInstance("V4L2GRABBER"))
|
|
|
|
|
int verticalPixelDecimation)
|
|
|
|
|
: _deviceName(device)
|
|
|
|
|
, _input(input)
|
|
|
|
|
, _videoStandard(videoStandard)
|
|
|
|
|
, _ioMethod(IO_METHOD_MMAP)
|
|
|
|
|
, _fileDescriptor(-1)
|
|
|
|
|
, _buffers()
|
|
|
|
|
, _pixelFormat(pixelFormat)
|
|
|
|
|
, _width(width)
|
|
|
|
|
, _height(height)
|
|
|
|
|
, _lineLength(-1)
|
|
|
|
|
, _frameByteSize(-1)
|
|
|
|
|
, _frameDecimation(std::max(1, frameDecimation))
|
|
|
|
|
, _noSignalCounterThreshold(50)
|
|
|
|
|
, _noSignalThresholdColor(ColorRgb{0,0,0})
|
|
|
|
|
, _currentFrame(0)
|
|
|
|
|
, _noSignalCounter(0)
|
|
|
|
|
, _streamNotifier(nullptr)
|
|
|
|
|
, _imageResampler()
|
|
|
|
|
, _log(Logger::getInstance("V4L2GRABBER"))
|
|
|
|
|
,_initialized(false)
|
|
|
|
|
{
|
|
|
|
|
_imageResampler.setHorizontalPixelDecimation(std::max(1, horizontalPixelDecimation));
|
|
|
|
|
_imageResampler.setVerticalPixelDecimation(std::max(1, verticalPixelDecimation));
|
|
|
|
|
|
|
|
|
|
open_device();
|
|
|
|
|
init_device(videoStandard, input);
|
|
|
|
|
getV4Ldevices();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
V4L2Grabber::~V4L2Grabber()
|
|
|
|
|
{
|
|
|
|
|
uninit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V4L2Grabber::uninit()
|
|
|
|
|
{
|
|
|
|
|
// stop if the grabber was not stopped
|
|
|
|
|
stop();
|
|
|
|
|
uninit_device();
|
|
|
|
|
close_device();
|
|
|
|
|
if (_initialized)
|
|
|
|
|
{
|
|
|
|
|
stop();
|
|
|
|
|
uninit_device();
|
|
|
|
|
close_device();
|
|
|
|
|
_initialized = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool V4L2Grabber::init()
|
|
|
|
|
{
|
|
|
|
|
if ( _deviceName == "auto" )
|
|
|
|
|
{
|
|
|
|
|
for (auto& dev: _v4lDevices)
|
|
|
|
|
{
|
|
|
|
|
Debug(_log, "check v4l2 device: %s (%s)",dev.first.c_str(), dev.second.c_str());
|
|
|
|
|
_deviceName = dev.first;
|
|
|
|
|
if ( init() )
|
|
|
|
|
{
|
|
|
|
|
Info(_log, "found usable v4l2 device: %s (%s)",dev.first.c_str(), dev.second.c_str());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! _initialized)
|
|
|
|
|
{
|
|
|
|
|
bool opened = false;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
open_device();
|
|
|
|
|
opened = true;
|
|
|
|
|
init_device(_videoStandard, _input);
|
|
|
|
|
_initialized = true;
|
|
|
|
|
}
|
|
|
|
|
catch(std::exception& e)
|
|
|
|
|
{
|
|
|
|
|
if (opened)
|
|
|
|
|
{
|
|
|
|
|
uninit_device();
|
|
|
|
|
close_device();
|
|
|
|
|
}
|
|
|
|
|
Error( _log, "V4l2 init failed (%s)", e.what());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _initialized;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V4L2Grabber::getV4Ldevices()
|
|
|
|
|
{
|
|
|
|
|
QDirIterator it("/sys/class/video4linux/", QDirIterator::NoIteratorFlags);
|
|
|
|
|
while(it.hasNext())
|
|
|
|
|
{
|
|
|
|
|
//_v4lDevices
|
|
|
|
|
QString dev = it.next();
|
|
|
|
|
if (it.fileName().startsWith("video"))
|
|
|
|
|
{
|
|
|
|
|
QFile devNameFile(dev+"/name");
|
|
|
|
|
QString devName;
|
|
|
|
|
if ( devNameFile.exists())
|
|
|
|
|
{
|
|
|
|
|
devNameFile.open(QFile::ReadOnly);
|
|
|
|
|
devName = devNameFile.readLine();
|
|
|
|
|
devName = devName.trimmed();
|
|
|
|
|
devNameFile.close();
|
|
|
|
|
}
|
|
|
|
|
_v4lDevices.emplace("/dev/"+it.fileName().toStdString(), devName.toStdString());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V4L2Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
|
|
|
|
@@ -77,17 +155,22 @@ void V4L2Grabber::setSignalThreshold(double redSignalThreshold, double greenSign
|
|
|
|
|
_noSignalThresholdColor.blue = uint8_t(255*blueSignalThreshold);
|
|
|
|
|
_noSignalCounterThreshold = std::max(1, noSignalCounterThreshold);
|
|
|
|
|
|
|
|
|
|
Info(_log, "Signal threshold set to: %d", _noSignalThresholdColor);
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << _noSignalThresholdColor;
|
|
|
|
|
Info(_log, "Signal threshold set to: %s", ss.str().c_str() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V4L2Grabber::start()
|
|
|
|
|
bool V4L2Grabber::start()
|
|
|
|
|
{
|
|
|
|
|
if (_streamNotifier != nullptr && !_streamNotifier->isEnabled())
|
|
|
|
|
if (init() && _streamNotifier != nullptr && !_streamNotifier->isEnabled())
|
|
|
|
|
{
|
|
|
|
|
_streamNotifier->setEnabled(true);
|
|
|
|
|
start_capturing();
|
|
|
|
|
Info(_log, "Started");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V4L2Grabber::stop()
|
|
|
|
@@ -106,25 +189,19 @@ void V4L2Grabber::open_device()
|
|
|
|
|
|
|
|
|
|
if (-1 == stat(_deviceName.c_str(), &st))
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "V4L2GRABBER ERROR: Cannot identify '" << _deviceName << "'";
|
|
|
|
|
throw_errno_exception(oss.str());
|
|
|
|
|
throw_errno_exception("Cannot identify '" + _deviceName + "'");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!S_ISCHR(st.st_mode))
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "'" << _deviceName << "' is no device";
|
|
|
|
|
throw_exception(oss.str());
|
|
|
|
|
throw_exception("'" + _deviceName + "' is no device");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_fileDescriptor = open(_deviceName.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
|
|
|
|
|
|
|
|
|
|
if (-1 == _fileDescriptor)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "V4L2GRABBER ERROR: Cannot open '" << _deviceName << "'";
|
|
|
|
|
throw_errno_exception(oss.str());
|
|
|
|
|
throw_errno_exception("Cannot open '" + _deviceName + "'");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create the notifier for when a new frame is available
|
|
|
|
@@ -155,7 +232,7 @@ void V4L2Grabber::init_read(unsigned int buffer_size)
|
|
|
|
|
_buffers[0].start = malloc(buffer_size);
|
|
|
|
|
|
|
|
|
|
if (!_buffers[0].start) {
|
|
|
|
|
throw_exception("V4L2GRABBER ERROR: Out of memory");
|
|
|
|
|
throw_exception("Out of memory");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -171,18 +248,14 @@ void V4L2Grabber::init_mmap()
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(VIDIOC_REQBUFS, &req)) {
|
|
|
|
|
if (EINVAL == errno) {
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "'" << _deviceName << "' does not support memory mapping";
|
|
|
|
|
throw_exception(oss.str());
|
|
|
|
|
throw_exception("'" + _deviceName + "' does not support memory mapping");
|
|
|
|
|
} else {
|
|
|
|
|
throw_errno_exception("VIDIOC_REQBUFS");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (req.count < 2) {
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "Insufficient buffer memory on " << _deviceName;
|
|
|
|
|
throw_exception(oss.str());
|
|
|
|
|
throw_exception("Insufficient buffer memory on " + _deviceName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_buffers.resize(req.count);
|
|
|
|
@@ -225,9 +298,7 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
|
|
|
|
|
if (-1 == xioctl(VIDIOC_REQBUFS, &req)) {
|
|
|
|
|
if (EINVAL == errno)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "'" << _deviceName << "' does not support user pointer";
|
|
|
|
|
throw_exception(oss.str());
|
|
|
|
|
throw_exception("'" + _deviceName + "' does not support user pointer");
|
|
|
|
|
} else {
|
|
|
|
|
throw_errno_exception("VIDIOC_REQBUFS");
|
|
|
|
|
}
|
|
|
|
@@ -240,7 +311,7 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
|
|
|
|
|
_buffers[n_buffers].start = malloc(buffer_size);
|
|
|
|
|
|
|
|
|
|
if (!_buffers[n_buffers].start) {
|
|
|
|
|
throw_exception("V4L2GRABBER ERROR: Out of memory");
|
|
|
|
|
throw_exception("Out of memory");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -251,9 +322,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|
|
|
|
if (-1 == xioctl(VIDIOC_QUERYCAP, &cap))
|
|
|
|
|
{
|
|
|
|
|
if (EINVAL == errno) {
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "'" << _deviceName << "' is no V4L2 device";
|
|
|
|
|
throw_exception(oss.str());
|
|
|
|
|
throw_exception("'" + _deviceName + "' is no V4L2 device");
|
|
|
|
|
} else {
|
|
|
|
|
throw_errno_exception("VIDIOC_QUERYCAP");
|
|
|
|
|
}
|
|
|
|
@@ -261,18 +330,14 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|
|
|
|
|
|
|
|
|
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "'" << _deviceName << "' is no video capture device";
|
|
|
|
|
throw_exception(oss.str());
|
|
|
|
|
throw_exception("'" + _deviceName + "' is no video capture device");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
throw_exception("'" + _deviceName + "' does not support read i/o");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
@@ -280,9 +345,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|
|
|
|
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());
|
|
|
|
|
throw_exception("'" + _deviceName + "' does not support streaming i/o");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@@ -413,7 +476,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|
|
|
|
_height = fmt.fmt.pix.height;
|
|
|
|
|
|
|
|
|
|
// display the used width and height
|
|
|
|
|
Info(Logger::getInstance("V4l2GRABBER"), "width=%d height=%d", _width, _height );
|
|
|
|
|
Info(_log, "width=%d height=%d", _width, _height );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check pixel format and frame size
|
|
|
|
@@ -435,7 +498,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|
|
|
|
Debug(_log, "Pixel format=RGB32");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw_exception("V4L2GRABBER ERROR: Only pixel formats UYVY, YUYV, and RGB32 are supported");
|
|
|
|
|
throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (_ioMethod) {
|
|
|
|
@@ -727,14 +790,12 @@ int V4L2Grabber::xioctl(int request, void *arg)
|
|
|
|
|
|
|
|
|
|
void V4L2Grabber::throw_exception(const std::string & error)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << error << " ERROR";
|
|
|
|
|
throw std::runtime_error(oss.str());
|
|
|
|
|
throw std::runtime_error(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V4L2Grabber::throw_errno_exception(const std::string & error)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << error << " ERROR " << errno << ", " << strerror(errno);
|
|
|
|
|
oss << error << " error code " << errno << ", " << strerror(errno);
|
|
|
|
|
throw std::runtime_error(oss.str());
|
|
|
|
|
}
|
|
|
|
|