diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index ab002c3d..40de0870 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -4,26 +4,27 @@ #include #include +// Qt includes +#include +#include + // util includes #include #include +#include /// Capture class for V4L2 devices /// /// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html -class V4L2Grabber +class V4L2Grabber : public QObject { -public: - typedef void (*ImageCallback)(void * arg, const Image & image); + Q_OBJECT +public: enum VideoStandard { PAL, NTSC, NO_CHANGE }; - enum Mode3D { - MODE_NONE, MODE_3DSBS, MODE_3DTAB - }; - public: V4L2Grabber( const std::string & device, @@ -36,21 +37,24 @@ public: int verticalPixelDecimation); virtual ~V4L2Grabber(); +public slots: void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom); - void set3D(Mode3D mode); - - void setCallback(ImageCallback callback, void * arg); + void set3D(VideoMode mode); void start(); - void capture(int frameCount = -1); - void stop(); +signals: + void newFrame(const Image & image); + +private slots: + int read_frame(); + private: void open_device(); @@ -70,8 +74,6 @@ private: void stop_capturing(); - int read_frame(); - bool process_image(const void *p, int size); void process_image(const uint8_t *p); @@ -111,10 +113,9 @@ private: int _horizontalPixelDecimation; int _verticalPixelDecimation; - Mode3D _mode3D; + VideoMode _mode3D; int _currentFrame; - ImageCallback _callback; - void * _callbackArg; + QSocketNotifier * _streamNotifier; }; diff --git a/libsrc/grabber/v4l2/CMakeLists.txt b/libsrc/grabber/v4l2/CMakeLists.txt index ec7830e0..bb8e15b9 100644 --- a/libsrc/grabber/v4l2/CMakeLists.txt +++ b/libsrc/grabber/v4l2/CMakeLists.txt @@ -2,18 +2,27 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/v4l2) -SET(V4L2_HEADERS +SET(V4L2_QT_HEADERS ${CURRENT_HEADER_DIR}/V4L2Grabber.h ) +SET(V4L2_HEADERS +) + SET(V4L2_SOURCES ${CURRENT_SOURCE_DIR}/V4L2Grabber.cpp ) +QT4_WRAP_CPP(V4L2_HEADERS_MOC ${V4L2_QT_HEADERS}) + add_library(v4l2-grabber ${V4L2_HEADERS} ${V4L2_SOURCES} + ${V4L2_QT_HEADERS} + ${V4L2_HEADERS_MOC} ) target_link_libraries(v4l2-grabber - hyperion-utils) + hyperion + ${QT_LIBRARIES} +) diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 09a36140..59bd0635 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -59,10 +59,9 @@ V4L2Grabber::V4L2Grabber( _frameDecimation(std::max(1, frameDecimation)), _horizontalPixelDecimation(std::max(1, horizontalPixelDecimation)), _verticalPixelDecimation(std::max(1, verticalPixelDecimation)), - _mode3D(MODE_NONE), + _mode3D(VIDEO_2D), _currentFrame(0), - _callback(nullptr), - _callbackArg(nullptr) + _streamNotifier(nullptr) { open_device(); init_device(videoStandard, input); @@ -82,66 +81,21 @@ void V4L2Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int crop _cropBottom = cropBottom; } -void V4L2Grabber::set3D(Mode3D mode) +void V4L2Grabber::set3D(VideoMode mode) { _mode3D = mode; } -void V4L2Grabber::setCallback(V4L2Grabber::ImageCallback callback, void *arg) -{ - _callback = callback; - _callbackArg = arg; -} - void V4L2Grabber::start() { + _streamNotifier->setEnabled(true); start_capturing(); } -void V4L2Grabber::capture(int frameCount) -{ - for (int count = 0; count < frameCount || frameCount < 0; ++count) - { - for (;;) - { - // the set of file descriptors for select - fd_set fds; - FD_ZERO(&fds); - FD_SET(_fileDescriptor, &fds); - - // timeout - struct timeval tv; - tv.tv_sec = 2; - tv.tv_usec = 0; - - // block until data is available - int r = select(_fileDescriptor + 1, &fds, NULL, NULL, &tv); - - if (-1 == r) - { - if (EINTR == errno) - continue; - throw_errno_exception("select"); - } - - if (0 == r) - { - throw_exception("select timeout"); - } - - if (read_frame()) - { - break; - } - - /* EAGAIN - continue select loop. */ - } - } -} - void V4L2Grabber::stop() { stop_capturing(); + _streamNotifier->setEnabled(false); } void V4L2Grabber::open_device() @@ -170,6 +124,10 @@ void V4L2Grabber::open_device() 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); + connect(_streamNotifier, SIGNAL(activated(int)), this, SLOT(read_frame())); } void V4L2Grabber::close_device() @@ -178,6 +136,12 @@ void V4L2Grabber::close_device() throw_errno_exception("close"); _fileDescriptor = -1; + + if (_streamNotifier != nullptr) + { + delete _streamNotifier; + _streamNotifier = nullptr; + } } void V4L2Grabber::init_read(unsigned int buffer_size) @@ -674,10 +638,10 @@ void V4L2Grabber::process_image(const uint8_t * data) switch (_mode3D) { - case MODE_3DSBS: + case VIDEO_3DSBS: width = _width/2; break; - case MODE_3DTAB: + case VIDEO_3DTAB: height = _height/2; break; default: @@ -717,10 +681,7 @@ void V4L2Grabber::process_image(const uint8_t * data) } } - if (_callback != nullptr) - { - (*_callback)(_callbackArg, image); - } + emit newFrame(image); } int V4L2Grabber::xioctl(int request, void *arg) diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt index db49e438..0cf387af 100644 --- a/src/hyperion-v4l2/CMakeLists.txt +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -14,16 +14,21 @@ include_directories( ${QT_INCLUDES} ) -set(Hyperion_V4L2_HEADERS - ProtoConnection.h +set(Hyperion_V4L2_QT_HEADERS ImageHandler.h + ScreenshotHandler.h +) + +set(Hyperion_V4L2_HEADERS VideoStandardParameter.h + ProtoConnection.h ) set(Hyperion_V4L2_SOURCES hyperion-v4l2.cpp ProtoConnection.cpp ImageHandler.cpp + ScreenshotHandler.cpp ) set(Hyperion_V4L2_PROTOS @@ -34,9 +39,13 @@ protobuf_generate_cpp(Hyperion_V4L2_PROTO_SRCS Hyperion_V4L2_PROTO_HDRS ${Hyperion_V4L2_PROTOS} ) +QT4_WRAP_CPP(Hyperion_V4L2_MOC_SOURCES ${Hyperion_V4L2_QT_HEADERS}) + add_executable(hyperion-v4l2 ${Hyperion_V4L2_HEADERS} ${Hyperion_V4L2_SOURCES} + ${Hyperion_V4L2_QT_HEADERS} + ${Hyperion_V4L2_MOC_SOURCES} ${Hyperion_V4L2_PROTO_SRCS} ${Hyperion_V4L2_PROTO_HDRS} ) @@ -48,5 +57,5 @@ target_link_libraries(hyperion-v4l2 hyperion-utils ${PROTOBUF_LIBRARIES} pthread - ${QT_LIBRARIES} + ${QT_LIBRARIES} ) diff --git a/src/hyperion-v4l2/ImageHandler.cpp b/src/hyperion-v4l2/ImageHandler.cpp index 49198953..afe7419c 100644 --- a/src/hyperion-v4l2/ImageHandler.cpp +++ b/src/hyperion-v4l2/ImageHandler.cpp @@ -10,6 +10,10 @@ ImageHandler::ImageHandler(const std::string & address, int priority, double sig _connection.setSkipReply(skipProtoReply); } +ImageHandler::~ImageHandler() +{ +} + void ImageHandler::receiveImage(const Image & image) { // check if we should do signal detection @@ -32,10 +36,3 @@ void ImageHandler::receiveImage(const Image & image) } } } - -void ImageHandler::imageCallback(void *arg, const Image &image) -{ - ImageHandler * handler = static_cast(arg); - handler->receiveImage(image); -} - diff --git a/src/hyperion-v4l2/ImageHandler.h b/src/hyperion-v4l2/ImageHandler.h index e0e0adbf..5658c750 100644 --- a/src/hyperion-v4l2/ImageHandler.h +++ b/src/hyperion-v4l2/ImageHandler.h @@ -1,24 +1,30 @@ +// Qt includes +#include + +// hyperion includes +#include +#include + // blackborder includes #include -// hyperion-v4l includes +// hyperion v4l2 includes #include "ProtoConnection.h" /// This class handles callbacks from the V4L2 grabber -class ImageHandler +class ImageHandler : public QObject { + Q_OBJECT + public: ImageHandler(const std::string & address, int priority, double signalThreshold, bool skipProtoReply); + virtual ~ImageHandler(); +public slots: /// Handle a single image /// @param image The image to process void receiveImage(const Image & image); - /// static function used to direct callbacks to a ImageHandler object - /// @param arg This should be an ImageHandler instance - /// @param image The image to process - static void imageCallback(void * arg, const Image & image); - private: /// Priority for calls to Hyperion const int _priority; diff --git a/src/hyperion-v4l2/ScreenshotHandler.cpp b/src/hyperion-v4l2/ScreenshotHandler.cpp new file mode 100644 index 00000000..0f9daaef --- /dev/null +++ b/src/hyperion-v4l2/ScreenshotHandler.cpp @@ -0,0 +1,25 @@ +// Qt includes +#include +#include + +// hyperion-v4l2 includes +#include "ScreenshotHandler.h" + +ScreenshotHandler::ScreenshotHandler(const std::string & filename) : + _filename(filename) +{ +} + +ScreenshotHandler::~ScreenshotHandler() +{ +} + +void ScreenshotHandler::receiveImage(const Image & image) +{ + // store as PNG + QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); + pngImage.save(_filename.c_str()); + + // Quit the application after the first image + QCoreApplication::quit(); +} diff --git a/src/hyperion-v4l2/ScreenshotHandler.h b/src/hyperion-v4l2/ScreenshotHandler.h new file mode 100644 index 00000000..3118c57d --- /dev/null +++ b/src/hyperion-v4l2/ScreenshotHandler.h @@ -0,0 +1,24 @@ +// Qt includes +#include + +// hyperionincludes +#include +#include + +/// This class handles callbacks from the V4L2 grabber +class ScreenshotHandler : public QObject +{ + Q_OBJECT + +public: + ScreenshotHandler(const std::string & filename); + virtual ~ScreenshotHandler(); + +public slots: + /// Handle a single image + /// @param image The image to process + void receiveImage(const Image & image); + +private: + const std::string _filename; +}; diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index 7f654c35..2268a033 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -4,7 +4,7 @@ #include // QT includes -#include +#include // getoptPlusPLus includes #include @@ -19,6 +19,7 @@ #include "ProtoConnection.h" #include "VideoStandardParameter.h" #include "ImageHandler.h" +#include "ScreenshotHandler.h" using namespace vlofgren; @@ -32,8 +33,11 @@ void saveScreenshot(void *, const Image & image) int main(int argc, char** argv) { + QCoreApplication app(argc, argv); + // force the locale setlocale(LC_ALL, "C"); + QLocale::setDefault(QLocale::c()); try { @@ -113,31 +117,30 @@ int main(int argc, char** argv) // set 3D mode if applicable if (arg3DSBS.isSet()) { - grabber.set3D(V4L2Grabber::MODE_3DSBS); + grabber.set3D(VIDEO_3DSBS); } else if (arg3DTAB.isSet()) { - grabber.set3D(V4L2Grabber::MODE_3DTAB); + grabber.set3D(VIDEO_3DTAB); } - // start the grabber - grabber.start(); - // run the grabber if (argScreenshot.isSet()) { - grabber.setCallback(&saveScreenshot, nullptr); - grabber.capture(1); + ScreenshotHandler handler("screenshot.png"); + QObject::connect(&grabber, SIGNAL(newFrame(Image)), &handler, SLOT(receiveImage(Image))); + grabber.start(); + QCoreApplication::exec(); + grabber.stop(); } else { ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSignalThreshold.getValue(), argSkipReply.isSet()); - grabber.setCallback(&ImageHandler::imageCallback, &handler); - grabber.capture(); + QObject::connect(&grabber, SIGNAL(newFrame(Image)), &handler, SLOT(receiveImage(Image))); + grabber.start(); + QCoreApplication::exec(); + grabber.stop(); } - - // stop the grabber - grabber.stop(); } catch (const std::runtime_error & e) {