hyperion won't fail, if v4l2 has invalid device + auto device (#118)

* hyperion won't fail, if v4l2 has invalid device

* add v4l auto select. use "auto" as device name
This commit is contained in:
redPanther 2016-07-14 23:40:10 +02:00 committed by brindosch
parent 9f1bc6d9e8
commit b49ada956b
6 changed files with 152 additions and 78 deletions

View File

@ -3,6 +3,7 @@
// stl includes // stl includes
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
// Qt includes // Qt includes
#include <QObject> #include <QObject>
@ -50,7 +51,7 @@ public slots:
double blueSignalThreshold, double blueSignalThreshold,
int noSignalCounterThreshold); int noSignalCounterThreshold);
void start(); bool start();
void stop(); void stop();
@ -61,6 +62,11 @@ private slots:
int read_frame(); int read_frame();
private: private:
void getV4Ldevices();
bool init();
void uninit();
void open_device(); void open_device();
void close_device(); void close_device();
@ -102,8 +108,11 @@ private:
}; };
private: private:
const std::string _deviceName; std::string _deviceName;
const io_method _ioMethod; std::map<std::string,std::string> _v4lDevices;
int _input;
VideoStandard _videoStandard;
io_method _ioMethod;
int _fileDescriptor; int _fileDescriptor;
std::vector<buffer> _buffers; std::vector<buffer> _buffers;
@ -125,4 +134,5 @@ private:
ImageResampler _imageResampler; ImageResampler _imageResampler;
Logger * _log; Logger * _log;
bool _initialized;
}; };

View File

@ -30,7 +30,7 @@ public:
virtual ~V4L2Wrapper(); virtual ~V4L2Wrapper();
public slots: public slots:
void start(); bool start();
void stop(); void stop();

View File

@ -7,12 +7,15 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
#include <sstream>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <QDirIterator>
#include <QFileInfo>
#include "grabber/V4L2Grabber.h" #include "grabber/V4L2Grabber.h"
@ -26,38 +29,113 @@ V4L2Grabber::V4L2Grabber(const std::string & device,
int height, int height,
int frameDecimation, int frameDecimation,
int horizontalPixelDecimation, int horizontalPixelDecimation,
int verticalPixelDecimation) : int verticalPixelDecimation)
_deviceName(device), : _deviceName(device)
_ioMethod(IO_METHOD_MMAP), , _input(input)
_fileDescriptor(-1), , _videoStandard(videoStandard)
_buffers(), , _ioMethod(IO_METHOD_MMAP)
_pixelFormat(pixelFormat), , _fileDescriptor(-1)
_width(width), , _buffers()
_height(height), , _pixelFormat(pixelFormat)
_lineLength(-1), , _width(width)
_frameByteSize(-1), , _height(height)
_frameDecimation(std::max(1, frameDecimation)), , _lineLength(-1)
_noSignalCounterThreshold(50), , _frameByteSize(-1)
_noSignalThresholdColor(ColorRgb{0,0,0}), , _frameDecimation(std::max(1, frameDecimation))
_currentFrame(0), , _noSignalCounterThreshold(50)
_noSignalCounter(0), , _noSignalThresholdColor(ColorRgb{0,0,0})
_streamNotifier(nullptr), , _currentFrame(0)
_imageResampler(), , _noSignalCounter(0)
_log(Logger::getInstance("V4L2GRABBER")) , _streamNotifier(nullptr)
, _imageResampler()
, _log(Logger::getInstance("V4L2GRABBER"))
,_initialized(false)
{ {
_imageResampler.setHorizontalPixelDecimation(std::max(1, horizontalPixelDecimation)); _imageResampler.setHorizontalPixelDecimation(std::max(1, horizontalPixelDecimation));
_imageResampler.setVerticalPixelDecimation(std::max(1, verticalPixelDecimation)); _imageResampler.setVerticalPixelDecimation(std::max(1, verticalPixelDecimation));
open_device(); getV4Ldevices();
init_device(videoStandard, input);
} }
V4L2Grabber::~V4L2Grabber() V4L2Grabber::~V4L2Grabber()
{
uninit();
}
void V4L2Grabber::uninit()
{ {
// stop if the grabber was not stopped // stop if the grabber was not stopped
stop(); if (_initialized)
uninit_device(); {
close_device(); 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) 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); _noSignalThresholdColor.blue = uint8_t(255*blueSignalThreshold);
_noSignalCounterThreshold = std::max(1, noSignalCounterThreshold); _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); _streamNotifier->setEnabled(true);
start_capturing(); start_capturing();
Info(_log, "Started"); Info(_log, "Started");
return true;
} }
return false;
} }
void V4L2Grabber::stop() void V4L2Grabber::stop()
@ -106,25 +189,19 @@ void V4L2Grabber::open_device()
if (-1 == stat(_deviceName.c_str(), &st)) if (-1 == stat(_deviceName.c_str(), &st))
{ {
std::ostringstream oss; throw_errno_exception("Cannot identify '" + _deviceName + "'");
oss << "V4L2GRABBER ERROR: Cannot identify '" << _deviceName << "'";
throw_errno_exception(oss.str());
} }
if (!S_ISCHR(st.st_mode)) if (!S_ISCHR(st.st_mode))
{ {
std::ostringstream oss; throw_exception("'" + _deviceName + "' is no device");
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) if (-1 == _fileDescriptor)
{ {
std::ostringstream oss; throw_errno_exception("Cannot open '" + _deviceName + "'");
oss << "V4L2GRABBER ERROR: Cannot open '" << _deviceName << "'";
throw_errno_exception(oss.str());
} }
// create the notifier for when a new frame is available // 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); _buffers[0].start = malloc(buffer_size);
if (!_buffers[0].start) { 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 (-1 == xioctl(VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) { if (EINVAL == errno) {
std::ostringstream oss; throw_exception("'" + _deviceName + "' does not support memory mapping");
oss << "'" << _deviceName << "' does not support memory mapping";
throw_exception(oss.str());
} else { } else {
throw_errno_exception("VIDIOC_REQBUFS"); throw_errno_exception("VIDIOC_REQBUFS");
} }
} }
if (req.count < 2) { if (req.count < 2) {
std::ostringstream oss; throw_exception("Insufficient buffer memory on " + _deviceName);
oss << "Insufficient buffer memory on " << _deviceName;
throw_exception(oss.str());
} }
_buffers.resize(req.count); _buffers.resize(req.count);
@ -225,9 +298,7 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
if (-1 == xioctl(VIDIOC_REQBUFS, &req)) { if (-1 == xioctl(VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) if (EINVAL == errno)
{ {
std::ostringstream oss; throw_exception("'" + _deviceName + "' does not support user pointer");
oss << "'" << _deviceName << "' does not support user pointer";
throw_exception(oss.str());
} else { } else {
throw_errno_exception("VIDIOC_REQBUFS"); throw_errno_exception("VIDIOC_REQBUFS");
} }
@ -240,7 +311,7 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
_buffers[n_buffers].start = malloc(buffer_size); _buffers[n_buffers].start = malloc(buffer_size);
if (!_buffers[n_buffers].start) { 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 (-1 == xioctl(VIDIOC_QUERYCAP, &cap))
{ {
if (EINVAL == errno) { if (EINVAL == errno) {
std::ostringstream oss; throw_exception("'" + _deviceName + "' is no V4L2 device");
oss << "'" << _deviceName << "' is no V4L2 device";
throw_exception(oss.str());
} else { } else {
throw_errno_exception("VIDIOC_QUERYCAP"); throw_errno_exception("VIDIOC_QUERYCAP");
} }
@ -261,18 +330,14 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{ {
std::ostringstream oss; throw_exception("'" + _deviceName + "' is no video capture device");
oss << "'" << _deviceName << "' is no video capture device";
throw_exception(oss.str());
} }
switch (_ioMethod) { switch (_ioMethod) {
case IO_METHOD_READ: case IO_METHOD_READ:
if (!(cap.capabilities & V4L2_CAP_READWRITE)) if (!(cap.capabilities & V4L2_CAP_READWRITE))
{ {
std::ostringstream oss; throw_exception("'" + _deviceName + "' does not support read i/o");
oss << "'" << _deviceName << "' does not support read i/o";
throw_exception(oss.str());
} }
break; break;
@ -280,9 +345,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
case IO_METHOD_USERPTR: case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) if (!(cap.capabilities & V4L2_CAP_STREAMING))
{ {
std::ostringstream oss; throw_exception("'" + _deviceName + "' does not support streaming i/o");
oss << "'" << _deviceName << "' does not support streaming i/o";
throw_exception(oss.str());
} }
break; break;
} }
@ -413,7 +476,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
_height = fmt.fmt.pix.height; _height = fmt.fmt.pix.height;
// display the used width and 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 // check pixel format and frame size
@ -435,7 +498,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
Debug(_log, "Pixel format=RGB32"); Debug(_log, "Pixel format=RGB32");
break; break;
default: 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) { switch (_ioMethod) {
@ -727,14 +790,12 @@ int V4L2Grabber::xioctl(int request, void *arg)
void V4L2Grabber::throw_exception(const std::string & error) void V4L2Grabber::throw_exception(const std::string & error)
{ {
std::ostringstream oss; throw std::runtime_error(error);
oss << error << " ERROR";
throw std::runtime_error(oss.str());
} }
void V4L2Grabber::throw_errno_exception(const std::string & error) void V4L2Grabber::throw_errno_exception(const std::string & error)
{ {
std::ostringstream oss; std::ostringstream oss;
oss << error << " ERROR " << errno << ", " << strerror(errno); oss << error << " error code " << errno << ", " << strerror(errno);
throw std::runtime_error(oss.str()); throw std::runtime_error(oss.str());
} }

View File

@ -68,9 +68,13 @@ V4L2Wrapper::~V4L2Wrapper()
delete _processor; delete _processor;
} }
void V4L2Wrapper::start() bool V4L2Wrapper::start()
{ {
_grabber.start(); bool grabber_started = _grabber.start();
if ( ! grabber_started )
_timer.stop();
return grabber_started;
} }
void V4L2Wrapper::stop() void V4L2Wrapper::stop()

View File

@ -164,8 +164,8 @@ int main(int argc, char** argv)
{ {
ProtoConnectionWrapper handler(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet()); ProtoConnectionWrapper handler(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet());
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>))); QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
grabber.start(); if (grabber.start())
QCoreApplication::exec(); QCoreApplication::exec();
grabber.stop(); grabber.stop();
} }
} }

View File

@ -360,8 +360,6 @@ void HyperionDaemon::createGrabberV4L2()
if (_config.isMember("grabber-v4l2")) if (_config.isMember("grabber-v4l2"))
{ {
const Json::Value & grabberConfig = _config["grabber-v4l2"]; const Json::Value & grabberConfig = _config["grabber-v4l2"];
if (grabberConfig.get("enable", true).asBool())
{
_v4l2Grabber = new V4L2Wrapper( _v4l2Grabber = new V4L2Wrapper(
grabberConfig.get("device", "/dev/video0").asString(), grabberConfig.get("device", "/dev/video0").asString(),
grabberConfig.get("input", 0).asInt(), grabberConfig.get("input", 0).asInt(),
@ -381,11 +379,12 @@ void HyperionDaemon::createGrabberV4L2()
grabberConfig.get("cropRight", 0).asInt(), grabberConfig.get("cropRight", 0).asInt(),
grabberConfig.get("cropTop", 0).asInt(), grabberConfig.get("cropTop", 0).asInt(),
grabberConfig.get("cropBottom", 0).asInt()); grabberConfig.get("cropBottom", 0).asInt());
Debug(_log, "V4L2 grabber created");
QObject::connect(_v4l2Grabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), _protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) ); QObject::connect(_v4l2Grabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), _protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
if (grabberConfig.get("enable", true).asBool() && _v4l2Grabber->start())
_v4l2Grabber->start(); {
Info(_log, "V4L2 grabber created and started"); Info(_log, "V4L2 grabber started");
} }
} }
#else #else