From 2d7d18b34d027883546e6358e93c6ae9ba4f98f9 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Tue, 28 Jan 2014 08:47:16 +0000 Subject: [PATCH 01/14] Added coding framework for X11 Former-commit-id: 2915defe12e23d328716eade251849d40d098a8a --- CMakeLists.txt | 11 ++++--- src/CMakeLists.txt | 11 +++++-- src/hyperion-x11/CMakeLists.txt | 55 +++++++++++++++++++++++++++++++++ src/hyperion-x11/X11Grabber.cpp | 14 +++++++++ src/hyperion-x11/X11Grabber.h | 0 5 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 src/hyperion-x11/CMakeLists.txt create mode 100644 src/hyperion-x11/X11Grabber.cpp create mode 100644 src/hyperion-x11/X11Grabber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b6b9ed3c..36fc2e56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,18 @@ cmake_minimum_required(VERSION 2.8) #set(CMAKE_TOOLCHAIN_FILE /opt/raspberrypi/Toolchain-RaspberryPi.cmake) # set the build options -option (ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ON) -option (ENABLE_SPIDEV "Enable the SPIDEV device" ON) - +option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ON) message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX}) + +option(ENABLE_SPIDEV "Enable the SPIDEV device" ON) message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV}) -option (ENABLE_V4L2 "Enable the V4L2 grabber" ON) +option(ENABLE_V4L2 "Enable the V4L2 grabber" ON) message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2}) +option(ENABLE_X11 "Enable the X11 grabber" ON) +message(STATUS "ENABLE_X11 = " ${ENABLE_X11}) + # Createt the configuration file # configure a header file to pass some of the CMake settings # to the source code diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b06b4ee7..16c88d77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,12 @@ add_subdirectory(hyperiond) add_subdirectory(hyperion-remote) -if (ENABLE_V4L2) + +# Add the 'Video 4 Linux' grabber if it is enabled +if(ENABLE_V4L2) add_subdirectory(hyperion-v4l2) -endif (ENABLE_V4L2) +endif(ENABLE_V4L2) + +# Add the X11 grabber if it is enabled +if(ENABLE_X11) + add_subdirectory(hyperion-x11) +endif(ENABLE_X11) diff --git a/src/hyperion-x11/CMakeLists.txt b/src/hyperion-x11/CMakeLists.txt new file mode 100644 index 00000000..5b4fd821 --- /dev/null +++ b/src/hyperion-x11/CMakeLists.txt @@ -0,0 +1,55 @@ +# Configure minimum CMAKE version +cmake_minimum_required(VERSION 2.8) + +# Set the project name +project(hyperion-x11) + +# add protocol buffers +find_package(Protobuf REQUIRED) + +# find Qt4 +find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${PROTOBUF_INCLUDE_DIRS} + ${QT_INCLUDES} +) + +set(Hyperion_X11_HEADERS + X11Grabber.h + ../hyperion-v4l2/ProtoConnection.h +) + +set(Hyperion_X11_SOURCES + X11Grabber.cpp + ../hyperion-v4l2/ProtoConnection.cpp +) + +set(Hyperion_X11_PROTOS + ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/protoserver/message.proto +) + +protobuf_generate_cpp(Hyperion_X11_PROTO_SRCS Hyperion_X11_PROTO_HDRS + ${Hyperion_X11_PROTOS} +) + +add_executable(hyperion-x11 + ${Hyperion_X11_HEADERS} + ${Hyperion_X11_SOURCES} + ${Hyperion_X11_PROTO_SRCS} + ${Hyperion_X11_PROTO_HDRS} +) + +target_link_libraries(hyperion-x11 + getoptPlusPlus + blackborder + hyperion-utils + ${PROTOBUF_LIBRARIES} + pthread +) + +qt4_use_modules(hyperion-x11 + Core + Gui + Network) diff --git a/src/hyperion-x11/X11Grabber.cpp b/src/hyperion-x11/X11Grabber.cpp new file mode 100644 index 00000000..d1331743 --- /dev/null +++ b/src/hyperion-x11/X11Grabber.cpp @@ -0,0 +1,14 @@ + +#include + +int main(int argc, char ** argv) +{ + Display * dspl; + Drawable drwbl; + int x = 0; + int y = 0; + int width = 0; + int height = 0; + + XImage * xImage = XGetImage(dspl, drwbl, x, y, width, height, AllPlanes, ZPixmap); +} diff --git a/src/hyperion-x11/X11Grabber.h b/src/hyperion-x11/X11Grabber.h new file mode 100644 index 00000000..e69de29b From 8e446bf8835c1126ab39c1d45b0d9d1466d7d726 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Thu, 30 Jan 2014 12:35:29 +0000 Subject: [PATCH 02/14] Implemented the X11-grabber (based on v4l code). Former-commit-id: 17ad66ff7e2a1e3fae2d853e5360f791934b6563 --- src/hyperion-x11/CMakeLists.txt | 19 ++++- src/hyperion-x11/ProtoWrapper.cpp | 17 +++++ src/hyperion-x11/ProtoWrapper.h | 23 ++++++ src/hyperion-x11/X11Grabber.cpp | 121 +++++++++++++++++++++++++++--- src/hyperion-x11/X11Grabber.h | 37 +++++++++ src/hyperion-x11/X11Wrapper.cpp | 33 ++++++++ src/hyperion-x11/X11Wrapper.h | 39 ++++++++++ src/hyperion-x11/hyperion-x11.cpp | 90 ++++++++++++++++++++++ 8 files changed, 366 insertions(+), 13 deletions(-) create mode 100644 src/hyperion-x11/ProtoWrapper.cpp create mode 100644 src/hyperion-x11/ProtoWrapper.h create mode 100644 src/hyperion-x11/X11Wrapper.cpp create mode 100644 src/hyperion-x11/X11Wrapper.h create mode 100644 src/hyperion-x11/hyperion-x11.cpp diff --git a/src/hyperion-x11/CMakeLists.txt b/src/hyperion-x11/CMakeLists.txt index 5b4fd821..43cc9a1d 100644 --- a/src/hyperion-x11/CMakeLists.txt +++ b/src/hyperion-x11/CMakeLists.txt @@ -10,19 +10,30 @@ find_package(Protobuf REQUIRED) # find Qt4 find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) +# Find X11 +find_package(X11 REQUIRED) + include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${PROTOBUF_INCLUDE_DIRS} ${QT_INCLUDES} + ${X11_INCLUDES} ) +set(Hyperion_X11_QT_HEADERS + ProtoWrapper.h + X11Wrapper.h) + set(Hyperion_X11_HEADERS X11Grabber.h ../hyperion-v4l2/ProtoConnection.h ) set(Hyperion_X11_SOURCES + hyperion-x11.cpp + ProtoWrapper.cpp X11Grabber.cpp + X11Wrapper.cpp ../hyperion-v4l2/ProtoConnection.cpp ) @@ -30,15 +41,16 @@ set(Hyperion_X11_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/protoserver/message.proto ) -protobuf_generate_cpp(Hyperion_X11_PROTO_SRCS Hyperion_X11_PROTO_HDRS - ${Hyperion_X11_PROTOS} -) +QT4_WRAP_CPP(Hyperion_X11_HEADERS_MOC ${Hyperion_X11_QT_HEADERS}) + +protobuf_generate_cpp(Hyperion_X11_PROTO_SRCS Hyperion_X11_PROTO_HDRS ${Hyperion_X11_PROTOS}) add_executable(hyperion-x11 ${Hyperion_X11_HEADERS} ${Hyperion_X11_SOURCES} ${Hyperion_X11_PROTO_SRCS} ${Hyperion_X11_PROTO_HDRS} + ${Hyperion_X11_HEADERS_MOC} ) target_link_libraries(hyperion-x11 @@ -46,6 +58,7 @@ target_link_libraries(hyperion-x11 blackborder hyperion-utils ${PROTOBUF_LIBRARIES} + ${X11_LIBRARIES} pthread ) diff --git a/src/hyperion-x11/ProtoWrapper.cpp b/src/hyperion-x11/ProtoWrapper.cpp new file mode 100644 index 00000000..89710257 --- /dev/null +++ b/src/hyperion-x11/ProtoWrapper.cpp @@ -0,0 +1,17 @@ + +// +#include "ProtoWrapper.h" + +ProtoWrapper::ProtoWrapper(const std::string & protoAddress, const bool skipReply) : + _priority(200), + _duration_ms(2000), + _connection(protoAddress) +{ + _connection.setSkipReply(skipReply); +} + +void ProtoWrapper::process(const Image & image) +{ + std::cout << "Attempt to send screenshot" << std::endl; + _connection.setImage(image, _priority, _duration_ms); +} diff --git a/src/hyperion-x11/ProtoWrapper.h b/src/hyperion-x11/ProtoWrapper.h new file mode 100644 index 00000000..4d184868 --- /dev/null +++ b/src/hyperion-x11/ProtoWrapper.h @@ -0,0 +1,23 @@ + +// QT includes +#include + + +#include "../hyperion-v4l2/ProtoConnection.h" + +class ProtoWrapper : public QObject +{ + Q_OBJECT +public: + ProtoWrapper(const std::string & protoAddress, const bool skipReply); + +public slots: + void process(const Image & image); + +private: + + int _priority; + int _duration_ms; + + ProtoConnection _connection; +}; diff --git a/src/hyperion-x11/X11Grabber.cpp b/src/hyperion-x11/X11Grabber.cpp index d1331743..cd6d0fda 100644 --- a/src/hyperion-x11/X11Grabber.cpp +++ b/src/hyperion-x11/X11Grabber.cpp @@ -1,14 +1,115 @@ +// STL includes +#include +#include -#include +// X11 includes +#include -int main(int argc, char ** argv) +// X11Grabber includes +#include "X11Grabber.h" + +X11Grabber::X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : + _pixelDecimation(pixelDecimation), + _cropWidth(cropHorizontal), + _cropHeight(cropVertical), + _x11Display(nullptr), + _screenWidth(0), + _screenHeight(0), + _image(0,0) { - Display * dspl; - Drawable drwbl; - int x = 0; - int y = 0; - int width = 0; - int height = 0; - - XImage * xImage = XGetImage(dspl, drwbl, x, y, width, height, AllPlanes, ZPixmap); + // empty +} + +X11Grabber::~X11Grabber() +{ + if (_x11Display != nullptr) + { + XCloseDisplay(_x11Display); + } +} + +int X11Grabber::open() +{ + const char * display_name = nullptr; + _x11Display = XOpenDisplay(display_name); + + if (_x11Display == nullptr) + { + std::cerr << "Failed to open the default X11-display" << std::endl; + return -1; + } + + return 0; +} + +Image & X11Grabber::grab() +{ + if (_x11Display == nullptr) + { + open(); + } + + updateScreenDimensions(); + + const int croppedWidth = _screenWidth - 2*_cropWidth; + const int croppedHeight = _screenHeight - 2*_cropHeight; + + // Capture the current screen + XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropWidth, _cropHeight, croppedWidth, croppedHeight, AllPlanes, ZPixmap); + if (xImage == nullptr) + { + std::cerr << "Grab failed" << std::endl; + return _image; + } + + // Copy the capture XImage to the local image (and apply required decimation) + ColorRgb * outputPtr = _image.memptr(); + for (int iY=(_pixelDecimation/2); iYred = uint8_t((pixel >> 16) & 0xff); + outputPtr->green = uint8_t((pixel >> 8) & 0xff); + outputPtr->blue = uint8_t((pixel >> 0) & 0xff); + + // Move to the next output pixel + ++outputPtr; + } + } + // Cleanup allocated resources of the X11 grab + XDestroyImage(xImage); + + return _image; +} + +int X11Grabber::updateScreenDimensions() +{ + XWindowAttributes window_attributes_return; + const Status status = XGetWindowAttributes(_x11Display, DefaultRootWindow(_x11Display), &window_attributes_return); + if (status == 0) + { + std::cerr << "Failed to obtain window attributes" << std::endl; + return -1; + } + + if (_screenWidth == unsigned(window_attributes_return.width) && _screenHeight == unsigned(window_attributes_return.height)) + { + // No update required + return 0; + } + std::cout << "Update of screen resolution: [" << _screenWidth << "x" << _screenHeight <<"] => "; + _screenWidth = window_attributes_return.width; + _screenHeight = window_attributes_return.height; + 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; + _image.resize(width, height); + + return 0; } diff --git a/src/hyperion-x11/X11Grabber.h b/src/hyperion-x11/X11Grabber.h index e69de29b..21d36c47 100644 --- a/src/hyperion-x11/X11Grabber.h +++ b/src/hyperion-x11/X11Grabber.h @@ -0,0 +1,37 @@ + +// Hyperion-utils includes +#include +#include + +// X11 includes +#include + +class X11Grabber +{ +public: + + X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); + + virtual ~X11Grabber(); + + int open(); + + Image & grab(); + +private: + + const unsigned _pixelDecimation; + + const unsigned _cropWidth; + const unsigned _cropHeight; + + /// Reference to the X11 display (nullptr if not opened) + Display * _x11Display; + + unsigned _screenWidth; + unsigned _screenHeight; + + Image _image; + + int updateScreenDimensions(); +}; diff --git a/src/hyperion-x11/X11Wrapper.cpp b/src/hyperion-x11/X11Wrapper.cpp new file mode 100644 index 00000000..5bf4a97f --- /dev/null +++ b/src/hyperion-x11/X11Wrapper.cpp @@ -0,0 +1,33 @@ + +// Hyperion-X11 includes +#include "X11Wrapper.h" + +X11Wrapper::X11Wrapper(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : + _timer(this), + _grabber(cropHorizontal, cropVertical, pixelDecimation) +{ + // Connect capturing to the timeout signal of the timer + connect(&_timer, SIGNAL(timeout()), this, SLOT(capture())); +} + +const Image & X11Wrapper::getScreenshot() +{ + const Image & screenshot = _grabber.grab(); + return screenshot; +} + +void X11Wrapper::start() +{ + _timer.start(200); +} + +void X11Wrapper::stop() +{ + _timer.stop(); +} + +void X11Wrapper::capture() +{ + const Image & screenshot = _grabber.grab(); + emit sig_screenshot(screenshot); +} diff --git a/src/hyperion-x11/X11Wrapper.h b/src/hyperion-x11/X11Wrapper.h new file mode 100644 index 00000000..405cbce9 --- /dev/null +++ b/src/hyperion-x11/X11Wrapper.h @@ -0,0 +1,39 @@ + +// QT includes +#include + +// Hyperion-X11 includes +#include "X11Grabber.h" + +class X11Wrapper : public QObject +{ + Q_OBJECT +public: + X11Wrapper(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); + + const Image & getScreenshot(); + + /// + /// Starts the timed capturing of screenshots + /// + void start(); + + void stop(); + +signals: + void sig_screenshot(const Image & screenshot); + +private slots: + /// + /// Performs a single screenshot capture and publishes the capture screenshot on the screenshot + /// signal. + /// + void capture(); + +private: + /// The QT timer to generate capture-publish events + QTimer _timer; + + /// The grabber for creating screenshots + X11Grabber _grabber; +}; diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp new file mode 100644 index 00000000..ef9b5ab4 --- /dev/null +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -0,0 +1,90 @@ + +// QT includes +#include +#include + +// getoptPlusPLus includes +#include + +#include "ProtoWrapper.h" +#include "X11Wrapper.h" + +using namespace vlofgren; + +// save the image as screenshot +void saveScreenshot(const char * filename, 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); +} + +int main(int argc, char ** argv) +{ + QCoreApplication app(argc, argv); + + try + { + // create the option parser and initialize all parameters + OptionsParser optionParser("X11 capture application for Hyperion"); + ParameterSet & parameters = optionParser.getParameters(); + + IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]"); + IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]"); + IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=1]"); + SwitchParameter<> & argScreenshot = parameters.add> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); + StringParameter & argAddress = parameters.add ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); + IntParameter & argPriority = parameters.add ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); + SwitchParameter<> & argSkipReply = parameters.add> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); + SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); + + // set defaults + argCropWidth.setDefault(0); + argCropHeight.setDefault(0); + argSizeDecimation.setDefault(1); + argAddress.setDefault("127.0.0.1:19445"); + argPriority.setDefault(800); + + // parse all options + optionParser.parse(argc, const_cast(argv)); + + // check if we need to display the usage. exit if we do. + if (argHelp.isSet()) + { + optionParser.usage(); + return 0; + } + + // Create the X11 grabbing stuff + X11Wrapper x11Wrapper(argCropWidth.getValue(), argCropHeight.getValue(), argSizeDecimation.getValue()); + + if (argScreenshot.isSet()) + { + // Capture a single screenshot and finish + const Image & screenshot = x11Wrapper.getScreenshot(); + saveScreenshot("screenshot.png", screenshot); + } + else + { + // Create the Proto-connection with hyperiond + ProtoWrapper protoWrapper("127.0.0.1:19445", argSkipReply.isSet()); + + // Connect the screen capturing to the proto processing + QObject::connect(&x11Wrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(process(Image))); + + // Start the capturing + x11Wrapper.start(); + + // Start the application + app.exec(); + } + } + catch (const std::runtime_error & e) + { + // An error occured. Display error and quit + std::cerr << e.what() << std::endl; + return -1; + } + + return 0; +} From 2fa46fdd982ce4817935d313dec3b6a463bb5862 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Sun, 2 Feb 2014 16:58:46 +0100 Subject: [PATCH 03/14] Fixed correct addres configuration Former-commit-id: b001df5968f009cf9686a3741170790d82eaba1f --- src/hyperion-x11/hyperion-x11.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index ef9b5ab4..6e6ee9ba 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -67,7 +67,7 @@ int main(int argc, char ** argv) else { // Create the Proto-connection with hyperiond - ProtoWrapper protoWrapper("127.0.0.1:19445", argSkipReply.isSet()); + ProtoWrapper protoWrapper(argAddress.getValue(), argSkipReply.isSet()); // Connect the screen capturing to the proto processing QObject::connect(&x11Wrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(process(Image))); From 591a071856bf2df439c72248c7329f5a8f70e9c6 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Tue, 25 Feb 2014 22:08:26 +0100 Subject: [PATCH 04/14] Fixed missing include Former-commit-id: 53dab1a29d04b9cf083461caa20249b78600440d --- libsrc/hyperion/ImageProcessorFactory.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsrc/hyperion/ImageProcessorFactory.cpp b/libsrc/hyperion/ImageProcessorFactory.cpp index 70987845..a47e532f 100644 --- a/libsrc/hyperion/ImageProcessorFactory.cpp +++ b/libsrc/hyperion/ImageProcessorFactory.cpp @@ -1,4 +1,7 @@ +// STL includes +#include + // Hyperion includes #include #include From 6b63b57f17f299f36c67dc69552d825f80f8532f Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Tue, 25 Feb 2014 22:08:54 +0100 Subject: [PATCH 05/14] Added performance test for x11 grabbing Former-commit-id: b8c60cf9984c6961675b41002bed40d251bff9fa --- test/CMakeLists.txt | 8 +++ test/TestX11Performance.cpp | 139 ++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 test/TestX11Performance.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bd46d059..8bf17456 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -53,6 +53,14 @@ add_executable(test_qtscreenshot TestQtScreenshot.cpp) target_link_libraries(test_qtscreenshot ${QT_LIBRARIES}) +if(ENABLE_X11) + # Find X11 + find_package(X11 REQUIRED) + + add_executable(test_x11performance TestX11Performance.cpp) + target_link_libraries(test_x11performance ${X11_LIBRARIES} ${QT_LIBRARIES}) +endif(ENABLE_X11) + add_executable(determineWs2811Timing DetermineWs2811Timing.cpp) add_executable(test_rs232highspeed diff --git a/test/TestX11Performance.cpp b/test/TestX11Performance.cpp new file mode 100644 index 00000000..9b7fb402 --- /dev/null +++ b/test/TestX11Performance.cpp @@ -0,0 +1,139 @@ + +// X11 includes +#include +#include + +#include + +#include +#include + +void foo_1(int pixelDecimation) +{ + int cropWidth = 0; + int cropHeight = 0; + + Image image(64, 64); + + /// Reference to the X11 display (nullptr if not opened) + Display * x11Display; + + const char * display_name = nullptr; + x11Display = XOpenDisplay(display_name); + + std::cout << "Opened display: " << x11Display << std::endl; + + XWindowAttributes window_attributes_return; + XGetWindowAttributes(x11Display, DefaultRootWindow(x11Display), &window_attributes_return); + + int screenWidth = window_attributes_return.width; + int screenHeight = window_attributes_return.height; + 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; + image.resize(width, height); + + const int croppedWidth = screenWidth - 2*cropWidth; + const int croppedHeight = screenHeight - 2*cropHeight; + + QElapsedTimer timer; + timer.start(); + + XImage * xImage = XGetImage(x11Display, DefaultRootWindow(x11Display), cropWidth, cropHeight, croppedWidth, croppedHeight, AllPlanes, ZPixmap); + + std::cout << "Captured image: " << xImage << std::endl; + + // Copy the capture XImage to the local image (and apply required decimation) + ColorRgb * outputPtr = image.memptr(); + for (int iY=(pixelDecimation/2); iYred = uint8_t((pixel >> 16) & 0xff); + outputPtr->green = uint8_t((pixel >> 8) & 0xff); + outputPtr->blue = uint8_t((pixel >> 0) & 0xff); + + // Move to the next output pixel + ++outputPtr; + } + } + + // Cleanup allocated resources of the X11 grab + XDestroyImage(xImage); + + std::cout << "Time required: " << timer.elapsed() << " us" << std::endl; + + XCloseDisplay(x11Display); +} + +void foo_2(int pixelDecimation) +{ + int cropWidth = 0; + int cropHeight = 0; + + Image image(64, 64); + + /// Reference to the X11 display (nullptr if not opened) + Display * x11Display; + + const char * display_name = nullptr; + x11Display = XOpenDisplay(display_name); + + XWindowAttributes window_attributes_return; + XGetWindowAttributes(x11Display, DefaultRootWindow(x11Display), &window_attributes_return); + + int screenWidth = window_attributes_return.width; + int screenHeight = window_attributes_return.height; + 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; + image.resize(width, height); + + const int croppedWidth = screenWidth - 2*cropWidth; + const int croppedHeight = screenHeight - 2*cropHeight; + + QElapsedTimer timer; + timer.start(); + + // Copy the capture XImage to the local image (and apply required decimation) + ColorRgb * outputPtr = image.memptr(); + for (int iY=(pixelDecimation/2); iYred = uint8_t((pixel >> 16) & 0xff); + outputPtr->green = uint8_t((pixel >> 8) & 0xff); + outputPtr->blue = uint8_t((pixel >> 0) & 0xff); + + // Move to the next output pixel + ++outputPtr; + + // Cleanup allocated resources of the X11 grab + XDestroyImage(xImage); + } + } + std::cout << "Time required: " << timer.elapsed() << " us" << std::endl; + + + XCloseDisplay(x11Display); +} + +int main() +{ + foo_1(10); + foo_2(10); + return 0; +} From 5f457fd9e3eb32b1beebc26921e4963e1935742c Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Thu, 6 Mar 2014 21:01:14 +0100 Subject: [PATCH 06/14] Added qt screenshot test Former-commit-id: c28e098ded780a6597952c83007fea1fe8da36fa --- test/TestQtScreenshot.cpp | 56 +++++++++++++++++++++++++++++++++++-- test/TestX11Performance.cpp | 4 +-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/test/TestQtScreenshot.cpp b/test/TestQtScreenshot.cpp index 27e03b0f..d2bed9e7 100644 --- a/test/TestQtScreenshot.cpp +++ b/test/TestQtScreenshot.cpp @@ -6,15 +6,67 @@ #include #include #include +#include +#include + +#include + +// Utils includes +#include +#include + +void createScreenshot(const int cropHorizontal, const int cropVertical, const int decimation, Image & image) +{ + // Create the full size screenshot + const QRect screenSize = QApplication::desktop()->screenGeometry(); + const int croppedWidth = screenSize.width() - 2*cropVertical; + const int croppedHeight = screenSize.height() - 2*cropHorizontal; + const QPixmap fullSizeScreenshot = QPixmap::grabWindow(QApplication::desktop()->winId(), cropVertical, cropHorizontal, croppedWidth, croppedHeight); + + // Scale the screenshot to the required size + const int width = fullSizeScreenshot.width()/decimation; + const int height = fullSizeScreenshot.height()/decimation; + const QPixmap scaledScreenshot = fullSizeScreenshot.scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation); + + // Convert the QPixmap to QImage in order to get out RGB values + const QImage qImage = scaledScreenshot.toImage(); + + // Make sure that the output image has the right size + image.resize(width, height); + + // Copy the data into the output image + for (int y=0; y> 16; + outPixel.green = (inPixel & 0x0000ff00) >> 8; + outPixel.blue = (inPixel & 0x000000ff); + } + } +} int main(int argc, char** argv) { + int decimation = 10; QApplication app(argc, argv); + QElapsedTimer timer; - QPixmap originalPixmap = QPixmap::grabWindow(QApplication::desktop()->winId()); + Image screenshot(64,64); - std::cout << "Grabbed image: [" << originalPixmap.width() << "; " << originalPixmap.height() << "]" << std::endl; + int loopCnt = 100; + timer.start(); + for (int i=0; i Date: Fri, 21 Nov 2014 21:24:33 +0100 Subject: [PATCH 07/14] Refactor V4L2 and X11 grabbers to share more code Former-commit-id: 46176e53d1acf39f9bd0c0ecbb8e5fb5ab4d45be --- .../grabber}/X11Grabber.h | 0 include/protoserver/ProtoConnection.h | 102 +++++++ include/protoserver/ProtoConnectionWrapper.h | 34 +++ libsrc/grabber/CMakeLists.txt | 5 +- libsrc/grabber/x11/CMakeLists.txt | 37 +++ libsrc/grabber/x11/X11Grabber.cpp | 115 ++++++++ libsrc/protoserver/CMakeLists.txt | 8 +- libsrc/protoserver/ProtoConnection.cpp | 188 +++++++++++++ libsrc/protoserver/ProtoConnectionWrapper.cpp | 19 ++ src/hyperion-v4l2/CMakeLists.txt | 21 +- src/hyperion-v4l2/ImageHandler.cpp | 18 -- src/hyperion-v4l2/ImageHandler.h | 31 --- src/hyperion-v4l2/ProtoConnection.cpp | 188 ------------- src/hyperion-v4l2/ProtoConnection.h | 102 ------- src/hyperion-v4l2/hyperion-v4l2.cpp | 250 +++++++++--------- src/hyperion-x11/CMakeLists.txt | 23 +- src/hyperion-x11/ProtoWrapper.cpp | 17 -- src/hyperion-x11/ProtoWrapper.h | 23 -- src/hyperion-x11/X11Grabber.cpp | 115 -------- src/hyperion-x11/X11Wrapper.cpp | 20 +- src/hyperion-x11/X11Wrapper.h | 38 +-- src/hyperion-x11/hyperion-x11.cpp | 118 ++++----- 22 files changed, 724 insertions(+), 748 deletions(-) rename {src/hyperion-x11 => include/grabber}/X11Grabber.h (100%) create mode 100644 include/protoserver/ProtoConnection.h create mode 100644 include/protoserver/ProtoConnectionWrapper.h create mode 100644 libsrc/grabber/x11/CMakeLists.txt create mode 100644 libsrc/grabber/x11/X11Grabber.cpp create mode 100644 libsrc/protoserver/ProtoConnection.cpp create mode 100644 libsrc/protoserver/ProtoConnectionWrapper.cpp delete mode 100644 src/hyperion-v4l2/ImageHandler.cpp delete mode 100644 src/hyperion-v4l2/ImageHandler.h delete mode 100644 src/hyperion-v4l2/ProtoConnection.cpp delete mode 100644 src/hyperion-v4l2/ProtoConnection.h delete mode 100644 src/hyperion-x11/ProtoWrapper.cpp delete mode 100644 src/hyperion-x11/ProtoWrapper.h delete mode 100644 src/hyperion-x11/X11Grabber.cpp diff --git a/src/hyperion-x11/X11Grabber.h b/include/grabber/X11Grabber.h similarity index 100% rename from src/hyperion-x11/X11Grabber.h rename to include/grabber/X11Grabber.h diff --git a/include/protoserver/ProtoConnection.h b/include/protoserver/ProtoConnection.h new file mode 100644 index 00000000..bb41e1b7 --- /dev/null +++ b/include/protoserver/ProtoConnection.h @@ -0,0 +1,102 @@ +#pragma once + +// stl includes +#include + +// Qt includes +#include +#include +#include +#include + +// hyperion util +#include +#include + +// jsoncpp includes +#include + +/// +/// Connection class to setup an connection to the hyperion server and execute commands +/// +class ProtoConnection +{ +public: + /// + /// Constructor + /// + /// @param address The address of the Hyperion server (for example "192.168.0.32:19444) + /// + ProtoConnection(const std::string & address); + + /// + /// Destructor + /// + ~ProtoConnection(); + + /// Do not read reply messages from Hyperion if set to true + void setSkipReply(bool skip); + + /// + /// Set all leds to the specified color + /// + /// @param color The color + /// @param priority The priority + /// @param duration The duration in milliseconds + /// + void setColor(const ColorRgb & color, int priority, int duration = 1); + + /// + /// Set the leds according to the given image (assume the image is stretched to the display size) + /// + /// @param image The image + /// @param priority The priority + /// @param duration The duration in milliseconds + /// + void setImage(const Image & image, int priority, int duration = -1); + + /// + /// Clear the given priority channel + /// + /// @param priority The priority + /// + void clear(int priority); + + /// + /// Clear all priority channels + /// + void clearAll(); + +private: + /// Try to connect to the Hyperion host + void connectToHost(); + + /// + /// Send a command message and receive its reply + /// + /// @param message The message to send + /// + void sendMessage(const proto::HyperionRequest & message); + + /// + /// Parse a reply message + /// + /// @param reply The received reply + /// + /// @return true if the reply indicates success + /// + bool parseReply(const proto::HyperionReply & reply); + +private: + /// The TCP-Socket with the connection to the server + QTcpSocket _socket; + + /// Host address + QString _host; + + /// Host port + uint16_t _port; + + /// Skip receiving reply messages from Hyperion if set + bool _skipReply; +}; diff --git a/include/protoserver/ProtoConnectionWrapper.h b/include/protoserver/ProtoConnectionWrapper.h new file mode 100644 index 00000000..f5b072ea --- /dev/null +++ b/include/protoserver/ProtoConnectionWrapper.h @@ -0,0 +1,34 @@ +// Qt includes +#include + +// hyperion includes +#include +#include + +// hyperion proto includes +#include "protoserver/ProtoConnection.h" + +/// This class handles callbacks from the V4L2 grabber +class ProtoConnectionWrapper : public QObject +{ + Q_OBJECT + +public: + ProtoConnectionWrapper(const std::string & address, int priority, int duration_ms, bool skipProtoReply); + virtual ~ProtoConnectionWrapper(); + +public slots: + /// Handle a single image + /// @param image The image to process + void receiveImage(const Image & image); + +private: + /// Priority for calls to Hyperion + const int _priority; + + /// Duration for color calls to Hyperion + const int _duration_ms; + + /// Hyperion proto connection object + ProtoConnection _connection; +}; diff --git a/libsrc/grabber/CMakeLists.txt b/libsrc/grabber/CMakeLists.txt index 322a5a98..85279243 100644 --- a/libsrc/grabber/CMakeLists.txt +++ b/libsrc/grabber/CMakeLists.txt @@ -1,4 +1,3 @@ - if (ENABLE_DISPMANX) add_subdirectory(dispmanx) endif (ENABLE_DISPMANX) @@ -6,3 +5,7 @@ endif (ENABLE_DISPMANX) if (ENABLE_V4L2) add_subdirectory(v4l2) endif (ENABLE_V4L2) + +if (ENABLE_X11) + add_subdirectory(x11) +endif() diff --git a/libsrc/grabber/x11/CMakeLists.txt b/libsrc/grabber/x11/CMakeLists.txt new file mode 100644 index 00000000..869fb4bf --- /dev/null +++ b/libsrc/grabber/x11/CMakeLists.txt @@ -0,0 +1,37 @@ +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/x11) + +# Find X11 +find_package(X11 REQUIRED) + +include_directories( + ${QT_INCLUDES} + ${X11_INCLUDES} +) + +SET(X11_QT_HEADERS + ${CURRENT_HEADER_DIR}/X11Grabber.h +) + +SET(X11_HEADERS + ${CURRENT_HEADER_DIR}/X11Grabber.h +) + +SET(X11_SOURCES + ${CURRENT_SOURCE_DIR}/X11Grabber.cpp +) + +QT4_WRAP_CPP(X11_HEADERS_MOC ${X11_QT_HEADERS}) + +add_library(x11-grabber + ${X11_HEADERS} + ${X11_SOURCES} + ${X11_QT_HEADERS} + ${X11_HEADERS_MOC} +) + +target_link_libraries(x11-grabber + hyperion + ${QT_LIBRARIES} +) diff --git a/libsrc/grabber/x11/X11Grabber.cpp b/libsrc/grabber/x11/X11Grabber.cpp new file mode 100644 index 00000000..93f6e434 --- /dev/null +++ b/libsrc/grabber/x11/X11Grabber.cpp @@ -0,0 +1,115 @@ +// STL includes +#include +#include + +// X11 includes +#include + +// X11Grabber includes +#include + +X11Grabber::X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : + _pixelDecimation(pixelDecimation), + _cropWidth(cropHorizontal), + _cropHeight(cropVertical), + _x11Display(nullptr), + _screenWidth(0), + _screenHeight(0), + _image(0,0) +{ + // empty +} + +X11Grabber::~X11Grabber() +{ + if (_x11Display != nullptr) + { + XCloseDisplay(_x11Display); + } +} + +int X11Grabber::open() +{ + const char * display_name = nullptr; + _x11Display = XOpenDisplay(display_name); + + if (_x11Display == nullptr) + { + std::cerr << "Failed to open the default X11-display" << std::endl; + return -1; + } + + return 0; +} + +Image & X11Grabber::grab() +{ + if (_x11Display == nullptr) + { + open(); + } + + updateScreenDimensions(); + + const int croppedWidth = _screenWidth - 2*_cropWidth; + const int croppedHeight = _screenHeight - 2*_cropHeight; + + // Capture the current screen + XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropWidth, _cropHeight, croppedWidth, croppedHeight, AllPlanes, ZPixmap); + if (xImage == nullptr) + { + std::cerr << "Grab failed" << std::endl; + return _image; + } + + // Copy the capture XImage to the local image (and apply required decimation) + ColorRgb * outputPtr = _image.memptr(); + for (int iY=(_pixelDecimation/2); iYred = uint8_t((pixel >> 16) & 0xff); + outputPtr->green = uint8_t((pixel >> 8) & 0xff); + outputPtr->blue = uint8_t((pixel >> 0) & 0xff); + + // Move to the next output pixel + ++outputPtr; + } + } + // Cleanup allocated resources of the X11 grab + XDestroyImage(xImage); + + return _image; +} + +int X11Grabber::updateScreenDimensions() +{ + XWindowAttributes window_attributes_return; + const Status status = XGetWindowAttributes(_x11Display, DefaultRootWindow(_x11Display), &window_attributes_return); + if (status == 0) + { + std::cerr << "Failed to obtain window attributes" << std::endl; + return -1; + } + + if (_screenWidth == unsigned(window_attributes_return.width) && _screenHeight == unsigned(window_attributes_return.height)) + { + // No update required + return 0; + } + std::cout << "Update of screen resolution: [" << _screenWidth << "x" << _screenHeight <<"] => "; + _screenWidth = window_attributes_return.width; + _screenHeight = window_attributes_return.height; + 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; + _image.resize(width, height); + + return 0; +} diff --git a/libsrc/protoserver/CMakeLists.txt b/libsrc/protoserver/CMakeLists.txt index 9e1992a3..f13c6a83 100644 --- a/libsrc/protoserver/CMakeLists.txt +++ b/libsrc/protoserver/CMakeLists.txt @@ -10,7 +10,9 @@ include_directories( # Group the headers that go through the MOC compiler set(ProtoServer_QT_HEADERS ${CURRENT_HEADER_DIR}/ProtoServer.h + ${CURRENT_HEADER_DIR}/ProtoConnection.h ${CURRENT_SOURCE_DIR}/ProtoClientConnection.h + ${CURRENT_HEADER_DIR}/ProtoConnectionWrapper.h ) set(ProtoServer_HEADERS @@ -19,6 +21,8 @@ set(ProtoServer_HEADERS set(ProtoServer_SOURCES ${CURRENT_SOURCE_DIR}/ProtoServer.cpp ${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp + ${CURRENT_SOURCE_DIR}/ProtoConnection.cpp + ${CURRENT_SOURCE_DIR}/ProtoConnectionWrapper.cpp ) set(ProtoServer_PROTOS @@ -44,5 +48,5 @@ add_library(protoserver target_link_libraries(protoserver hyperion hyperion-utils - ${PROTOBUF_LIBRARIES} - ${QT_LIBRARIES}) + ${PROTOBUF_LIBRARIES} + ${QT_LIBRARIES}) diff --git a/libsrc/protoserver/ProtoConnection.cpp b/libsrc/protoserver/ProtoConnection.cpp new file mode 100644 index 00000000..d7a6037e --- /dev/null +++ b/libsrc/protoserver/ProtoConnection.cpp @@ -0,0 +1,188 @@ +// stl includes +#include + +// Qt includes +#include + +// protoserver includes +#include "protoserver/ProtoConnection.h" + +ProtoConnection::ProtoConnection(const std::string & a) : + _socket(), + _skipReply(false) +{ + QString address(a.c_str()); + QStringList parts = address.split(":"); + if (parts.size() != 2) + { + throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(address).toStdString()); + } + _host = parts[0]; + + bool ok; + _port = parts[1].toUShort(&ok); + if (!ok) + { + throw std::runtime_error(QString("Wrong address: Unable to parse the port number (%1)").arg(parts[1]).toStdString()); + } + + // try to connect to host + std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl; + connectToHost(); +} + +ProtoConnection::~ProtoConnection() +{ + _socket.close(); +} + +void ProtoConnection::setSkipReply(bool skip) +{ + _skipReply = skip; +} + +void ProtoConnection::setColor(const ColorRgb & color, int priority, int duration) +{ + proto::HyperionRequest request; + request.set_command(proto::HyperionRequest::COLOR); + proto::ColorRequest * colorRequest = request.MutableExtension(proto::ColorRequest::colorRequest); + colorRequest->set_rgbcolor((color.red << 16) | (color.green << 8) | color.blue); + colorRequest->set_priority(priority); + colorRequest->set_duration(duration); + + // send command message + sendMessage(request); +} + +void ProtoConnection::setImage(const Image &image, int priority, int duration) +{ + proto::HyperionRequest request; + request.set_command(proto::HyperionRequest::IMAGE); + proto::ImageRequest * imageRequest = request.MutableExtension(proto::ImageRequest::imageRequest); + imageRequest->set_imagedata(image.memptr(), image.width() * image.height() * 3); + imageRequest->set_imagewidth(image.width()); + imageRequest->set_imageheight(image.height()); + imageRequest->set_priority(priority); + imageRequest->set_duration(duration); + + // send command message + sendMessage(request); +} + +void ProtoConnection::clear(int priority) +{ + proto::HyperionRequest request; + request.set_command(proto::HyperionRequest::CLEAR); + proto::ClearRequest * clearRequest = request.MutableExtension(proto::ClearRequest::clearRequest); + clearRequest->set_priority(priority); + + // send command message + sendMessage(request); +} + +void ProtoConnection::clearAll() +{ + proto::HyperionRequest request; + request.set_command(proto::HyperionRequest::CLEARALL); + + // send command message + sendMessage(request); +} + +void ProtoConnection::connectToHost() +{ + _socket.connectToHost(_host, _port); + if (_socket.waitForConnected()) { + std::cout << "Connected to Hyperion host" << std::endl; + } +} + +void ProtoConnection::sendMessage(const proto::HyperionRequest &message) +{ + if (_socket.state() == QAbstractSocket::UnconnectedState) + { + std::cout << "Currently disconnected: trying to connect to host" << std::endl; + connectToHost(); + } + + if (_socket.state() != QAbstractSocket::ConnectedState) + { + return; + } + + // We only get here if we are connected + + // serialize message (FastWriter already appends a newline) + std::string serializedMessage = message.SerializeAsString(); + + int length = serializedMessage.size(); + const uint8_t header[] = { + uint8_t((length >> 24) & 0xFF), + uint8_t((length >> 16) & 0xFF), + uint8_t((length >> 8) & 0xFF), + uint8_t((length ) & 0xFF)}; + + // write message + int count = 0; + count += _socket.write(reinterpret_cast(header), 4); + count += _socket.write(reinterpret_cast(serializedMessage.data()), length); + if (!_socket.waitForBytesWritten()) + { + std::cerr << "Error while writing data to host" << std::endl; + return; + } + + if (!_skipReply) + { + // read reply data + QByteArray serializedReply; + length = -1; + while (length < 0 && serializedReply.size() < length+4) + { + // receive reply + if (!_socket.waitForReadyRead()) + { + std::cerr << "Error while reading data from host" << std::endl; + return; + } + + serializedReply += _socket.readAll(); + + if (length < 0 && serializedReply.size() >= 4) + { + // read the message size + length = + ((serializedReply[0]<<24) & 0xFF000000) | + ((serializedReply[1]<<16) & 0x00FF0000) | + ((serializedReply[2]<< 8) & 0x0000FF00) | + ((serializedReply[3] ) & 0x000000FF); + } + } + + // parse reply data + proto::HyperionReply reply; + reply.ParseFromArray(serializedReply.constData()+4, length); + + // parse reply message + parseReply(reply); + } +} + +bool ProtoConnection::parseReply(const proto::HyperionReply &reply) +{ + bool success = false; + + if (!reply.success()) + { + if (reply.has_error()) + { + throw std::runtime_error("Error: " + reply.error()); + } + else + { + throw std::runtime_error("Error: No error info"); + } + } + + return success; +} diff --git a/libsrc/protoserver/ProtoConnectionWrapper.cpp b/libsrc/protoserver/ProtoConnectionWrapper.cpp new file mode 100644 index 00000000..7cf88f43 --- /dev/null +++ b/libsrc/protoserver/ProtoConnectionWrapper.cpp @@ -0,0 +1,19 @@ +// protoserver includes +#include "protoserver/ProtoConnectionWrapper.h" + +ProtoConnectionWrapper::ProtoConnectionWrapper(const std::string & address, int priority, int duration_ms, bool skipProtoReply) : + _priority(priority), + _duration_ms(duration_ms), + _connection(address) +{ + _connection.setSkipReply(skipProtoReply); +} + +ProtoConnectionWrapper::~ProtoConnectionWrapper() +{ +} + +void ProtoConnectionWrapper::receiveImage(const Image & image) +{ + _connection.setImage(image, _priority, _duration_ms); +} diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt index 7d13baf8..6607c04f 100644 --- a/src/hyperion-v4l2/CMakeLists.txt +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -2,44 +2,29 @@ cmake_minimum_required(VERSION 2.8) project(hyperion-v4l2) -# add protocol buffers -find_package(Protobuf REQUIRED) - # find Qt4 find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) include_directories( - ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver ${PROTOBUF_INCLUDE_DIRS} ${QT_INCLUDES} ) set(Hyperion_V4L2_QT_HEADERS - ImageHandler.h ScreenshotHandler.h ) set(Hyperion_V4L2_HEADERS VideoStandardParameter.h PixelFormatParameter.h - ProtoConnection.h ) set(Hyperion_V4L2_SOURCES hyperion-v4l2.cpp - ProtoConnection.cpp - ImageHandler.cpp ScreenshotHandler.cpp ) -set(Hyperion_V4L2_PROTOS - ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/protoserver/message.proto -) - -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 @@ -47,8 +32,6 @@ add_executable(hyperion-v4l2 ${Hyperion_V4L2_SOURCES} ${Hyperion_V4L2_QT_HEADERS} ${Hyperion_V4L2_MOC_SOURCES} - ${Hyperion_V4L2_PROTO_SRCS} - ${Hyperion_V4L2_PROTO_HDRS} ) target_link_libraries(hyperion-v4l2 @@ -56,7 +39,7 @@ target_link_libraries(hyperion-v4l2 getoptPlusPlus blackborder hyperion-utils - ${PROTOBUF_LIBRARIES} + protoserver pthread ${QT_LIBRARIES} ) diff --git a/src/hyperion-v4l2/ImageHandler.cpp b/src/hyperion-v4l2/ImageHandler.cpp deleted file mode 100644 index 19a1da80..00000000 --- a/src/hyperion-v4l2/ImageHandler.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// hyperion-v4l2 includes -#include "ImageHandler.h" - -ImageHandler::ImageHandler(const std::string & address, int priority, bool skipProtoReply) : - _priority(priority), - _connection(address) -{ - _connection.setSkipReply(skipProtoReply); -} - -ImageHandler::~ImageHandler() -{ -} - -void ImageHandler::receiveImage(const Image & image) -{ - _connection.setImage(image, _priority, 1000); -} diff --git a/src/hyperion-v4l2/ImageHandler.h b/src/hyperion-v4l2/ImageHandler.h deleted file mode 100644 index 8730989d..00000000 --- a/src/hyperion-v4l2/ImageHandler.h +++ /dev/null @@ -1,31 +0,0 @@ -// Qt includes -#include - -// hyperion includes -#include -#include - -// hyperion v4l2 includes -#include "ProtoConnection.h" - -/// This class handles callbacks from the V4L2 grabber -class ImageHandler : public QObject -{ - Q_OBJECT - -public: - ImageHandler(const std::string & address, int priority, bool skipProtoReply); - virtual ~ImageHandler(); - -public slots: - /// Handle a single image - /// @param image The image to process - void receiveImage(const Image & image); - -private: - /// Priority for calls to Hyperion - const int _priority; - - /// Hyperion proto connection object - ProtoConnection _connection; -}; diff --git a/src/hyperion-v4l2/ProtoConnection.cpp b/src/hyperion-v4l2/ProtoConnection.cpp deleted file mode 100644 index 9f59eddd..00000000 --- a/src/hyperion-v4l2/ProtoConnection.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// stl includes -#include - -// Qt includes -#include - -// hyperion-v4l2 includes -#include "ProtoConnection.h" - -ProtoConnection::ProtoConnection(const std::string & a) : - _socket(), - _skipReply(false) -{ - QString address(a.c_str()); - QStringList parts = address.split(":"); - if (parts.size() != 2) - { - throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(address).toStdString()); - } - _host = parts[0]; - - bool ok; - _port = parts[1].toUShort(&ok); - if (!ok) - { - throw std::runtime_error(QString("Wrong address: Unable to parse the port number (%1)").arg(parts[1]).toStdString()); - } - - // try to connect to host - std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl; - connectToHost(); -} - -ProtoConnection::~ProtoConnection() -{ - _socket.close(); -} - -void ProtoConnection::setSkipReply(bool skip) -{ - _skipReply = skip; -} - -void ProtoConnection::setColor(const ColorRgb & color, int priority, int duration) -{ - proto::HyperionRequest request; - request.set_command(proto::HyperionRequest::COLOR); - proto::ColorRequest * colorRequest = request.MutableExtension(proto::ColorRequest::colorRequest); - colorRequest->set_rgbcolor((color.red << 16) | (color.green << 8) | color.blue); - colorRequest->set_priority(priority); - colorRequest->set_duration(duration); - - // send command message - sendMessage(request); -} - -void ProtoConnection::setImage(const Image &image, int priority, int duration) -{ - proto::HyperionRequest request; - request.set_command(proto::HyperionRequest::IMAGE); - proto::ImageRequest * imageRequest = request.MutableExtension(proto::ImageRequest::imageRequest); - imageRequest->set_imagedata(image.memptr(), image.width() * image.height() * 3); - imageRequest->set_imagewidth(image.width()); - imageRequest->set_imageheight(image.height()); - imageRequest->set_priority(priority); - imageRequest->set_duration(duration); - - // send command message - sendMessage(request); -} - -void ProtoConnection::clear(int priority) -{ - proto::HyperionRequest request; - request.set_command(proto::HyperionRequest::CLEAR); - proto::ClearRequest * clearRequest = request.MutableExtension(proto::ClearRequest::clearRequest); - clearRequest->set_priority(priority); - - // send command message - sendMessage(request); -} - -void ProtoConnection::clearAll() -{ - proto::HyperionRequest request; - request.set_command(proto::HyperionRequest::CLEARALL); - - // send command message - sendMessage(request); -} - -void ProtoConnection::connectToHost() -{ - _socket.connectToHost(_host, _port); - if (_socket.waitForConnected()) { - std::cout << "Connected to Hyperion host" << std::endl; - } -} - -void ProtoConnection::sendMessage(const proto::HyperionRequest &message) -{ - if (_socket.state() == QAbstractSocket::UnconnectedState) - { - std::cout << "Currently disconnected: trying to connect to host" << std::endl; - connectToHost(); - } - - if (_socket.state() != QAbstractSocket::ConnectedState) - { - return; - } - - // We only get here if we are connected - - // serialize message (FastWriter already appends a newline) - std::string serializedMessage = message.SerializeAsString(); - - int length = serializedMessage.size(); - const uint8_t header[] = { - uint8_t((length >> 24) & 0xFF), - uint8_t((length >> 16) & 0xFF), - uint8_t((length >> 8) & 0xFF), - uint8_t((length ) & 0xFF)}; - - // write message - int count = 0; - count += _socket.write(reinterpret_cast(header), 4); - count += _socket.write(reinterpret_cast(serializedMessage.data()), length); - if (!_socket.waitForBytesWritten()) - { - std::cerr << "Error while writing data to host" << std::endl; - return; - } - - if (!_skipReply) - { - // read reply data - QByteArray serializedReply; - length = -1; - while (length < 0 && serializedReply.size() < length+4) - { - // receive reply - if (!_socket.waitForReadyRead()) - { - std::cerr << "Error while reading data from host" << std::endl; - return; - } - - serializedReply += _socket.readAll(); - - if (length < 0 && serializedReply.size() >= 4) - { - // read the message size - length = - ((serializedReply[0]<<24) & 0xFF000000) | - ((serializedReply[1]<<16) & 0x00FF0000) | - ((serializedReply[2]<< 8) & 0x0000FF00) | - ((serializedReply[3] ) & 0x000000FF); - } - } - - // parse reply data - proto::HyperionReply reply; - reply.ParseFromArray(serializedReply.constData()+4, length); - - // parse reply message - parseReply(reply); - } -} - -bool ProtoConnection::parseReply(const proto::HyperionReply &reply) -{ - bool success = false; - - if (!reply.success()) - { - if (reply.has_error()) - { - throw std::runtime_error("Error: " + reply.error()); - } - else - { - throw std::runtime_error("Error: No error info"); - } - } - - return success; -} diff --git a/src/hyperion-v4l2/ProtoConnection.h b/src/hyperion-v4l2/ProtoConnection.h deleted file mode 100644 index a220c794..00000000 --- a/src/hyperion-v4l2/ProtoConnection.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -// stl includes -#include - -// Qt includes -#include -#include -#include -#include - -// hyperion util -#include -#include - -// jsoncpp includes -#include - -/// -/// Connection class to setup an connection to the hyperion server and execute commands -/// -class ProtoConnection -{ -public: - /// - /// Constructor - /// - /// @param address The address of the Hyperion server (for example "192.168.0.32:19444) - /// - ProtoConnection(const std::string & address); - - /// - /// Destructor - /// - ~ProtoConnection(); - - /// Do not read reply messages from Hyperion if set to true - void setSkipReply(bool skip); - - /// - /// Set all leds to the specified color - /// - /// @param color The color - /// @param priority The priority - /// @param duration The duration in milliseconds - /// - void setColor(const ColorRgb & color, int priority, int duration = 1); - - /// - /// Set the leds according to the given image (assume the image is stretched to the display size) - /// - /// @param image The image - /// @param priority The priority - /// @param duration The duration in milliseconds - /// - void setImage(const Image & image, int priority, int duration = -1); - - /// - /// Clear the given priority channel - /// - /// @param priority The priority - /// - void clear(int priority); - - /// - /// Clear all priority channels - /// - void clearAll(); - -private: - /// Try to connect to the Hyperion host - void connectToHost(); - - /// - /// Send a command message and receive its reply - /// - /// @param message The message to send - /// - void sendMessage(const proto::HyperionRequest & message); - - /// - /// Parse a reply message - /// - /// @param reply The received reply - /// - /// @return true if the reply indicates success - /// - bool parseReply(const proto::HyperionReply & reply); - -private: - /// The TCP-Socket with the connection to the server - QTcpSocket _socket; - - /// Host address - QString _host; - - /// Host port - uint16_t _port; - - /// Skip receiving reply messages from Hyperion if set - bool _skipReply; -}; diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index 86c23781..aacd1c19 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -15,11 +15,13 @@ // grabber includes #include "grabber/V4L2Grabber.h" +// proto includes +#include "protoserver/ProtoConnection.h" +#include "protoserver/ProtoConnectionWrapper.h" + // hyperion-v4l2 includes -#include "ProtoConnection.h" #include "VideoStandardParameter.h" #include "PixelFormatParameter.h" -#include "ImageHandler.h" #include "ScreenshotHandler.h" using namespace vlofgren; @@ -27,144 +29,144 @@ using namespace vlofgren; // save the image as screenshot void saveScreenshot(void *, 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("screenshot.png"); + // store as PNG + QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); + pngImage.save("screenshot.png"); } int main(int argc, char** argv) { - QCoreApplication app(argc, argv); + QCoreApplication app(argc, argv); - // force the locale - setlocale(LC_ALL, "C"); - QLocale::setDefault(QLocale::c()); + // force the locale + setlocale(LC_ALL, "C"); + QLocale::setDefault(QLocale::c()); - // register the image type to use in signals - qRegisterMetaType>("Image"); + // register the image type to use in signals + qRegisterMetaType>("Image"); - try - { - // create the option parser and initialize all parameters - OptionsParser optionParser("V4L capture application for Hyperion"); - ParameterSet & parameters = optionParser.getParameters(); + try + { + // create the option parser and initialize all parameters + OptionsParser optionParser("V4L capture application for Hyperion"); + ParameterSet & parameters = optionParser.getParameters(); - StringParameter & argDevice = parameters.add ('d', "device", "The device to use [default=/dev/video0]"); - VideoStandardParameter & argVideoStandard = parameters.add('v', "video-standard", "The used video standard. Valid values are PAL or NTSC (optional)"); - PixelFormatParameter & argPixelFormat = parameters.add (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, and RGB32 (optional)"); - IntParameter & argInput = parameters.add (0x0, "input", "Input channel (optional)"); - IntParameter & argWidth = parameters.add (0x0, "width", "Try to set the width of the video input (optional)"); - IntParameter & argHeight = parameters.add (0x0, "height", "Try to set the height of the video input (optional)"); - IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default=0]"); - IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default=0]"); - IntParameter & argCropLeft = parameters.add (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)"); - IntParameter & argCropRight = parameters.add (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)"); - IntParameter & argCropTop = parameters.add (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)"); - IntParameter & argCropBottom = parameters.add (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)"); - IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=1]"); - IntParameter & argFrameDecimation = parameters.add ('f', "frame-decimator", "Decimation factor for the video frames [default=1]"); - SwitchParameter<> & argScreenshot = parameters.add> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); - DoubleParameter & argSignalThreshold = parameters.add ('t', "signal-threshold", "The signal threshold for detecting the presence of a signal. Value should be between 0.0 and 1.0."); - DoubleParameter & argRedSignalThreshold = parameters.add (0x0, "red-threshold", "The red signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)"); - DoubleParameter & argGreenSignalThreshold = parameters.add (0x0, "green-threshold", "The green signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)"); - DoubleParameter & argBlueSignalThreshold = parameters.add (0x0, "blue-threshold", "The blue signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)"); - SwitchParameter<> & arg3DSBS = parameters.add> (0x0, "3DSBS", "Interpret the incoming video stream as 3D side-by-side"); - SwitchParameter<> & arg3DTAB = parameters.add> (0x0, "3DTAB", "Interpret the incoming video stream as 3D top-and-bottom"); - StringParameter & argAddress = parameters.add ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); - IntParameter & argPriority = parameters.add ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); - SwitchParameter<> & argSkipReply = parameters.add> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); - SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); + StringParameter & argDevice = parameters.add ('d', "device", "The device to use [default=/dev/video0]"); + VideoStandardParameter & argVideoStandard = parameters.add('v', "video-standard", "The used video standard. Valid values are PAL or NTSC (optional)"); + PixelFormatParameter & argPixelFormat = parameters.add (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, and RGB32 (optional)"); + IntParameter & argInput = parameters.add (0x0, "input", "Input channel (optional)"); + IntParameter & argWidth = parameters.add (0x0, "width", "Try to set the width of the video input (optional)"); + IntParameter & argHeight = parameters.add (0x0, "height", "Try to set the height of the video input (optional)"); + IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default=0]"); + IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default=0]"); + IntParameter & argCropLeft = parameters.add (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)"); + IntParameter & argCropRight = parameters.add (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)"); + IntParameter & argCropTop = parameters.add (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)"); + IntParameter & argCropBottom = parameters.add (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)"); + IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=1]"); + IntParameter & argFrameDecimation = parameters.add ('f', "frame-decimator", "Decimation factor for the video frames [default=1]"); + SwitchParameter<> & argScreenshot = parameters.add> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); + DoubleParameter & argSignalThreshold = parameters.add ('t', "signal-threshold", "The signal threshold for detecting the presence of a signal. Value should be between 0.0 and 1.0."); + DoubleParameter & argRedSignalThreshold = parameters.add (0x0, "red-threshold", "The red signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)"); + DoubleParameter & argGreenSignalThreshold = parameters.add (0x0, "green-threshold", "The green signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)"); + DoubleParameter & argBlueSignalThreshold = parameters.add (0x0, "blue-threshold", "The blue signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)"); + SwitchParameter<> & arg3DSBS = parameters.add> (0x0, "3DSBS", "Interpret the incoming video stream as 3D side-by-side"); + SwitchParameter<> & arg3DTAB = parameters.add> (0x0, "3DTAB", "Interpret the incoming video stream as 3D top-and-bottom"); + StringParameter & argAddress = parameters.add ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); + IntParameter & argPriority = parameters.add ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); + SwitchParameter<> & argSkipReply = parameters.add> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); + SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); - // set defaults - argDevice.setDefault("/dev/video0"); - argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE); - argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE); - argInput.setDefault(-1); - argWidth.setDefault(-1); - argHeight.setDefault(-1); - argCropWidth.setDefault(0); - argCropHeight.setDefault(0); - argSizeDecimation.setDefault(1); - argFrameDecimation.setDefault(1); - argAddress.setDefault("127.0.0.1:19445"); - argPriority.setDefault(800); - argSignalThreshold.setDefault(-1); + // set defaults + argDevice.setDefault("/dev/video0"); + argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE); + argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE); + argInput.setDefault(-1); + argWidth.setDefault(-1); + argHeight.setDefault(-1); + argCropWidth.setDefault(0); + argCropHeight.setDefault(0); + argSizeDecimation.setDefault(1); + argFrameDecimation.setDefault(1); + argAddress.setDefault("127.0.0.1:19445"); + argPriority.setDefault(800); + argSignalThreshold.setDefault(-1); - // parse all options - optionParser.parse(argc, const_cast(argv)); + // parse all options + optionParser.parse(argc, const_cast(argv)); - // check if we need to display the usage. exit if we do. - if (argHelp.isSet()) - { - optionParser.usage(); - return 0; - } + // check if we need to display the usage. exit if we do. + if (argHelp.isSet()) + { + optionParser.usage(); + return 0; + } - if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue()); - if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue()); - if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue()); - if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue()); + if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue()); + if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue()); + if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue()); + if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue()); - // initialize the grabber - V4L2Grabber grabber( - argDevice.getValue(), - argInput.getValue(), - argVideoStandard.getValue(), - argPixelFormat.getValue(), - argWidth.getValue(), - argHeight.getValue(), - std::max(1, argFrameDecimation.getValue()), - std::max(1, argSizeDecimation.getValue()), - std::max(1, argSizeDecimation.getValue())); + // initialize the grabber + V4L2Grabber grabber( + argDevice.getValue(), + argInput.getValue(), + argVideoStandard.getValue(), + argPixelFormat.getValue(), + argWidth.getValue(), + argHeight.getValue(), + std::max(1, argFrameDecimation.getValue()), + std::max(1, argSizeDecimation.getValue()), + std::max(1, argSizeDecimation.getValue())); - // set signal detection - grabber.setSignalThreshold( - std::min(1.0, std::max(0.0, argRedSignalThreshold.isSet() ? argRedSignalThreshold.getValue() : argSignalThreshold.getValue())), - std::min(1.0, std::max(0.0, argGreenSignalThreshold.isSet() ? argGreenSignalThreshold.getValue() : argSignalThreshold.getValue())), - std::min(1.0, std::max(0.0, argBlueSignalThreshold.isSet() ? argBlueSignalThreshold.getValue() : argSignalThreshold.getValue())), - 50); + // set signal detection + grabber.setSignalThreshold( + std::min(1.0, std::max(0.0, argRedSignalThreshold.isSet() ? argRedSignalThreshold.getValue() : argSignalThreshold.getValue())), + std::min(1.0, std::max(0.0, argGreenSignalThreshold.isSet() ? argGreenSignalThreshold.getValue() : argSignalThreshold.getValue())), + std::min(1.0, std::max(0.0, argBlueSignalThreshold.isSet() ? argBlueSignalThreshold.getValue() : argSignalThreshold.getValue())), + 50); - // set cropping values - grabber.setCropping( - std::max(0, argCropLeft.getValue()), - std::max(0, argCropRight.getValue()), - std::max(0, argCropTop.getValue()), - std::max(0, argCropBottom.getValue())); + // set cropping values + grabber.setCropping( + std::max(0, argCropLeft.getValue()), + std::max(0, argCropRight.getValue()), + std::max(0, argCropTop.getValue()), + std::max(0, argCropBottom.getValue())); - // set 3D mode if applicable - if (arg3DSBS.isSet()) - { - grabber.set3D(VIDEO_3DSBS); - } - else if (arg3DTAB.isSet()) - { - grabber.set3D(VIDEO_3DTAB); - } + // set 3D mode if applicable + if (arg3DSBS.isSet()) + { + grabber.set3D(VIDEO_3DSBS); + } + else if (arg3DTAB.isSet()) + { + grabber.set3D(VIDEO_3DTAB); + } - // run the grabber - if (argScreenshot.isSet()) - { - 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(), argSkipReply.isSet()); - QObject::connect(&grabber, SIGNAL(newFrame(Image)), &handler, SLOT(receiveImage(Image))); - grabber.start(); - QCoreApplication::exec(); - grabber.stop(); - } - } - catch (const std::runtime_error & e) - { - // An error occured. Display error and quit - std::cerr << e.what() << std::endl; - return 1; - } + // run the grabber + if (argScreenshot.isSet()) + { + ScreenshotHandler handler("screenshot.png"); + QObject::connect(&grabber, SIGNAL(newFrame(Image)), &handler, SLOT(receiveImage(Image))); + grabber.start(); + QCoreApplication::exec(); + grabber.stop(); + } + else + { + ProtoConnectionWrapper handler(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet()); + QObject::connect(&grabber, SIGNAL(newFrame(Image)), &handler, SLOT(receiveImage(Image))); + grabber.start(); + QCoreApplication::exec(); + grabber.stop(); + } + } + catch (const std::runtime_error & e) + { + // An error occured. Display error and quit + std::cerr << e.what() << std::endl; + return 1; + } - return 0; + return 0; } diff --git a/src/hyperion-x11/CMakeLists.txt b/src/hyperion-x11/CMakeLists.txt index 43cc9a1d..7841232c 100644 --- a/src/hyperion-x11/CMakeLists.txt +++ b/src/hyperion-x11/CMakeLists.txt @@ -4,9 +4,6 @@ cmake_minimum_required(VERSION 2.8) # Set the project name project(hyperion-x11) -# add protocol buffers -find_package(Protobuf REQUIRED) - # find Qt4 find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) @@ -14,42 +11,27 @@ find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) find_package(X11 REQUIRED) include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${PROTOBUF_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver ${QT_INCLUDES} ${X11_INCLUDES} ) set(Hyperion_X11_QT_HEADERS - ProtoWrapper.h X11Wrapper.h) set(Hyperion_X11_HEADERS - X11Grabber.h - ../hyperion-v4l2/ProtoConnection.h ) set(Hyperion_X11_SOURCES hyperion-x11.cpp - ProtoWrapper.cpp - X11Grabber.cpp X11Wrapper.cpp - ../hyperion-v4l2/ProtoConnection.cpp -) - -set(Hyperion_X11_PROTOS - ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/protoserver/message.proto ) QT4_WRAP_CPP(Hyperion_X11_HEADERS_MOC ${Hyperion_X11_QT_HEADERS}) -protobuf_generate_cpp(Hyperion_X11_PROTO_SRCS Hyperion_X11_PROTO_HDRS ${Hyperion_X11_PROTOS}) - add_executable(hyperion-x11 ${Hyperion_X11_HEADERS} ${Hyperion_X11_SOURCES} - ${Hyperion_X11_PROTO_SRCS} - ${Hyperion_X11_PROTO_HDRS} ${Hyperion_X11_HEADERS_MOC} ) @@ -57,7 +39,8 @@ target_link_libraries(hyperion-x11 getoptPlusPlus blackborder hyperion-utils - ${PROTOBUF_LIBRARIES} + protoserver + x11-grabber ${X11_LIBRARIES} pthread ) diff --git a/src/hyperion-x11/ProtoWrapper.cpp b/src/hyperion-x11/ProtoWrapper.cpp deleted file mode 100644 index c45fed7b..00000000 --- a/src/hyperion-x11/ProtoWrapper.cpp +++ /dev/null @@ -1,17 +0,0 @@ - -// -#include "ProtoWrapper.h" - -ProtoWrapper::ProtoWrapper(const std::string & protoAddress, const bool skipReply) : - _priority(10), - _duration_ms(2000), - _connection(protoAddress) -{ - _connection.setSkipReply(skipReply); -} - -void ProtoWrapper::process(const Image & image) -{ - std::cout << "Attempt to send screenshot" << std::endl; - _connection.setImage(image, _priority, _duration_ms); -} diff --git a/src/hyperion-x11/ProtoWrapper.h b/src/hyperion-x11/ProtoWrapper.h deleted file mode 100644 index 4d184868..00000000 --- a/src/hyperion-x11/ProtoWrapper.h +++ /dev/null @@ -1,23 +0,0 @@ - -// QT includes -#include - - -#include "../hyperion-v4l2/ProtoConnection.h" - -class ProtoWrapper : public QObject -{ - Q_OBJECT -public: - ProtoWrapper(const std::string & protoAddress, const bool skipReply); - -public slots: - void process(const Image & image); - -private: - - int _priority; - int _duration_ms; - - ProtoConnection _connection; -}; diff --git a/src/hyperion-x11/X11Grabber.cpp b/src/hyperion-x11/X11Grabber.cpp deleted file mode 100644 index cd6d0fda..00000000 --- a/src/hyperion-x11/X11Grabber.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// STL includes -#include -#include - -// X11 includes -#include - -// X11Grabber includes -#include "X11Grabber.h" - -X11Grabber::X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : - _pixelDecimation(pixelDecimation), - _cropWidth(cropHorizontal), - _cropHeight(cropVertical), - _x11Display(nullptr), - _screenWidth(0), - _screenHeight(0), - _image(0,0) -{ - // empty -} - -X11Grabber::~X11Grabber() -{ - if (_x11Display != nullptr) - { - XCloseDisplay(_x11Display); - } -} - -int X11Grabber::open() -{ - const char * display_name = nullptr; - _x11Display = XOpenDisplay(display_name); - - if (_x11Display == nullptr) - { - std::cerr << "Failed to open the default X11-display" << std::endl; - return -1; - } - - return 0; -} - -Image & X11Grabber::grab() -{ - if (_x11Display == nullptr) - { - open(); - } - - updateScreenDimensions(); - - const int croppedWidth = _screenWidth - 2*_cropWidth; - const int croppedHeight = _screenHeight - 2*_cropHeight; - - // Capture the current screen - XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropWidth, _cropHeight, croppedWidth, croppedHeight, AllPlanes, ZPixmap); - if (xImage == nullptr) - { - std::cerr << "Grab failed" << std::endl; - return _image; - } - - // Copy the capture XImage to the local image (and apply required decimation) - ColorRgb * outputPtr = _image.memptr(); - for (int iY=(_pixelDecimation/2); iYred = uint8_t((pixel >> 16) & 0xff); - outputPtr->green = uint8_t((pixel >> 8) & 0xff); - outputPtr->blue = uint8_t((pixel >> 0) & 0xff); - - // Move to the next output pixel - ++outputPtr; - } - } - // Cleanup allocated resources of the X11 grab - XDestroyImage(xImage); - - return _image; -} - -int X11Grabber::updateScreenDimensions() -{ - XWindowAttributes window_attributes_return; - const Status status = XGetWindowAttributes(_x11Display, DefaultRootWindow(_x11Display), &window_attributes_return); - if (status == 0) - { - std::cerr << "Failed to obtain window attributes" << std::endl; - return -1; - } - - if (_screenWidth == unsigned(window_attributes_return.width) && _screenHeight == unsigned(window_attributes_return.height)) - { - // No update required - return 0; - } - std::cout << "Update of screen resolution: [" << _screenWidth << "x" << _screenHeight <<"] => "; - _screenWidth = window_attributes_return.width; - _screenHeight = window_attributes_return.height; - 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; - _image.resize(width, height); - - return 0; -} diff --git a/src/hyperion-x11/X11Wrapper.cpp b/src/hyperion-x11/X11Wrapper.cpp index 5bf4a97f..303a03b4 100644 --- a/src/hyperion-x11/X11Wrapper.cpp +++ b/src/hyperion-x11/X11Wrapper.cpp @@ -3,31 +3,31 @@ #include "X11Wrapper.h" X11Wrapper::X11Wrapper(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : - _timer(this), - _grabber(cropHorizontal, cropVertical, pixelDecimation) + _timer(this), + _grabber(cropHorizontal, cropVertical, pixelDecimation) { - // Connect capturing to the timeout signal of the timer - connect(&_timer, SIGNAL(timeout()), this, SLOT(capture())); + // Connect capturing to the timeout signal of the timer + connect(&_timer, SIGNAL(timeout()), this, SLOT(capture())); } const Image & X11Wrapper::getScreenshot() { - const Image & screenshot = _grabber.grab(); - return screenshot; + const Image & screenshot = _grabber.grab(); + return screenshot; } void X11Wrapper::start() { - _timer.start(200); + _timer.start(100); } void X11Wrapper::stop() { - _timer.stop(); + _timer.stop(); } void X11Wrapper::capture() { - const Image & screenshot = _grabber.grab(); - emit sig_screenshot(screenshot); + const Image & screenshot = _grabber.grab(); + emit sig_screenshot(screenshot); } diff --git a/src/hyperion-x11/X11Wrapper.h b/src/hyperion-x11/X11Wrapper.h index 405cbce9..986dceaf 100644 --- a/src/hyperion-x11/X11Wrapper.h +++ b/src/hyperion-x11/X11Wrapper.h @@ -3,37 +3,37 @@ #include // Hyperion-X11 includes -#include "X11Grabber.h" +#include class X11Wrapper : public QObject { - Q_OBJECT + Q_OBJECT public: - X11Wrapper(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); + X11Wrapper(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); - const Image & getScreenshot(); + const Image & getScreenshot(); - /// - /// Starts the timed capturing of screenshots - /// - void start(); + /// + /// Starts the timed capturing of screenshots + /// + void start(); - void stop(); + void stop(); signals: - void sig_screenshot(const Image & screenshot); + void sig_screenshot(const Image & screenshot); private slots: - /// - /// Performs a single screenshot capture and publishes the capture screenshot on the screenshot - /// signal. - /// - void capture(); + /// + /// Performs a single screenshot capture and publishes the capture screenshot on the screenshot + /// signal. + /// + void capture(); private: - /// The QT timer to generate capture-publish events - QTimer _timer; + /// The QT timer to generate capture-publish events + QTimer _timer; - /// The grabber for creating screenshots - X11Grabber _grabber; + /// The grabber for creating screenshots + X11Grabber _grabber; }; diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index aed83cc3..0a494516 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -6,7 +6,7 @@ // getoptPlusPLus includes #include -#include "ProtoWrapper.h" +#include "protoserver/ProtoConnectionWrapper.h" #include "X11Wrapper.h" using namespace vlofgren; @@ -14,77 +14,77 @@ using namespace vlofgren; // save the image as screenshot void saveScreenshot(const char * filename, 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); + // store as PNG + QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); + pngImage.save(filename); } int main(int argc, char ** argv) { - QCoreApplication app(argc, argv); + QCoreApplication app(argc, argv); - try - { - // create the option parser and initialize all parameters - OptionsParser optionParser("X11 capture application for Hyperion"); - ParameterSet & parameters = optionParser.getParameters(); + try + { + // create the option parser and initialize all parameters + OptionsParser optionParser("X11 capture application for Hyperion"); + ParameterSet & parameters = optionParser.getParameters(); - IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]"); - IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]"); - IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=16]"); - SwitchParameter<> & argScreenshot = parameters.add> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); - StringParameter & argAddress = parameters.add ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); - IntParameter & argPriority = parameters.add ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); - SwitchParameter<> & argSkipReply = parameters.add> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); - SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); + IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]"); + IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]"); + IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=16]"); + SwitchParameter<> & argScreenshot = parameters.add> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); + StringParameter & argAddress = parameters.add ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); + IntParameter & argPriority = parameters.add ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); + SwitchParameter<> & argSkipReply = parameters.add> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); + SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); - // set defaults - argCropWidth.setDefault(0); - argCropHeight.setDefault(0); - argSizeDecimation.setDefault(16); - argAddress.setDefault("127.0.0.1:19445"); - argPriority.setDefault(800); + // set defaults + argCropWidth.setDefault(0); + argCropHeight.setDefault(0); + argSizeDecimation.setDefault(16); + argAddress.setDefault("127.0.0.1:19445"); + argPriority.setDefault(800); - // parse all options - optionParser.parse(argc, const_cast(argv)); + // parse all options + optionParser.parse(argc, const_cast(argv)); - // check if we need to display the usage. exit if we do. - if (argHelp.isSet()) - { - optionParser.usage(); - return 0; - } + // check if we need to display the usage. exit if we do. + if (argHelp.isSet()) + { + optionParser.usage(); + return 0; + } - // Create the X11 grabbing stuff - X11Wrapper x11Wrapper(argCropWidth.getValue(), argCropHeight.getValue(), argSizeDecimation.getValue()); + // Create the X11 grabbing stuff + X11Wrapper x11Wrapper(argCropWidth.getValue(), argCropHeight.getValue(), argSizeDecimation.getValue()); - if (argScreenshot.isSet()) - { - // Capture a single screenshot and finish - const Image & screenshot = x11Wrapper.getScreenshot(); - saveScreenshot("screenshot.png", screenshot); - } - else - { - // Create the Proto-connection with hyperiond - ProtoWrapper protoWrapper(argAddress.getValue(), argSkipReply.isSet()); + if (argScreenshot.isSet()) + { + // Capture a single screenshot and finish + const Image & screenshot = x11Wrapper.getScreenshot(); + saveScreenshot("screenshot.png", screenshot); + } + else + { + // Create the Proto-connection with hyperiond + ProtoConnectionWrapper protoWrapper(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet()); - // Connect the screen capturing to the proto processing - QObject::connect(&x11Wrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(process(Image))); + // Connect the screen capturing to the proto processing + QObject::connect(&x11Wrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(process(Image))); - // Start the capturing - x11Wrapper.start(); + // Start the capturing + x11Wrapper.start(); - // Start the application - app.exec(); - } - } - catch (const std::runtime_error & e) - { - // An error occured. Display error and quit - std::cerr << e.what() << std::endl; - return -1; - } + // Start the application + app.exec(); + } + } + catch (const std::runtime_error & e) + { + // An error occured. Display error and quit + std::cerr << e.what() << std::endl; + return -1; + } - return 0; + return 0; } From 0fd25285a663ddaec2e72288d0c78ea33ef55f4f Mon Sep 17 00:00:00 2001 From: poljvd Date: Mon, 1 Dec 2014 20:24:53 +0100 Subject: [PATCH 08/14] add framerate parameter to X11 grabber application Former-commit-id: b8f2bdb5c3e376a4505eec46204efe46dce1a6d9 --- src/hyperion-x11/X11Wrapper.cpp | 7 +++++-- src/hyperion-x11/X11Wrapper.h | 2 +- src/hyperion-x11/hyperion-x11.cpp | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/hyperion-x11/X11Wrapper.cpp b/src/hyperion-x11/X11Wrapper.cpp index 303a03b4..abaa4d3f 100644 --- a/src/hyperion-x11/X11Wrapper.cpp +++ b/src/hyperion-x11/X11Wrapper.cpp @@ -2,10 +2,13 @@ // Hyperion-X11 includes #include "X11Wrapper.h" -X11Wrapper::X11Wrapper(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : +X11Wrapper::X11Wrapper(int grabInterval, const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : _timer(this), _grabber(cropHorizontal, cropVertical, pixelDecimation) { + _timer.setSingleShot(false); + _timer.setInterval(grabInterval); + // Connect capturing to the timeout signal of the timer connect(&_timer, SIGNAL(timeout()), this, SLOT(capture())); } @@ -18,7 +21,7 @@ const Image & X11Wrapper::getScreenshot() void X11Wrapper::start() { - _timer.start(100); + _timer.start(); } void X11Wrapper::stop() diff --git a/src/hyperion-x11/X11Wrapper.h b/src/hyperion-x11/X11Wrapper.h index 986dceaf..6f67d7b9 100644 --- a/src/hyperion-x11/X11Wrapper.h +++ b/src/hyperion-x11/X11Wrapper.h @@ -9,7 +9,7 @@ class X11Wrapper : public QObject { Q_OBJECT public: - X11Wrapper(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); + X11Wrapper(int grabInterval, const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); const Image & getScreenshot(); diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index 0a494516..8d354378 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -29,6 +29,7 @@ int main(int argc, char ** argv) OptionsParser optionParser("X11 capture application for Hyperion"); ParameterSet & parameters = optionParser.getParameters(); + IntParameter & argFps = parameters.add ('f', "framerate", "Cpture frame rate [default=10]"); IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]"); IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]"); IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=16]"); @@ -39,6 +40,7 @@ int main(int argc, char ** argv) SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); // set defaults + argFps.setDefault(10); argCropWidth.setDefault(0); argCropHeight.setDefault(0); argSizeDecimation.setDefault(16); @@ -56,7 +58,8 @@ int main(int argc, char ** argv) } // Create the X11 grabbing stuff - X11Wrapper x11Wrapper(argCropWidth.getValue(), argCropHeight.getValue(), argSizeDecimation.getValue()); + int grabInterval = 1000 / argFps.getValue(); + X11Wrapper x11Wrapper(grabInterval, argCropWidth.getValue(), argCropHeight.getValue(), argSizeDecimation.getValue()); if (argScreenshot.isSet()) { From bf7ddf5991a2b2ec7a41b7b1da0af3591b41ba37 Mon Sep 17 00:00:00 2001 From: poljvd Date: Mon, 1 Dec 2014 21:27:33 +0100 Subject: [PATCH 09/14] Fix output size of X11 image after deciamtion Former-commit-id: c6120c7c4116f855405f65c21b0c0f5e1e443b6b --- libsrc/grabber/v4l2/V4L2Grabber.cpp | 1201 ++++++++++++++------------- libsrc/grabber/x11/X11Grabber.cpp | 13 +- 2 files changed, 614 insertions(+), 600 deletions(-) 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; From 93de6569855179523f5608b4eaae84393ada6db9 Mon Sep 17 00:00:00 2001 From: poljvd Date: Mon, 1 Dec 2014 21:35:04 +0100 Subject: [PATCH 10/14] fix slot name Former-commit-id: fc2faf3b70be3b0a01cf3c72f7092c57e7c88783 --- src/hyperion-x11/hyperion-x11.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index 8d354378..c473b7ed 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -73,7 +73,7 @@ int main(int argc, char ** argv) ProtoConnectionWrapper protoWrapper(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet()); // Connect the screen capturing to the proto processing - QObject::connect(&x11Wrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(process(Image))); + QObject::connect(&x11Wrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(receiveImage(Image))); // Start the capturing x11Wrapper.start(); From e608b11caaa7f8b559c60e1c4c5421f7e579a2e3 Mon Sep 17 00:00:00 2001 From: poljvd Date: Sun, 14 Dec 2014 14:26:59 +0100 Subject: [PATCH 11/14] Extract ImageResampler from X11Grabber Former-commit-id: 63e95fa85fc0ef5a66f6eb8b396cf4b6370cf5dd --- CMakeLists.txt | 4 +- include/grabber/V4L2Grabber.h | 136 ++++----- include/grabber/X11Grabber.h | 12 +- include/utils/Image.h | 353 +++++++++++------------ include/utils/ImageResampler.h | 40 +++ include/{grabber => utils}/PixelFormat.h | 17 +- libsrc/grabber/v4l2/CMakeLists.txt | 1 - libsrc/grabber/v4l2/V4L2Grabber.cpp | 16 +- libsrc/grabber/x11/X11Grabber.cpp | 52 +--- libsrc/utils/CMakeLists.txt | 6 + libsrc/utils/ImageResampler.cpp | 133 +++++++++ src/hyperion-v4l2/PixelFormatParameter.h | 58 ++-- src/hyperion-v4l2/hyperion-v4l2.cpp | 1 + src/hyperion-x11/X11Wrapper.cpp | 4 +- src/hyperion-x11/X11Wrapper.h | 2 +- src/hyperion-x11/hyperion-x11.cpp | 27 +- 16 files changed, 517 insertions(+), 345 deletions(-) create mode 100644 include/utils/ImageResampler.h rename include/{grabber => utils}/PixelFormat.h (70%) create mode 100644 libsrc/utils/ImageResampler.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 251bf2da..d8a25196 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,8 +57,8 @@ include_directories(${CMAKE_SOURCE_DIR}/include) # Prefer static linking over dynamic #set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") -#set(CMAKE_BUILD_TYPE "Debug") -set(CMAKE_BUILD_TYPE "Release") +set(CMAKE_BUILD_TYPE "Debug") +#set(CMAKE_BUILD_TYPE "Release") # enable C++11 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall") diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index fe867112..d130a5e5 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -11,119 +11,119 @@ // util includes #include #include +#include #include // grabber includes #include -#include /// Capture class for V4L2 devices /// /// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html class V4L2Grabber : public QObject { - Q_OBJECT + Q_OBJECT public: - V4L2Grabber(const std::string & device, - int input, - VideoStandard videoStandard, PixelFormat pixelFormat, - int width, - int height, - int frameDecimation, - int horizontalPixelDecimation, - int verticalPixelDecimation); - virtual ~V4L2Grabber(); + V4L2Grabber(const std::string & device, + int input, + VideoStandard videoStandard, PixelFormat pixelFormat, + int width, + int height, + int frameDecimation, + int horizontalPixelDecimation, + int verticalPixelDecimation); + virtual ~V4L2Grabber(); public slots: - void setCropping(int cropLeft, - int cropRight, - int cropTop, - int cropBottom); + void setCropping(int cropLeft, + int cropRight, + int cropTop, + int cropBottom); - void set3D(VideoMode mode); + void set3D(VideoMode mode); - void setSignalThreshold(double redSignalThreshold, - double greenSignalThreshold, - double blueSignalThreshold, - int noSignalCounterThreshold); + void setSignalThreshold(double redSignalThreshold, + double greenSignalThreshold, + double blueSignalThreshold, + int noSignalCounterThreshold); - void start(); + void start(); - void stop(); + void stop(); signals: - void newFrame(const Image & image); + void newFrame(const Image & image); private slots: - int read_frame(); + int read_frame(); private: - void open_device(); + void open_device(); - void close_device(); + void close_device(); - void init_read(unsigned int buffer_size); + void init_read(unsigned int buffer_size); - void init_mmap(); + void init_mmap(); - void init_userp(unsigned int buffer_size); + void init_userp(unsigned int buffer_size); - void init_device(VideoStandard videoStandard, int input); + void init_device(VideoStandard videoStandard, int input); - void uninit_device(); + void uninit_device(); - void start_capturing(); + void start_capturing(); - void stop_capturing(); + void stop_capturing(); - bool process_image(const void *p, int size); + bool process_image(const void *p, int size); - void process_image(const uint8_t *p); + void process_image(const uint8_t *p); - int xioctl(int request, void *arg); + int xioctl(int request, void *arg); - void throw_exception(const std::string &error); + void throw_exception(const std::string &error); - void throw_errno_exception(const std::string &error); + void throw_errno_exception(const std::string &error); private: - enum io_method { - IO_METHOD_READ, - IO_METHOD_MMAP, - IO_METHOD_USERPTR - }; + enum io_method { + IO_METHOD_READ, + IO_METHOD_MMAP, + IO_METHOD_USERPTR + }; - struct buffer { - void *start; - size_t length; - }; + struct buffer { + void *start; + size_t length; + }; private: - const std::string _deviceName; - const io_method _ioMethod; - int _fileDescriptor; - std::vector _buffers; + const std::string _deviceName; + const io_method _ioMethod; + int _fileDescriptor; + std::vector _buffers; - PixelFormat _pixelFormat; - int _width; - int _height; - int _frameByteSize; - int _cropLeft; - int _cropRight; - int _cropTop; - int _cropBottom; - int _frameDecimation; - int _horizontalPixelDecimation; - int _verticalPixelDecimation; - int _noSignalCounterThreshold; + PixelFormat _pixelFormat; + int _width; + int _height; + int _frameByteSize; + int _cropLeft; + int _cropRight; + int _cropTop; + int _cropBottom; + int _frameDecimation; + int _horizontalPixelDecimation; + int _verticalPixelDecimation; + int _noSignalCounterThreshold; - ColorRgb _noSignalThresholdColor; + ColorRgb _noSignalThresholdColor; - VideoMode _mode3D; + VideoMode _mode3D; - int _currentFrame; - int _noSignalCounter; + int _currentFrame; + int _noSignalCounter; - QSocketNotifier * _streamNotifier; + QSocketNotifier * _streamNotifier; }; diff --git a/include/grabber/X11Grabber.h b/include/grabber/X11Grabber.h index 21d36c47..ee857b01 100644 --- a/include/grabber/X11Grabber.h +++ b/include/grabber/X11Grabber.h @@ -2,6 +2,7 @@ // Hyperion-utils includes #include #include +#include // X11 includes #include @@ -10,7 +11,7 @@ class X11Grabber { public: - X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); + X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation); virtual ~X11Grabber(); @@ -19,11 +20,12 @@ public: Image & grab(); private: + ImageResampler _imageResampler; - const unsigned _pixelDecimation; - - const unsigned _cropWidth; - const unsigned _cropHeight; + int _cropLeft; + int _cropRight; + int _cropTop; + int _cropBottom; /// Reference to the X11 display (nullptr if not opened) Display * _x11Display; diff --git a/include/utils/Image.h b/include/utils/Image.h index 5e8b45ad..19b5ae35 100644 --- a/include/utils/Image.h +++ b/include/utils/Image.h @@ -11,207 +11,202 @@ class Image { public: - typedef Pixel_T pixel_type; + typedef Pixel_T pixel_type; - /// - /// Default constructor for an image - /// - Image() : - _width(1), - _height(1), - _pixels(new Pixel_T[2]), - _endOfPixels(_pixels + 1) - { - memset(_pixels, 0, 2*sizeof(Pixel_T)); - } + /// + /// Default constructor for an image + /// + Image() : + _width(1), + _height(1), + _pixels(new Pixel_T[2]), + _endOfPixels(_pixels + 1) + { + memset(_pixels, 0, 2*sizeof(Pixel_T)); + } - /// - /// Constructor for an image with specified width and height - /// - /// @param width The width of the image - /// @param height The height of the image - /// - Image(const unsigned width, const unsigned height) : - _width(width), - _height(height), - _pixels(new Pixel_T[width * height + 1]), - _endOfPixels(_pixels + width * height) - { - memset(_pixels, 0, (_width*_height+1)*sizeof(Pixel_T)); - } + /// + /// Constructor for an image with specified width and height + /// + /// @param width The width of the image + /// @param height The height of the image + /// + Image(const unsigned width, const unsigned height) : + _width(width), + _height(height), + _pixels(new Pixel_T[width * height + 1]), + _endOfPixels(_pixels + width * height) + { + memset(_pixels, 0, (_width*_height+1)*sizeof(Pixel_T)); + } - /// - /// Constructor for an image with specified width and height - /// - /// @param width The width of the image - /// @param height The height of the image - /// @param background The color of the image - /// - Image(const unsigned width, const unsigned height, const Pixel_T background) : - _width(width), - _height(height), - _pixels(new Pixel_T[width * height + 1]), - _endOfPixels(_pixels + width * height) - { - std::fill(_pixels, _endOfPixels, background); - } + /// + /// Constructor for an image with specified width and height + /// + /// @param width The width of the image + /// @param height The height of the image + /// @param background The color of the image + /// + Image(const unsigned width, const unsigned height, const Pixel_T background) : + _width(width), + _height(height), + _pixels(new Pixel_T[width * height + 1]), + _endOfPixels(_pixels + width * height) + { + std::fill(_pixels, _endOfPixels, background); + } - /// - /// Copy constructor for an image - /// - Image(const Image & other) : - _width(other._width), - _height(other._height), - _pixels(new Pixel_T[other._width * other._height + 1]), - _endOfPixels(_pixels + other._width * other._height) - { - memcpy(_pixels, other._pixels, other._width * other._height * sizeof(Pixel_T)); - } + /// + /// Copy constructor for an image + /// + Image(const Image & other) : + _width(other._width), + _height(other._height), + _pixels(new Pixel_T[other._width * other._height + 1]), + _endOfPixels(_pixels + other._width * other._height) + { + memcpy(_pixels, other._pixels, other._width * other._height * sizeof(Pixel_T)); + } - /// - /// Destructor - /// - ~Image() - { - delete[] _pixels; - } + /// + /// Destructor + /// + ~Image() + { + delete[] _pixels; + } - /// - /// Returns the width of the image - /// - /// @return The width of the image - /// - inline unsigned width() const - { - return _width; - } + /// + /// Returns the width of the image + /// + /// @return The width of the image + /// + inline unsigned width() const + { + return _width; + } - /// - /// Returns the height of the image - /// - /// @return The height of the image - /// - inline unsigned height() const - { - return _height; - } + /// + /// Returns the height of the image + /// + /// @return The height of the image + /// + inline unsigned height() const + { + return _height; + } - uint8_t alpha(const unsigned pixel) const - { - return (_pixels + pixel)->red; - } + uint8_t red(const unsigned pixel) const + { + return (_pixels + pixel)->red; + } - uint8_t red(const unsigned pixel) const - { - return (_pixels + pixel)->red; - } + uint8_t green(const unsigned pixel) const + { + return (_pixels + pixel)->green; + } - uint8_t green(const unsigned pixel) const - { - return (_pixels + pixel)->green; - } + uint8_t blue(const unsigned pixel) const + { + return (_pixels + pixel)->blue; + } - uint8_t blue(const unsigned pixel) const - { - return (_pixels + pixel)->blue; - } + /// + /// Returns a const reference to a specified pixel in the image + /// + /// @param x The x index + /// @param y The y index + /// + /// @return const reference to specified pixel + /// + const Pixel_T& operator()(const unsigned x, const unsigned y) const + { + return _pixels[toIndex(x,y)]; + } - /// - /// Returns a const reference to a specified pixel in the image - /// - /// @param x The x index - /// @param y The y index - /// - /// @return const reference to specified pixel - /// - const Pixel_T& operator()(const unsigned x, const unsigned y) const - { - return _pixels[toIndex(x,y)]; - } + /// + /// Returns a reference to a specified pixel in the image + /// + /// @param x The x index + /// @param y The y index + /// + /// @return reference to specified pixel + /// + Pixel_T& operator()(const unsigned x, const unsigned y) + { + return _pixels[toIndex(x,y)]; + } - /// - /// Returns a reference to a specified pixel in the image - /// - /// @param x The x index - /// @param y The y index - /// - /// @return reference to specified pixel - /// - Pixel_T& operator()(const unsigned x, const unsigned y) - { - return _pixels[toIndex(x,y)]; - } + /// Resize the image + /// @param width The width of the image + /// @param height The height of the image + void resize(const unsigned width, const unsigned height) + { + if ((width*height) > (_endOfPixels-_pixels)) + { + delete[] _pixels; + _pixels = new Pixel_T[width*height + 1]; + _endOfPixels = _pixels + width*height; + } - /// Resize the image - /// @param width The width of the image - /// @param height The height of the image - void resize(const unsigned width, const unsigned height) - { - if ((width*height) > (_endOfPixels-_pixels)) - { - delete[] _pixels; - _pixels = new Pixel_T[width*height + 1]; - _endOfPixels = _pixels + width*height; - } + _width = width; + _height = height; + } - _width = width; - _height = height; - } + /// + /// Copies another image into this image. The images should have exactly the same size. + /// + /// @param other The image to copy into this + /// + void copy(const Image& other) + { + assert(other._width == _width); + assert(other._height == _height); - /// - /// Copies another image into this image. The images should have exactly the same size. - /// - /// @param other The image to copy into this - /// - void copy(const Image& other) - { - assert(other._width == _width); - assert(other._height == _height); + memcpy(_pixels, other._pixels, _width*_height*sizeof(Pixel_T)); + } - memcpy(_pixels, other._pixels, _width*_height*sizeof(Pixel_T)); - } + /// + /// Returns a memory pointer to the first pixel in the image + /// @return The memory pointer to the first pixel + /// + Pixel_T* memptr() + { + return _pixels; + } - /// - /// Returns a memory pointer to the first pixel in the image - /// @return The memory pointer to the first pixel - /// - Pixel_T* memptr() - { - return _pixels; - } - - /// - /// Returns a const memory pointer to the first pixel in the image - /// @return The const memory pointer to the first pixel - /// - const Pixel_T* memptr() const - { - return _pixels; - } + /// + /// Returns a const memory pointer to the first pixel in the image + /// @return The const memory pointer to the first pixel + /// + const Pixel_T* memptr() const + { + return _pixels; + } private: - /// - /// Translate x and y coordinate to index of the underlying vector - /// - /// @param x The x index - /// @param y The y index - /// - /// @return The index into the underlying data-vector - /// - inline unsigned toIndex(const unsigned x, const unsigned y) const - { - return y*_width + x; - } + /// + /// Translate x and y coordinate to index of the underlying vector + /// + /// @param x The x index + /// @param y The y index + /// + /// @return The index into the underlying data-vector + /// + inline unsigned toIndex(const unsigned x, const unsigned y) const + { + return y*_width + x; + } private: - /// The width of the image - unsigned _width; - /// The height of the image - unsigned _height; + /// The width of the image + unsigned _width; + /// The height of the image + unsigned _height; - /// The pixels of the image - Pixel_T* _pixels; + /// The pixels of the image + Pixel_T* _pixels; - /// Pointer to the last(extra) pixel - Pixel_T* _endOfPixels; + /// Pointer to the last(extra) pixel + Pixel_T* _endOfPixels; }; diff --git a/include/utils/ImageResampler.h b/include/utils/ImageResampler.h new file mode 100644 index 00000000..b81eb5b7 --- /dev/null +++ b/include/utils/ImageResampler.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include + +class ImageResampler +{ +public: + ImageResampler(); + ~ImageResampler(); + + void setHorizontalPixelDecimation(int decimator); + + void setVerticalPixelDecimation(int decimator); + + void setCropping(int cropLeft, + int cropRight, + int cropTop, + int cropBottom); + + void set3D(VideoMode mode); + + void processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat, + Image & outputImage) const; + +private: + static inline uint8_t clamp(int x); + static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, uint8_t & b); + +private: + int _horizontalDecimation; + int _verticalDecimation; + int _cropLeft; + int _cropRight; + int _cropTop; + int _cropBottom; + VideoMode _videoMode; +}; diff --git a/include/grabber/PixelFormat.h b/include/utils/PixelFormat.h similarity index 70% rename from include/grabber/PixelFormat.h rename to include/utils/PixelFormat.h index 6afc7510..b269dfcb 100644 --- a/include/grabber/PixelFormat.h +++ b/include/utils/PixelFormat.h @@ -9,8 +9,9 @@ enum PixelFormat { PIXELFORMAT_YUYV, PIXELFORMAT_UYVY, - PIXELFORMAT_RGB32, - PIXELFORMAT_NO_CHANGE + PIXELFORMAT_RGB32, + PIXELFORMAT_BGR32, + PIXELFORMAT_NO_CHANGE }; inline PixelFormat parsePixelFormat(std::string pixelFormat) @@ -26,10 +27,14 @@ inline PixelFormat parsePixelFormat(std::string pixelFormat) { return PIXELFORMAT_UYVY; } - else if (pixelFormat == "rgb32") - { - return PIXELFORMAT_RGB32; - } + else if (pixelFormat == "rgb32") + { + return PIXELFORMAT_RGB32; + } + else if (pixelFormat == "bgr32") + { + return PIXELFORMAT_BGR32; + } // return the default NO_CHANGE return PIXELFORMAT_NO_CHANGE; diff --git a/libsrc/grabber/v4l2/CMakeLists.txt b/libsrc/grabber/v4l2/CMakeLists.txt index 540a1217..5c7844e0 100644 --- a/libsrc/grabber/v4l2/CMakeLists.txt +++ b/libsrc/grabber/v4l2/CMakeLists.txt @@ -9,7 +9,6 @@ SET(V4L2_QT_HEADERS SET(V4L2_HEADERS ${CURRENT_HEADER_DIR}/VideoStandard.h - ${CURRENT_HEADER_DIR}/PixelFormat.h ) SET(V4L2_SOURCES diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 698d828f..27eefd43 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -20,19 +20,19 @@ 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); } diff --git a/libsrc/grabber/x11/X11Grabber.cpp b/libsrc/grabber/x11/X11Grabber.cpp index dd991aa6..0787e056 100644 --- a/libsrc/grabber/x11/X11Grabber.cpp +++ b/libsrc/grabber/x11/X11Grabber.cpp @@ -8,16 +8,20 @@ // X11Grabber includes #include -X11Grabber::X11Grabber(const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : - _pixelDecimation(pixelDecimation), - _cropWidth(cropHorizontal), - _cropHeight(cropVertical), +X11Grabber::X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) : + _imageResampler(), + _cropLeft(cropLeft), + _cropRight(cropRight), + _cropTop(cropTop), + _cropBottom(cropBottom), _x11Display(nullptr), _screenWidth(0), _screenHeight(0), _image(0,0) { - // empty + _imageResampler.setHorizontalPixelDecimation(horizontalPixelDecimation); + _imageResampler.setVerticalPixelDecimation(verticalPixelDecimation); + _imageResampler.setCropping(0, 0, 0, 0); // cropping is performed by XGetImage } X11Grabber::~X11Grabber() @@ -51,42 +55,18 @@ Image & X11Grabber::grab() updateScreenDimensions(); - const int croppedWidth = _screenWidth - 2*_cropWidth; - const int croppedHeight = _screenHeight - 2*_cropHeight; + const unsigned croppedWidth = _screenWidth - _cropLeft - _cropRight; + const unsigned croppedHeight = _screenHeight - _cropTop - _cropBottom; // Capture the current screen - XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropWidth, _cropHeight, croppedWidth, croppedHeight, AllPlanes, ZPixmap); + XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropLeft, _cropTop, croppedWidth, croppedHeight, AllPlanes, ZPixmap); if (xImage == nullptr) { std::cerr << "Grab failed" << std::endl; return _image; } - // 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); iYred = uint8_t((pixel >> 16) & 0xff); - outputPtr->green = uint8_t((pixel >> 8) & 0xff); - outputPtr->blue = uint8_t((pixel >> 0) & 0xff); - - // Move to the next output pixel - ++outputPtr; - ++width; - } - ++height; - } - - std::cout << "decimated X11 message: " << width << " x " << height << std::endl; + _imageResampler.processImage(reinterpret_cast(xImage->data), xImage->width, xImage->height, xImage->bytes_per_line, PIXELFORMAT_BGR32, _image); // Cleanup allocated resources of the X11 grab XDestroyImage(xImage); @@ -114,11 +94,5 @@ int X11Grabber::updateScreenDimensions() _screenHeight = window_attributes_return.height; 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 - 1) / _pixelDecimation + 1; - int height = (_screenHeight - 2 * _cropHeight - _pixelDecimation/2 - 1) / _pixelDecimation + 1; - - _image.resize(width, height); - return 0; } diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt index b1f0709e..5549ce74 100644 --- a/libsrc/utils/CMakeLists.txt +++ b/libsrc/utils/CMakeLists.txt @@ -13,6 +13,12 @@ add_library(hyperion-utils ${CURRENT_HEADER_DIR}/Image.h ${CURRENT_HEADER_DIR}/Sleep.h + ${CURRENT_HEADER_DIR}/PixelFormat.h + ${CURRENT_HEADER_DIR}/VideoMode.h + + ${CURRENT_HEADER_DIR}/ImageResampler.h + ${CURRENT_SOURCE_DIR}/ImageResampler.cpp + ${CURRENT_HEADER_DIR}/HsvTransform.h ${CURRENT_SOURCE_DIR}/HsvTransform.cpp ${CURRENT_HEADER_DIR}/RgbChannelTransform.h diff --git a/libsrc/utils/ImageResampler.cpp b/libsrc/utils/ImageResampler.cpp new file mode 100644 index 00000000..d66ca742 --- /dev/null +++ b/libsrc/utils/ImageResampler.cpp @@ -0,0 +1,133 @@ +#include "utils/ImageResampler.h" + +ImageResampler::ImageResampler() : + _horizontalDecimation(1), + _verticalDecimation(1), + _cropLeft(0), + _cropRight(0), + _cropTop(0), + _cropBottom(0), + _videoMode(VIDEO_2D) +{ + +} + +ImageResampler::~ImageResampler() +{ + +} + +void ImageResampler::setHorizontalPixelDecimation(int decimator) +{ + _horizontalDecimation = decimator; +} + +void ImageResampler::setVerticalPixelDecimation(int decimator) +{ + _verticalDecimation = decimator; +} + +void ImageResampler::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) +{ + _cropLeft = cropLeft; + _cropRight = cropRight; + _cropTop = cropTop; + _cropBottom = cropBottom; +} + +void ImageResampler::set3D(VideoMode mode) +{ + _videoMode = mode; +} + +void ImageResampler::processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat, Image &outputImage) const +{ + int cropLeft = _cropLeft; + int cropRight = _cropRight; + int cropTop = _cropTop; + int cropBottom = _cropBottom; + + // handle 3D mode + switch (_videoMode) + { + case VIDEO_3DSBS: + cropRight = width/2; + break; + case VIDEO_3DTAB: + cropBottom = height/2; + break; + default: + break; + } + + // calculate the output size + int outputWidth = (width - cropLeft - cropRight - _horizontalDecimation/2 + _horizontalDecimation - 1) / _horizontalDecimation; + int outputHeight = (height - cropTop - cropBottom - _verticalDecimation/2 + _verticalDecimation - 1) / _verticalDecimation; + outputImage.resize(outputWidth, outputHeight); + + for (int yDest = 0, ySource = cropTop + _verticalDecimation/2; yDest < outputHeight; ySource += _verticalDecimation, ++yDest) + { + for (int xDest = 0, xSource = cropLeft + _horizontalDecimation/2; xDest < outputWidth; xSource += _horizontalDecimation, ++xDest) + { + ColorRgb & rgb = outputImage(xDest, yDest); + + switch (pixelFormat) + { + case PIXELFORMAT_UYVY: + { + int index = lineLength * ySource + xSource * 2; + uint8_t y = data[index+1]; + uint8_t u = ((xSource&1) == 0) ? data[index ] : data[index-2]; + uint8_t v = ((xSource&1) == 0) ? data[index+2] : data[index ]; + yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); + } + break; + case PIXELFORMAT_YUYV: + { + int index = lineLength * ySource + xSource * 2; + uint8_t y = data[index]; + uint8_t u = ((xSource&1) == 0) ? data[index+1] : data[index-1]; + uint8_t v = ((xSource&1) == 0) ? data[index+3] : data[index+1]; + yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); + } + break; + case PIXELFORMAT_RGB32: + { + int index = lineLength * ySource + xSource * 4; + rgb.red = data[index ]; + rgb.green = data[index+1]; + rgb.blue = data[index+2]; + } + break; + case PIXELFORMAT_BGR32: + { + int index = lineLength * ySource + xSource * 4; + rgb.blue = data[index ]; + rgb.green = data[index+1]; + rgb.red = data[index+2]; + } + break; + case PIXELFORMAT_NO_CHANGE: + std::cerr << "Invalid pixel format given" << std::endl; + break; + } + } + } +} + +uint8_t ImageResampler::clamp(int x) +{ + return (x<0) ? 0 : ((x>255) ? 255 : uint8_t(x)); +} + +void ImageResampler::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; + + 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); +} diff --git a/src/hyperion-v4l2/PixelFormatParameter.h b/src/hyperion-v4l2/PixelFormatParameter.h index f9dff93e..3ebccfc7 100644 --- a/src/hyperion-v4l2/PixelFormatParameter.h +++ b/src/hyperion-v4l2/PixelFormatParameter.h @@ -2,7 +2,7 @@ #include // grabber includes -#include +#include using namespace vlofgren; @@ -10,34 +10,34 @@ using namespace vlofgren; typedef vlofgren::PODParameter PixelFormatParameter; namespace vlofgren { - /// Translates a string (as passed on the commandline) to a pixel format - /// - /// @param[in] s The string (as passed on the commandline) - /// @return The pixel format - /// @throws Parameter::ParameterRejected If the string did not result in a pixel format - template<> - PixelFormat PixelFormatParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) - { - QString input = QString::fromStdString(s).toLower(); + /// Translates a string (as passed on the commandline) to a pixel format + /// + /// @param[in] s The string (as passed on the commandline) + /// @return The pixel format + /// @throws Parameter::ParameterRejected If the string did not result in a pixel format + template<> + PixelFormat PixelFormatParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) + { + QString input = QString::fromStdString(s).toLower(); - if (input == "yuyv") - { - return PIXELFORMAT_YUYV; - } - else if (input == "uyvy") - { - return PIXELFORMAT_UYVY; - } - else if (input == "rgb32") - { - return PIXELFORMAT_RGB32; - } - else if (input == "no-change") - { - return PIXELFORMAT_NO_CHANGE; - } + if (input == "yuyv") + { + return PIXELFORMAT_YUYV; + } + else if (input == "uyvy") + { + return PIXELFORMAT_UYVY; + } + else if (input == "rgb32") + { + return PIXELFORMAT_RGB32; + } + else if (input == "no-change") + { + return PIXELFORMAT_NO_CHANGE; + } - throw Parameter::ParameterRejected("Invalid value for pixel format. Valid values are: YUYV, UYVY, RGB32, and NO-CHANGE"); - return PIXELFORMAT_NO_CHANGE; - } + throw Parameter::ParameterRejected("Invalid value for pixel format. Valid values are: YUYV, UYVY, RGB32, and NO-CHANGE"); + return PIXELFORMAT_NO_CHANGE; + } } diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index aacd1c19..fe011f6d 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -102,6 +102,7 @@ int main(int argc, char** argv) return 0; } + // cropping values if not defined if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue()); if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue()); if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue()); diff --git a/src/hyperion-x11/X11Wrapper.cpp b/src/hyperion-x11/X11Wrapper.cpp index abaa4d3f..6f33cc61 100644 --- a/src/hyperion-x11/X11Wrapper.cpp +++ b/src/hyperion-x11/X11Wrapper.cpp @@ -2,9 +2,9 @@ // Hyperion-X11 includes #include "X11Wrapper.h" -X11Wrapper::X11Wrapper(int grabInterval, const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation) : +X11Wrapper::X11Wrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) : _timer(this), - _grabber(cropHorizontal, cropVertical, pixelDecimation) + _grabber(cropLeft, cropRight, cropTop, cropBottom, horizontalPixelDecimation, verticalPixelDecimation) { _timer.setSingleShot(false); _timer.setInterval(grabInterval); diff --git a/src/hyperion-x11/X11Wrapper.h b/src/hyperion-x11/X11Wrapper.h index 6f67d7b9..211dfe18 100644 --- a/src/hyperion-x11/X11Wrapper.h +++ b/src/hyperion-x11/X11Wrapper.h @@ -9,7 +9,7 @@ class X11Wrapper : public QObject { Q_OBJECT public: - X11Wrapper(int grabInterval, const unsigned cropHorizontal, const unsigned cropVertical, const unsigned pixelDecimation); + X11Wrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation); const Image & getScreenshot(); diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index c473b7ed..609a3d9a 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -30,9 +30,13 @@ int main(int argc, char ** argv) ParameterSet & parameters = optionParser.getParameters(); IntParameter & argFps = parameters.add ('f', "framerate", "Cpture frame rate [default=10]"); - IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]"); - IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]"); - IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=16]"); + IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default=0]"); + IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default=0]"); + IntParameter & argCropLeft = parameters.add (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)"); + IntParameter & argCropRight = parameters.add (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)"); + IntParameter & argCropTop = parameters.add (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)"); + IntParameter & argCropBottom = parameters.add (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)"); + IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=8]"); SwitchParameter<> & argScreenshot = parameters.add> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); StringParameter & argAddress = parameters.add ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); IntParameter & argPriority = parameters.add ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); @@ -43,7 +47,7 @@ int main(int argc, char ** argv) argFps.setDefault(10); argCropWidth.setDefault(0); argCropHeight.setDefault(0); - argSizeDecimation.setDefault(16); + argSizeDecimation.setDefault(8); argAddress.setDefault("127.0.0.1:19445"); argPriority.setDefault(800); @@ -57,9 +61,22 @@ int main(int argc, char ** argv) return 0; } + // cropping values if not defined + if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue()); + if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue()); + if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue()); + if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue()); + // Create the X11 grabbing stuff int grabInterval = 1000 / argFps.getValue(); - X11Wrapper x11Wrapper(grabInterval, argCropWidth.getValue(), argCropHeight.getValue(), argSizeDecimation.getValue()); + X11Wrapper x11Wrapper( + grabInterval, + argCropLeft.getValue(), + argCropRight.getValue(), + argCropTop.getValue(), + argCropBottom.getValue(), + argSizeDecimation.getValue(), // horizontal decimation + argSizeDecimation.getValue()); // vertical decimation if (argScreenshot.isSet()) { From 2b6d485ea7192f26dc56524755846e76ac3c845d Mon Sep 17 00:00:00 2001 From: poljvd Date: Tue, 16 Dec 2014 21:30:08 +0100 Subject: [PATCH 12/14] Use ImageResampler in V4L2 grabber Former-commit-id: c6ec92c17b07444a49a303cdced6f59347f98f76 --- include/grabber/V4L2Grabber.h | 12 +-- libsrc/grabber/v4l2/V4L2Grabber.cpp | 109 ++++------------------------ 2 files changed, 17 insertions(+), 104 deletions(-) diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index d130a5e5..c4fc5a27 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -13,6 +13,7 @@ #include #include #include +#include // grabber includes #include @@ -108,22 +109,17 @@ private: PixelFormat _pixelFormat; int _width; int _height; + int _lineLength; int _frameByteSize; - int _cropLeft; - int _cropRight; - int _cropTop; - int _cropBottom; int _frameDecimation; - int _horizontalPixelDecimation; - int _verticalPixelDecimation; int _noSignalCounterThreshold; ColorRgb _noSignalThresholdColor; - VideoMode _mode3D; - int _currentFrame; int _noSignalCounter; QSocketNotifier * _streamNotifier; + + ImageResampler _imageResampler; }; diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 27eefd43..a3202f88 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -18,24 +18,6 @@ #define CLEAR(x) memset(&(x), 0, sizeof(x)) -static inline uint8_t clamp(int 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; - - 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, @@ -52,21 +34,19 @@ V4L2Grabber::V4L2Grabber(const std::string & device, _pixelFormat(pixelFormat), _width(width), _height(height), + _lineLength(-1), _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) + _streamNotifier(nullptr), + _imageResampler() { + _imageResampler.setHorizontalPixelDecimation(std::max(1, horizontalPixelDecimation)); + _imageResampler.setVerticalPixelDecimation(std::max(1, verticalPixelDecimation)); + open_device(); init_device(videoStandard, input); } @@ -81,15 +61,12 @@ V4L2Grabber::~V4L2Grabber() void V4L2Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) { - _cropLeft = cropLeft; - _cropRight = cropRight; - _cropTop = cropTop; - _cropBottom = cropBottom; + _imageResampler.setCropping(cropLeft, cropRight, cropTop, cropBottom); } void V4L2Grabber::set3D(VideoMode mode) { - _mode3D = mode; + _imageResampler.set3D(mode); } void V4L2Grabber::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold) @@ -414,6 +391,9 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) } } + // set the line length + _lineLength = fmt.fmt.pix.bytesperline; + // set the settings if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) { @@ -688,71 +668,8 @@ bool V4L2Grabber::process_image(const void *p, int size) void V4L2Grabber::process_image(const uint8_t * data) { - int width = _width; - int height = _height; - - 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; - - // 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; - - Image image(outputWidth, outputHeight); - - 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); - - 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(0, 0); + _imageResampler.processImage(data, _width, _height, _lineLength, _pixelFormat, image); // check signal (only in center of the resulting image, because some grabbers have noise values along the borders) bool noSignal = true; From 12da40dc58de95a805995c44160016446615421b Mon Sep 17 00:00:00 2001 From: poljvd Date: Wed, 17 Dec 2014 16:18:23 +0100 Subject: [PATCH 13/14] Fix typo Former-commit-id: 134df5769b2bd608d08357c132d2a5cb1763dc4f --- src/hyperion-x11/hyperion-x11.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index 609a3d9a..449f3990 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -29,7 +29,7 @@ int main(int argc, char ** argv) OptionsParser optionParser("X11 capture application for Hyperion"); ParameterSet & parameters = optionParser.getParameters(); - IntParameter & argFps = parameters.add ('f', "framerate", "Cpture frame rate [default=10]"); + IntParameter & argFps = parameters.add ('f', "framerate", "Capture frame rate [default=10]"); IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default=0]"); IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default=0]"); IntParameter & argCropLeft = parameters.add (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)"); From 6248489aede46d4ed6f2f6d4884fffd9a5dc9e4b Mon Sep 17 00:00:00 2001 From: poljvd Date: Wed, 17 Dec 2014 16:43:14 +0100 Subject: [PATCH 14/14] Improve readability of usage message Former-commit-id: 7919b8e95e57518ee5dd60c6de8b6524a1749998 --- dependencies/build/getoptPlusPlus/getoptpp.cc | 268 ++++++++++-------- 1 file changed, 144 insertions(+), 124 deletions(-) diff --git a/dependencies/build/getoptPlusPlus/getoptpp.cc b/dependencies/build/getoptPlusPlus/getoptpp.cc index 5444a325..37212901 100644 --- a/dependencies/build/getoptPlusPlus/getoptpp.cc +++ b/dependencies/build/getoptPlusPlus/getoptpp.cc @@ -17,6 +17,9 @@ #include "getoptpp.h" #include #include +#include +#include +#include using namespace std; @@ -34,107 +37,124 @@ OptionsParser::OptionsParser(const char* programDesc) : fprogramDesc(programDesc OptionsParser::~OptionsParser() {} ParameterSet& OptionsParser::getParameters() { - return parameters; + return parameters; } void OptionsParser::parse(int argc, const char* argv[]) throw(runtime_error) { - argv0 = argv[0]; + argv0 = argv[0]; - if(argc == 1) return; + if(argc == 1) return; - vector v(&argv[1], &argv[argc]); + vector v(&argv[1], &argv[argc]); - ParserState state(/* *this,*/ v); + ParserState state(/* *this,*/ v); - for(; !state.end(); state.advance()) { + for(; !state.end(); state.advance()) { - std::list::iterator i; + std::list::iterator i; - for(i = parameters.parameters.begin(); - i != parameters.parameters.end(); i++) - { - int n = 0; - try - { - n = (*i)->receive(state); - } - catch(Parameter::ExpectedArgument &) - { - throw Parameter::ExpectedArgument(state.get() + ": expected an argument"); - } - catch(Parameter::UnexpectedArgument &) - { - throw Parameter::UnexpectedArgument(state.get() + ": did not expect an argument"); - } - catch(Switchable::SwitchingError &) - { - throw Parameter::ParameterRejected(state.get() + ": parameter already set"); - } - catch(Parameter::ParameterRejected & pr) { - std::string what = pr.what(); - if(what.length()) - { - throw Parameter::ParameterRejected(state.get() + ": " + what); - } - throw Parameter::ParameterRejected(state.get() + " (unspecified error)"); - } + for(i = parameters.parameters.begin(); + i != parameters.parameters.end(); i++) + { + int n = 0; + try + { + n = (*i)->receive(state); + } + catch(Parameter::ExpectedArgument &) + { + throw Parameter::ExpectedArgument(state.get() + ": expected an argument"); + } + catch(Parameter::UnexpectedArgument &) + { + throw Parameter::UnexpectedArgument(state.get() + ": did not expect an argument"); + } + catch(Switchable::SwitchingError &) + { + throw Parameter::ParameterRejected(state.get() + ": parameter already set"); + } + catch(Parameter::ParameterRejected & pr) { + std::string what = pr.what(); + if(what.length()) + { + throw Parameter::ParameterRejected(state.get() + ": " + what); + } + throw Parameter::ParameterRejected(state.get() + " (unspecified error)"); + } - for (int j = 1; j < n; ++j) - { - state.advance(); - } + for (int j = 1; j < n; ++j) + { + state.advance(); + } - if(n != 0) - { - break; - } - } + if(n != 0) + { + break; + } + } - if(i == parameters.parameters.end()) { - std::string file = state.get(); - if(file == "--") { - state.advance(); - break; - } - else if(file.at(0) == '-') - throw Parameter::ParameterRejected(string("Bad parameter: ") + file); - else files.push_back(state.get()); - } - } + if(i == parameters.parameters.end()) { + std::string file = state.get(); + if(file == "--") { + state.advance(); + break; + } + else if(file.at(0) == '-') + throw Parameter::ParameterRejected(string("Bad parameter: ") + file); + else files.push_back(state.get()); + } + } - if(!state.end()) for(; !state.end(); state.advance()) { - files.push_back(state.get()); - } + if(!state.end()) for(; !state.end(); state.advance()) { + files.push_back(state.get()); + } } void OptionsParser::usage() const { - cerr << fprogramDesc << endl; - cerr << "Build time: " << __DATE__ << " " << __TIME__ << endl << endl; - cerr << "Usage: " << programName() << " [OPTIONS]" << endl << endl; + cerr << fprogramDesc << endl; + cerr << "Build time: " << __DATE__ << " " << __TIME__ << endl << endl; + cerr << "Usage: " << programName() << " [OPTIONS]" << endl << endl; - cerr << "Parameters: " << endl; + cerr << "Parameters: " << endl; - std::list::const_iterator i; - for(i = parameters.parameters.begin(); - i != parameters.parameters.end(); i++) - { - cerr.width(33); - cerr << std::left << " " + (*i)->usageLine(); + int totalWidth = 80; + int usageWidth = 33; - cerr.width(40); - cerr << std::left << (*i)->description() << endl; + // read total width from the terminal + struct winsize w; + if (ioctl(0, TIOCGWINSZ, &w) == 0) + { + if (w.ws_col > totalWidth) + totalWidth = w.ws_col; + } - } + std::list::const_iterator i; + for(i = parameters.parameters.begin(); + i != parameters.parameters.end(); i++) + { + cerr.width(usageWidth); + cerr << std::left << " " + (*i)->usageLine(); + + std::string description = (*i)->description(); + while (int(description.length()) > (totalWidth - usageWidth)) + { + size_t pos = description.find_last_of(' ', totalWidth - usageWidth); + cerr << description.substr(0, pos) << std::endl << std::string(usageWidth - 1, ' '); + description = description.substr(pos); + } + cerr << description << endl; + + } } const vector& OptionsParser::getFiles() const { - return files; + return files; } const string& OptionsParser::programName() const { - return argv0; + return argv0; } /* @@ -144,15 +164,15 @@ const string& OptionsParser::programName() const { */ ParameterSet::ParameterSet(const ParameterSet& ps) { - throw new runtime_error("ParameterSet not copyable"); + throw new runtime_error("ParameterSet not copyable"); } ParameterSet::~ParameterSet() { - for(std::list::iterator i = parameters.begin(); - i != parameters.end(); i++) - { - delete *i; - } + for(std::list::iterator i = parameters.begin(); + i != parameters.end(); i++) + { + delete *i; + } } @@ -161,18 +181,18 @@ ParameterSet::~ParameterSet() { */ Parameter& ParameterSet::operator[](char c) const { - for(std::list::const_iterator i = parameters.begin(); i!= parameters.end(); i++) { - if((*i)->shortOption() == c) return *(*i); - } - throw out_of_range("ParameterSet["+string(&c)+string("]")); + for(std::list::const_iterator i = parameters.begin(); i!= parameters.end(); i++) { + if((*i)->shortOption() == c) return *(*i); + } + throw out_of_range("ParameterSet["+string(&c)+string("]")); } Parameter& ParameterSet::operator[](const string& param) const { - for(std::list::const_iterator i = parameters.begin(); i!= parameters.end(); i++) { - if((*i)->longOption() == param) return *(*i); - } - throw out_of_range("ParameterSet["+param+"]"); + for(std::list::const_iterator i = parameters.begin(); i!= parameters.end(); i++) { + if((*i)->longOption() == param) return *(*i); + } + throw out_of_range("ParameterSet["+param+"]"); } @@ -186,29 +206,29 @@ Parameter& ParameterSet::operator[](const string& param) const { ParserState::ParserState(/*OptionsParser &opts, */vector& args) : - /*opts(opts),*/ arguments(args), iterator(args.begin()) + /*opts(opts),*/ arguments(args), iterator(args.begin()) { } const string ParserState::peek() const { - vector::const_iterator next = iterator+1; - if(next != arguments.end()) return *next; - else return ""; + vector::const_iterator next = iterator+1; + if(next != arguments.end()) return *next; + else return ""; } const string ParserState::get() const { - if(!end()) return *iterator; - else return ""; + if(!end()) return *iterator; + else return ""; } void ParserState::advance() { - iterator++; + iterator++; } bool ParserState::end() const { - return iterator == arguments.end(); + return iterator == arguments.end(); } @@ -222,7 +242,7 @@ bool ParserState::end() const { Parameter::Parameter(char shortOption, const std::string & longOption, const std::string & description) : - fshortOption(shortOption), flongOption(longOption), fdescription(description) + fshortOption(shortOption), flongOption(longOption), fdescription(description) { } @@ -250,22 +270,22 @@ MultiSwitchable::~MultiSwitchable() {} void UniquelySwitchable::set() throw (Switchable::SwitchingError) { - if(UniquelySwitchable::isSet()) throw Switchable::SwitchingError(); - fset = true; + if(UniquelySwitchable::isSet()) throw Switchable::SwitchingError(); + fset = true; } UniquelySwitchable::~UniquelySwitchable() {} PresettableUniquelySwitchable::~PresettableUniquelySwitchable() {} bool PresettableUniquelySwitchable::isSet() const { - return UniquelySwitchable::isSet() || fpreset.isSet(); + return UniquelySwitchable::isSet() || fpreset.isSet(); } void PresettableUniquelySwitchable::set() throw (Switchable::SwitchingError) { - UniquelySwitchable::set(); + UniquelySwitchable::set(); } void PresettableUniquelySwitchable::preset() { - fpreset.set(); + fpreset.set(); } /* @@ -279,58 +299,58 @@ void PresettableUniquelySwitchable::preset() { template<> PODParameter::PODParameter(char shortOption, const char *longOption, - const char* description) : CommonParameter(shortOption, longOption, description) { + const char* description) : CommonParameter(shortOption, longOption, description) { } template<> int PODParameter::validate(const string &s) throw(Parameter::ParameterRejected) { - // This is sadly necessary for strto*-functions to operate on - // const char*. The function doesn't write to the memory, though, - // so it's quite safe. + // This is sadly necessary for strto*-functions to operate on + // const char*. The function doesn't write to the memory, though, + // so it's quite safe. - char* cstr = const_cast(s.c_str()); - if(*cstr == '\0') throw ParameterRejected("No argument given"); + char* cstr = const_cast(s.c_str()); + if(*cstr == '\0') throw ParameterRejected("No argument given"); - long l = strtol(cstr, &cstr, 10); - if(*cstr != '\0') throw ParameterRejected("Expected int"); + long l = strtol(cstr, &cstr, 10); + if(*cstr != '\0') throw ParameterRejected("Expected int"); - if(l > INT_MAX || l < INT_MIN) { - throw ParameterRejected("Expected int"); - } + if(l > INT_MAX || l < INT_MIN) { + throw ParameterRejected("Expected int"); + } - return l; + return l; } template<> long PODParameter::validate(const string &s) throw(Parameter::ParameterRejected) { - char* cstr = const_cast(s.c_str()); - if(*cstr == '\0') throw ParameterRejected("No argument given"); + char* cstr = const_cast(s.c_str()); + if(*cstr == '\0') throw ParameterRejected("No argument given"); - long l = strtol(cstr, &cstr, 10); - if(*cstr != '\0') throw ParameterRejected("Expected long"); + long l = strtol(cstr, &cstr, 10); + if(*cstr != '\0') throw ParameterRejected("Expected long"); - return l; + return l; } template<> double PODParameter::validate(const string &s) throw(Parameter::ParameterRejected) { - char* cstr = const_cast(s.c_str()); - if(*cstr == '\0') throw ParameterRejected("No argument given"); + char* cstr = const_cast(s.c_str()); + if(*cstr == '\0') throw ParameterRejected("No argument given"); - double d = strtod(cstr, &cstr); - if(*cstr != '\0') throw ParameterRejected("Expected double"); + double d = strtod(cstr, &cstr); + if(*cstr != '\0') throw ParameterRejected("Expected double"); - return d; + return d; } template<> string PODParameter::validate(const string &s) throw(Parameter::ParameterRejected) { - return s; + return s; }