Merge pull request #518 from redPanther/proto_json_forwarder

multi device support via forwarding proto/json messages

Former-commit-id: bb5e8c110b8e34f062cbcc8c5b2985d20d09db56
This commit is contained in:
brindosch 2016-03-08 15:49:38 +01:00
commit d350ed6eef
47 changed files with 985 additions and 193 deletions

View File

@ -24,11 +24,11 @@ mkdir "$HYPERION_DIR/build"
cd "$HYPERION_DIR/build" cd "$HYPERION_DIR/build"
# run cmake to generate make files on the raspberry pi # run cmake to generate make files on the raspberry pi
cmake .. cmake -DCMAKE_BUILD_TYPE=Release -Wno-dev ..
# or if you are not compiling on the raspberry pi and need to disable the Dispmanx grabber and support for spi devices # or if you are not compiling on the raspberry pi and need to disable the Dispmanx grabber and support for spi devices
cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_X11=ON .. cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_X11=ON -DCMAKE_BUILD_TYPE=Release -Wno-dev ..
# as an alternative for the dispmanx grabber on non-rpi devices (e.g. cubox-i) you could try the framebuffer grabber # as an alternative for the dispmanx grabber on non-rpi devices (e.g. cubox-i) you could try the framebuffer grabber
cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_FB=ON .. cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_FB=ON -DCMAKE_BUILD_TYPE=Release -Wno-dev ..
# for OSX build you need XCode, qt4 libraries and cmake. You can use macport (homebrew might work too) to install them # for OSX build you need XCode, qt4 libraries and cmake. You can use macport (homebrew might work too) to install them
sudo port install qt4-mac sudo port install qt4-mac
sudo port install cmake sudo port install cmake
@ -36,6 +36,12 @@ cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_V4L2=OFF -DENABLE_OSX=O
# run make to build Hyperion # run make to build Hyperion
make make
# or if you have a system with more then 1 cpu core
make -j 4
# "4" is the number of cpu cores (e.g. 4 on RPi2), this makes compile faster
#after compile, to remove any stuff not needed for a release version.
strip bin/*
# The binaries are build in "$HYPERION_DIR/build/bin". You could copy those to /usr/bin # The binaries are build in "$HYPERION_DIR/build/bin". You could copy those to /usr/bin
sudo cp ./bin/hyperion-remote /usr/bin/ sudo cp ./bin/hyperion-remote /usr/bin/

View File

@ -23,6 +23,20 @@
"colorOrder" : "rgb" "colorOrder" : "rgb"
}, },
/// Configuration for message forwarding to other hyperions
/// protobuffer and json remote interface are forwarded to configured hosts
/// 'proto' is mostly used for video streams and 'json' for effects
///
/// ** pay attention which port you use. use correct ports for protols **
///
/// * 'proto' : list of host in form of <ip>:<port>
/// * 'json' : list of host in form of <ip>:<port>
/// "forwarder" :
/// {
/// "proto" : [ "127.0.0.1:19445","192.168.178.88:19445" ],
/// "json" : [ "127.0.0.1:19444","192.168.178.88:19444" ]
/// },
/// Color manipulation configuration used to tune the output colors to specific surroundings. /// Color manipulation configuration used to tune the output colors to specific surroundings.
/// The configuration contains a list of color-transforms. Each transform contains the /// The configuration contains a list of color-transforms. Each transform contains the
/// following fields: /// following fields:

View File

@ -19,6 +19,21 @@
"colorOrder" : "rgb" "colorOrder" : "rgb"
}, },
/// Configuration for message forwarding to other hyperions
/// protobuffer and json remote interface are forwarded to configured hosts
/// 'proto' is mostly used for video streams and 'json' for effects
///
/// ** pay attention which port you use. use correct ports for protols **
///
/// * 'proto' : list of host in form of <ip>:<port>
/// * 'json' : list of host in form of <ip>:<port>
/// "forwarder" :
/// {
/// "proto" : [ "127.0.0.1:19445","192.168.178.88:19445" ],
/// "json" : [ "127.0.0.1:19444","192.168.178.88:19444" ]
/// },
/// Color manipulation configuration used to tune the output colors to specific surroundings. /// Color manipulation configuration used to tune the output colors to specific surroundings.
/// The configuration contains a list of color-transforms. Each transform contains the /// The configuration contains a list of color-transforms. Each transform contains the
/// following fields: /// following fields:

View File

@ -68,6 +68,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;
@ -91,4 +94,7 @@ private:
/// Pointer to Hyperion for writing led values /// Pointer to Hyperion for writing led values
Hyperion * _hyperion; Hyperion * _hyperion;
// forwarding enabled
bool _forward;
}; };

View File

@ -68,6 +68,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;
@ -91,4 +94,7 @@ private:
/// Pointer to Hyperion for writing led values /// Pointer to Hyperion for writing led values
Hyperion * _hyperion; Hyperion * _hyperion;
// forwarding enabled
bool _forward;
}; };

View File

@ -68,6 +68,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;

View File

@ -69,6 +69,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;

View File

@ -44,6 +44,7 @@ public slots:
signals: signals:
void emitColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms); void emitColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms);
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private slots: private slots:
void newFrame(const Image<ColorRgb> & image); void newFrame(const Image<ColorRgb> & image);

View File

@ -13,6 +13,7 @@
// Hyperion includes // Hyperion includes
#include <hyperion/LedString.h> #include <hyperion/LedString.h>
#include <hyperion/PriorityMuxer.h> #include <hyperion/PriorityMuxer.h>
#include <hyperion/MessageForwarder.h>
// Effect engine includes // Effect engine includes
#include <effectengine/EffectDefinition.h> #include <effectengine/EffectDefinition.h>
@ -125,6 +126,10 @@ public slots:
/// Tell Hyperion that the transforms have changed and the leds need to be updated /// Tell Hyperion that the transforms have changed and the leds need to be updated
void transformsUpdated(); void transformsUpdated();
/// Returns MessageForwarder Object
/// @return instance of message forwarder object
MessageForwarder * getForwarder();
/// ///
/// Clears the given priority channel. This will switch the led-colors to the colors of the next /// Clears the given priority channel. This will switch the led-colors to the colors of the next
/// lower priority channel (or off if no more channels are set) /// lower priority channel (or off if no more channels are set)
@ -168,6 +173,7 @@ public:
static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig); static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig);
static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice); static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice);
static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig);
signals: signals:
/// Signal which is emitted when a priority channel is actively cleared /// Signal which is emitted when a priority channel is actively cleared
@ -201,6 +207,9 @@ private:
/// Effect engine /// Effect engine
EffectEngine * _effectEngine; EffectEngine * _effectEngine;
// proto and json Message forwarder
MessageForwarder * _messageForwarder;
/// The timer for handling priority channel timeouts /// The timer for handling priority channel timeouts
QTimer _timer; QTimer _timer;
}; };

View File

@ -0,0 +1,38 @@
#pragma once
// STL includes
#include <vector>
#include <map>
#include <cstdint>
#include <limits>
// QT includes
#include <QList>
#include <QStringList>
#include <QHostAddress>
// Utils includes
#include <utils/ColorRgb.h>
class MessageForwarder
{
public:
struct JsonSlaveAddress {
QHostAddress addr;
quint16 port;
};
MessageForwarder();
~MessageForwarder();
void addJsonSlave(std::string slave);
void addProtoSlave(std::string slave);
bool protoForwardingEnabled();
QStringList getProtoSlaves();
QList<MessageForwarder::JsonSlaveAddress> getJsonSlaves();
private:
QStringList _protoSlaves;
QList<MessageForwarder::JsonSlaveAddress> _jsonSlaves;
};

View File

@ -7,6 +7,7 @@
#include <QColor> #include <QColor>
#include <QImage> #include <QImage>
#include <QTcpSocket> #include <QTcpSocket>
#include <QTimer>
#include <QMap> #include <QMap>
// hyperion util // hyperion util
@ -19,8 +20,11 @@
/// ///
/// Connection class to setup an connection to the hyperion server and execute commands /// Connection class to setup an connection to the hyperion server and execute commands
/// ///
class ProtoConnection class ProtoConnection : public QObject
{ {
Q_OBJECT
public: public:
/// ///
/// Constructor /// Constructor
@ -67,10 +71,6 @@ public:
/// ///
void clearAll(); void clearAll();
private:
/// Try to connect to the Hyperion host
void connectToHost();
/// ///
/// Send a command message and receive its reply /// Send a command message and receive its reply
/// ///
@ -78,6 +78,13 @@ private:
/// ///
void sendMessage(const proto::HyperionRequest & message); void sendMessage(const proto::HyperionRequest & message);
private slots:
/// Try to connect to the Hyperion host
void connectToHost();
private:
/// ///
/// Parse a reply message /// Parse a reply message
/// ///
@ -99,4 +106,7 @@ private:
/// Skip receiving reply messages from Hyperion if set /// Skip receiving reply messages from Hyperion if set
bool _skipReply; bool _skipReply;
QTimer _timer;
QAbstractSocket::SocketState _prevSocketState;
}; };

View File

@ -6,11 +6,23 @@
// Qt includes // Qt includes
#include <QTcpServer> #include <QTcpServer>
#include <QSet> #include <QSet>
#include <QList>
#include <QStringList>
// Hyperion includes // Hyperion includes
#include <hyperion/Hyperion.h> #include <hyperion/Hyperion.h>
// hyperion includes
#include <utils/Image.h>
#include <utils/ColorRgb.h>
// forward decl
class ProtoClientConnection; class ProtoClientConnection;
class ProtoConnection;
namespace proto {
class HyperionRequest;
}
/// ///
/// This class creates a TCP server which accepts connections wich can then send /// This class creates a TCP server which accepts connections wich can then send
@ -35,6 +47,9 @@ public:
/// ///
uint16_t getPort() const; uint16_t getPort() const;
public slots:
void sendImageToProtoSlaves(int priority, const Image<ColorRgb> & image, int duration_ms);
private slots: private slots:
/// ///
/// Slot which is called when a client tries to create a new connection /// Slot which is called when a client tries to create a new connection
@ -47,6 +62,8 @@ private slots:
/// ///
void closedConnection(ProtoClientConnection * connection); void closedConnection(ProtoClientConnection * connection);
void newMessage(const proto::HyperionRequest * message);
private: private:
/// Hyperion instance /// Hyperion instance
Hyperion * _hyperion; Hyperion * _hyperion;
@ -56,4 +73,9 @@ private:
/// List with open connections /// List with open connections
QSet<ProtoClientConnection *> _openConnections; QSet<ProtoClientConnection *> _openConnections;
QStringList _forwardClients;
/// Hyperion proto connection object for forwarding
QList<ProtoConnection*> _proxy_connections;
}; };

View File

@ -5,6 +5,8 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <utils/ColorRgb.h>
template <typename Pixel_T> template <typename Pixel_T>
class Image class Image
@ -183,6 +185,25 @@ public:
{ {
return _pixels; return _pixels;
} }
///
/// Convert image of any color order to a RGB image.
///
/// @param[out] image The image that buffers the output
///
void toRgb(Image<ColorRgb>& image)
{
image.resize(_width, _height);
const unsigned imageSize = _width * _height;
for (unsigned idx=0; idx<imageSize; idx++)
{
const Pixel_T color = memptr()[idx];
image.memptr()[idx] = ColorRgb{color.red, color.green, color.blue};
}
}
private: private:
/// ///

View File

@ -13,7 +13,7 @@
#include <sys/types.h> #include <sys/types.h>
// Local includes // Local includes
#include "AmlogicGrabber.h" #include <grabber/AmlogicGrabber.h>
// Flags copied from 'include/linux/amlogic/amports/amvideocap.h' at https://github.com/codesnake/linux-amlogic // Flags copied from 'include/linux/amlogic/amports/amvideocap.h' at https://github.com/codesnake/linux-amlogic
#define AMVIDEOCAP_IOC_MAGIC 'V' #define AMVIDEOCAP_IOC_MAGIC 'V'

View File

@ -9,7 +9,7 @@
// Amlogic grabber includes // Amlogic grabber includes
#include <grabber/AmlogicWrapper.h> #include <grabber/AmlogicWrapper.h>
#include "AmlogicGrabber.h" #include <grabber/AmlogicGrabber.h>
AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) : AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) :
@ -26,6 +26,7 @@ AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeig
// Configure the timer to generate events every n milliseconds // Configure the timer to generate events every n milliseconds
_timer.setInterval(_updateInterval_ms); _timer.setInterval(_updateInterval_ms);
_timer.setSingleShot(false); _timer.setSingleShot(false);
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
_processor->setSize(grabWidth, grabHeight); _processor->setSize(grabWidth, grabHeight);
@ -55,8 +56,14 @@ void AmlogicWrapper::action()
return; return;
} }
_processor->process(_image, _ledColors); if ( _forward )
{
Image<ColorRgb> image_rgb;
_image.toRgb(image_rgb);
emit emitImage(_priority, image_rgb, _timeout_ms);
}
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }

View File

@ -7,7 +7,7 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic)
SET(AmlogicQT_HEADERS ${CURRENT_HEADER_DIR}/AmlogicWrapper.h) SET(AmlogicQT_HEADERS ${CURRENT_HEADER_DIR}/AmlogicWrapper.h)
SET(AmlogicHEADERS SET(AmlogicHEADERS
${CURRENT_SOURCE_DIR}/AmlogicGrabber.h ${CURRENT_HEADER_DIR}/AmlogicGrabber.h
) )
SET(AmlogicSOURCES SET(AmlogicSOURCES

View File

@ -1,40 +1,41 @@
# Find the BCM-package (VC control) # Find the BCM-package (VC control)
find_package(BCM REQUIRED) find_package(BCM REQUIRED)
include_directories(${BCM_INCLUDE_DIRS}) include_directories(${BCM_INCLUDE_DIRS})
# Define the current source locations # Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
# Group the headers that go through the MOC compiler # Group the headers that go through the MOC compiler
SET(DispmanxGrabberQT_HEADERS SET(DispmanxGrabberQT_HEADERS
${CURRENT_HEADER_DIR}/DispmanxWrapper.h ${CURRENT_HEADER_DIR}/DispmanxWrapper.h
) )
SET(DispmanxGrabberHEADERS SET(DispmanxGrabberHEADERS
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.h ${CURRENT_HEADER_DIR}/DispmanxFrameGrabber.h
) )
SET(DispmanxGrabberSOURCES SET(DispmanxGrabberSOURCES
${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp ${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp ${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp
) )
if(ENABLE_QT5) if(ENABLE_QT5)
QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
else(ENABLE_QT5) else(ENABLE_QT5)
QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
endif(ENABLE_QT5) endif(ENABLE_QT5)
add_library(dispmanx-grabber add_library(dispmanx-grabber
${DispmanxGrabberHEADERS} ${DispmanxGrabberHEADERS}
${DispmanxGrabberQT_HEADERS} ${DispmanxGrabberQT_HEADERS}
${DispmanxGrabberHEADERS_MOC} ${DispmanxGrabberHEADERS_MOC}
${DispmanxGrabberSOURCES} ${DispmanxGrabberSOURCES}
) )
target_link_libraries(dispmanx-grabber target_link_libraries(dispmanx-grabber
hyperion hyperion
${QT_LIBRARIES} ${QT_LIBRARIES}
${BCM_LIBRARIES}) ${BCM_LIBRARIES}
)

View File

@ -4,7 +4,7 @@
#include <iostream> #include <iostream>
// Local includes // Local includes
#include "DispmanxFrameGrabber.h" #include "grabber/DispmanxFrameGrabber.h"
DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height) : DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height) :
_vc_display(0), _vc_display(0),

View File

@ -9,7 +9,7 @@
// Dispmanx grabber includes // Dispmanx grabber includes
#include <grabber/DispmanxWrapper.h> #include <grabber/DispmanxWrapper.h>
#include "DispmanxFrameGrabber.h" #include <grabber/DispmanxFrameGrabber.h>
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) : DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) :
@ -28,6 +28,7 @@ DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHe
_timer.setSingleShot(false); _timer.setSingleShot(false);
_processor->setSize(grabWidth, grabHeight); _processor->setSize(grabWidth, grabHeight);
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
// Connect the QTimer to this // Connect the QTimer to this
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(action())); QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(action()));
@ -51,10 +52,17 @@ void DispmanxWrapper::action()
// Grab frame into the allocated image // Grab frame into the allocated image
_frameGrabber->grabFrame(_image); _frameGrabber->grabFrame(_image);
_processor->process(_image, _ledColors); if ( _forward )
{
Image<ColorRgb> image_rgb;
_image.toRgb(image_rgb);
emit emitImage(_priority, image_rgb, _timeout_ms);
}
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }
void DispmanxWrapper::stop() void DispmanxWrapper::stop()
{ {
// Stop the timer, effectivly stopping the process // Stop the timer, effectivly stopping the process

View File

@ -46,8 +46,9 @@ void FramebufferWrapper::action()
// Grab frame into the allocated image // Grab frame into the allocated image
_frameGrabber->grabFrame(_image); _frameGrabber->grabFrame(_image);
_processor->process(_image, _ledColors); emit emitImage(_priority, _image, _timeout_ms);
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }
void FramebufferWrapper::stop() void FramebufferWrapper::stop()

View File

@ -46,8 +46,9 @@ void OsxWrapper::action()
// Grab frame into the allocated image // Grab frame into the allocated image
_frameGrabber->grabFrame(_image); _frameGrabber->grabFrame(_image);
_processor->process(_image, _ledColors); emit emitImage(_priority, _image, _timeout_ms);
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }
void OsxWrapper::stop() void OsxWrapper::stop()

View File

@ -94,6 +94,9 @@ void V4L2Wrapper::newFrame(const Image<ColorRgb> &image)
// process the new image // process the new image
_processor->process(image, _ledColors); _processor->process(image, _ledColors);
// forward to other hyperions
emit emitImage(_priority, image, _timeout_ms);
// send colors to Hyperion // send colors to Hyperion
emit emitColors(_priority, _ledColors, _timeout_ms); emit emitColors(_priority, _ledColors, _timeout_ms);
} }

View File

@ -1,66 +1,68 @@
# Define the current source locations # Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
# Group the headers that go through the MOC compiler # Group the headers that go through the MOC compiler
SET(Hyperion_QT_HEADERS SET(Hyperion_QT_HEADERS
${CURRENT_HEADER_DIR}/Hyperion.h ${CURRENT_HEADER_DIR}/Hyperion.h
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h
) )
SET(Hyperion_HEADERS SET(Hyperion_HEADERS
${CURRENT_HEADER_DIR}/ImageProcessor.h ${CURRENT_HEADER_DIR}/ImageProcessor.h
${CURRENT_HEADER_DIR}/ImageProcessorFactory.h ${CURRENT_HEADER_DIR}/ImageProcessorFactory.h
${CURRENT_HEADER_DIR}/ImageToLedsMap.h ${CURRENT_HEADER_DIR}/ImageToLedsMap.h
${CURRENT_HEADER_DIR}/LedString.h ${CURRENT_HEADER_DIR}/LedString.h
${CURRENT_HEADER_DIR}/PriorityMuxer.h ${CURRENT_HEADER_DIR}/PriorityMuxer.h
${CURRENT_SOURCE_DIR}/MultiColorTransform.h ${CURRENT_SOURCE_DIR}/MultiColorTransform.h
) ${CURRENT_HEADER_DIR}/MessageForwarder.h
)
SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/Hyperion.cpp SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/ImageProcessor.cpp ${CURRENT_SOURCE_DIR}/Hyperion.cpp
${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp ${CURRENT_SOURCE_DIR}/ImageProcessor.cpp
${CURRENT_SOURCE_DIR}/LedString.cpp ${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp ${CURRENT_SOURCE_DIR}/LedString.cpp
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp ${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp
) ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp
${CURRENT_SOURCE_DIR}/MessageForwarder.cpp
set(Hyperion_RESOURCES )
${CURRENT_SOURCE_DIR}/resource.qrc
) set(Hyperion_RESOURCES
${CURRENT_SOURCE_DIR}/resource.qrc
if(ENABLE_QT5) )
QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") if(ENABLE_QT5)
else(ENABLE_QT5) QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") else(ENABLE_QT5)
endif(ENABLE_QT5) QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
add_library(hyperion endif(ENABLE_QT5)
${Hyperion_HEADERS}
${Hyperion_QT_HEADERS} add_library(hyperion
${Hyperion_HEADERS_MOC} ${Hyperion_HEADERS}
${Hyperion_SOURCES} ${Hyperion_QT_HEADERS}
${Hyperion_RESOURCES_RCC} ${Hyperion_HEADERS_MOC}
) ${Hyperion_SOURCES}
${Hyperion_RESOURCES_RCC}
if(ENABLE_QT5) )
qt5_use_modules(hyperion Widgets)
endif(ENABLE_QT5) if(ENABLE_QT5)
qt5_use_modules(hyperion Widgets)
target_link_libraries(hyperion endif(ENABLE_QT5)
blackborder
hyperion-utils target_link_libraries(hyperion
leddevice blackborder
effectengine hyperion-utils
serialport leddevice
${QT_LIBRARIES} effectengine
) serialport
${QT_LIBRARIES}
)

View File

@ -267,12 +267,45 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig,
} }
MessageForwarder * Hyperion::createMessageForwarder(const Json::Value & forwarderConfig)
{
MessageForwarder * forwarder = new MessageForwarder();
if ( ! forwarderConfig.isNull() )
{
if ( ! forwarderConfig["json"].isNull() && forwarderConfig["json"].isArray() )
{
for (const Json::Value& addr : forwarderConfig["json"])
{
std::cout << "Json forward to " << addr.asString() << std::endl;
forwarder->addJsonSlave(addr.asString());
}
}
if ( ! forwarderConfig["proto"].isNull() && forwarderConfig["proto"].isArray() )
{
for (const Json::Value& addr : forwarderConfig["proto"])
{
std::cout << "Proto forward to " << addr.asString() << std::endl;
forwarder->addProtoSlave(addr.asString());
}
}
}
return forwarder;
}
MessageForwarder * Hyperion::getForwarder()
{
return _messageForwarder;
}
Hyperion::Hyperion(const Json::Value &jsonConfig) : Hyperion::Hyperion(const Json::Value &jsonConfig) :
_ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))), _ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))),
_muxer(_ledString.leds().size()), _muxer(_ledString.leds().size()),
_raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])), _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])),
_device(LedDeviceFactory::construct(jsonConfig["device"])), _device(LedDeviceFactory::construct(jsonConfig["device"])),
_effectEngine(nullptr), _effectEngine(nullptr),
_messageForwarder(createMessageForwarder(jsonConfig["forwarder"])),
_timer() _timer()
{ {
if (!_raw2ledTransform->verifyTransforms()) if (!_raw2ledTransform->verifyTransforms())
@ -314,6 +347,9 @@ Hyperion::~Hyperion()
// delete the color transform // delete the color transform
delete _raw2ledTransform; delete _raw2ledTransform;
// delete the message forwarder
delete _messageForwarder;
} }
unsigned Hyperion::getLedCount() const unsigned Hyperion::getLedCount() const

View File

@ -0,0 +1,51 @@
// STL includes
#include <stdexcept>
#include <hyperion/MessageForwarder.h>
MessageForwarder::MessageForwarder()
{
}
MessageForwarder::~MessageForwarder()
{
}
void MessageForwarder::addJsonSlave(std::string slave)
{
QStringList parts = QString(slave.c_str()).split(":");
if (parts.size() != 2)
throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(slave.c_str()).toStdString());
bool ok;
quint16 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());
JsonSlaveAddress c;
c.addr = QHostAddress(parts[0]);
c.port = port;
_jsonSlaves << c;
}
void MessageForwarder::addProtoSlave(std::string slave)
{
_protoSlaves << QString(slave.c_str());
}
QStringList MessageForwarder::getProtoSlaves()
{
return _protoSlaves;
}
QList<MessageForwarder::JsonSlaveAddress> MessageForwarder::getJsonSlaves()
{
return _jsonSlaves;
}
bool MessageForwarder::protoForwardingEnabled()
{
return ! _protoSlaves.empty();
}

View File

@ -16,6 +16,7 @@
// hyperion util includes // hyperion util includes
#include <hyperion/ImageProcessorFactory.h> #include <hyperion/ImageProcessorFactory.h>
#include <hyperion/ImageProcessor.h> #include <hyperion/ImageProcessor.h>
#include <hyperion/MessageForwarder.h>
#include <hyperion/ColorTransform.h> #include <hyperion/ColorTransform.h>
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
@ -250,8 +251,27 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
handleNotImplemented(); handleNotImplemented();
} }
void JsonClientConnection::forwardJsonMessage(const Json::Value & message)
{
QTcpSocket client;
QList<MessageForwarder::JsonSlaveAddress> list = _hyperion->getForwarder()->getJsonSlaves();
for ( int i=0; i<list.size(); i++ )
{
client.connectToHost(list.at(i).addr, list.at(i).port);
if ( client.waitForConnected(500) )
{
sendMessage(message,&client);
client.close();
}
}
}
void JsonClientConnection::handleColorCommand(const Json::Value &message) void JsonClientConnection::handleColorCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt(); int duration = message.get("duration", -1).asInt();
@ -289,6 +309,8 @@ void JsonClientConnection::handleColorCommand(const Json::Value &message)
void JsonClientConnection::handleImageCommand(const Json::Value &message) void JsonClientConnection::handleImageCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt(); int duration = message.get("duration", -1).asInt();
@ -320,6 +342,8 @@ void JsonClientConnection::handleImageCommand(const Json::Value &message)
void JsonClientConnection::handleEffectCommand(const Json::Value &message) void JsonClientConnection::handleEffectCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt(); int duration = message.get("duration", -1).asInt();
@ -418,6 +442,8 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
void JsonClientConnection::handleClearCommand(const Json::Value &message) void JsonClientConnection::handleClearCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
@ -428,8 +454,10 @@ void JsonClientConnection::handleClearCommand(const Json::Value &message)
sendSuccessReply(); sendSuccessReply();
} }
void JsonClientConnection::handleClearallCommand(const Json::Value &) void JsonClientConnection::handleClearallCommand(const Json::Value & message)
{ {
forwardJsonMessage(message);
// clear priority // clear priority
_hyperion->clearall(); _hyperion->clearall();
@ -530,10 +558,51 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
response.append(serializedReply.c_str(), serializedReply.length()); response.append(serializedReply.c_str(), serializedReply.length());
_socket->write(response.data(), response.length()); _socket->write(response.data(), response.length());
} }
} }
void JsonClientConnection::sendMessage(const Json::Value & message, QTcpSocket * socket)
{
// serialize message (FastWriter already appends a newline)
std::string serializedMessage = Json::FastWriter().write(message);
// write message
socket->write(serializedMessage.c_str());
if (!socket->waitForBytesWritten())
{
//std::cout << "Error while writing data to host" << std::endl;
return;
}
// read reply data
QByteArray serializedReply;
while (!serializedReply.contains('\n'))
{
// receive reply
if (!socket->waitForReadyRead())
{
//std::cout << "Error while reading data from host" << std::endl;
return;
}
serializedReply += socket->readAll();
}
int bytes = serializedReply.indexOf('\n') + 1; // Find the end of message
// parse reply data
Json::Reader jsonReader;
Json::Value reply;
if (!jsonReader.parse(serializedReply.constData(), serializedReply.constData() + bytes, reply))
{
//std::cout << "Error while parsing reply: invalid json" << std::endl;
return;
}
}
void JsonClientConnection::sendSuccessReply() void JsonClientConnection::sendSuccessReply()
{ {
// create reply // create reply

View File

@ -124,6 +124,7 @@ private:
/// @param message The JSON message to send /// @param message The JSON message to send
/// ///
void sendMessage(const Json::Value & message); void sendMessage(const Json::Value & message);
void sendMessage(const Json::Value & message, QTcpSocket * socket);
/// ///
/// Send a standard reply indicating success /// Send a standard reply indicating success
@ -147,6 +148,11 @@ private:
/// ///
void handleWebSocketFrame(); void handleWebSocketFrame();
///
/// forward json message
///
void forwardJsonMessage(const Json::Value & message);
private: private:
/// ///
/// Check if a JSON messag is valid according to a given JSON schema /// Check if a JSON messag is valid according to a given JSON schema

View File

@ -16,6 +16,14 @@ JsonServer::JsonServer(Hyperion *hyperion, uint16_t port) :
throw std::runtime_error("Json server could not bind to port"); throw std::runtime_error("Json server could not bind to port");
} }
QList<MessageForwarder::JsonSlaveAddress> list = _hyperion->getForwarder()->getJsonSlaves();
for ( int i=0; i<list.size(); i++ )
{
if ( list.at(i).addr == QHostAddress::LocalHost && list.at(i).port == port ) {
throw std::runtime_error("Loop between proto server and forwarder detected. Fix your config!");
}
}
// Set trigger for incoming connections // Set trigger for incoming connections
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection())); connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));

View File

@ -1,8 +1,8 @@
#include "LedDeviceFadeCandy.h" #include "LedDeviceFadeCandy.h"
static const ssize_t MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds static const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds
static const ssize_t OPC_SET_PIXELS = 0; // OPC command codes static const unsigned OPC_SET_PIXELS = 0; // OPC command codes
static const ssize_t OPC_HEADER_SIZE = 4; // OPC header size static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) : LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) :

View File

@ -81,6 +81,9 @@ void ProtoClientConnection::socketClosed()
void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message) void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message)
{ {
// forward messages
emit newMessage(&message);
switch (message.command()) switch (message.command())
{ {
case proto::HyperionRequest::COLOR: case proto::HyperionRequest::COLOR:

View File

@ -6,12 +6,14 @@
// Qt includes // Qt includes
#include <QByteArray> #include <QByteArray>
#include <QTcpSocket> #include <QTcpSocket>
#include <QStringList>
// Hyperion includes // Hyperion includes
#include <hyperion/Hyperion.h> #include <hyperion/Hyperion.h>
// proto includes // proto includes
#include "message.pb.h" #include "message.pb.h"
#include "protoserver/ProtoConnection.h"
class ImageProcessor; class ImageProcessor;
@ -41,6 +43,7 @@ signals:
/// @param connection This connection object /// @param connection This connection object
/// ///
void connectionClosed(ProtoClientConnection * connection); void connectionClosed(ProtoClientConnection * connection);
void newMessage(const proto::HyperionRequest * message);
private slots: private slots:
/// ///

View File

@ -9,7 +9,8 @@
ProtoConnection::ProtoConnection(const std::string & a) : ProtoConnection::ProtoConnection(const std::string & a) :
_socket(), _socket(),
_skipReply(false) _skipReply(false),
_prevSocketState(QAbstractSocket::UnconnectedState)
{ {
QString address(a.c_str()); QString address(a.c_str());
QStringList parts = address.split(":"); QStringList parts = address.split(":");
@ -29,10 +30,18 @@ ProtoConnection::ProtoConnection(const std::string & a) :
// try to connect to host // try to connect to host
std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl; std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
connectToHost(); connectToHost();
// start the connection timer
_timer.setInterval(5000);
_timer.setSingleShot(false);
connect(&_timer,SIGNAL(timeout()), this, SLOT(connectToHost()) );
_timer.start();
} }
ProtoConnection::~ProtoConnection() ProtoConnection::~ProtoConnection()
{ {
_timer.stop();
_socket.close(); _socket.close();
} }
@ -91,20 +100,37 @@ void ProtoConnection::clearAll()
void ProtoConnection::connectToHost() void ProtoConnection::connectToHost()
{ {
_socket.connectToHost(_host, _port); // try connection only when
if (_socket.waitForConnected()) { if (_socket.state() == QAbstractSocket::UnconnectedState)
std::cout << "Connected to Hyperion host" << std::endl; {
_socket.connectToHost(_host, _port);
//_socket.waitForConnected(1000);
} }
} }
void ProtoConnection::sendMessage(const proto::HyperionRequest &message) void ProtoConnection::sendMessage(const proto::HyperionRequest &message)
{ {
if (_socket.state() == QAbstractSocket::UnconnectedState) // print out connection message only when state is changed
if (_socket.state() != _prevSocketState )
{ {
std::cout << "Currently disconnected: trying to connect to host" << std::endl; switch (_socket.state() )
connectToHost(); {
case QAbstractSocket::UnconnectedState:
std::cout << "No connection to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
break;
case QAbstractSocket::ConnectedState:
std::cout << "Connected to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
break;
default:
//std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
break;
}
_prevSocketState = _socket.state();
} }
if (_socket.state() != QAbstractSocket::ConnectedState) if (_socket.state() != QAbstractSocket::ConnectedState)
{ {
return; return;

View File

@ -2,7 +2,9 @@
#include <stdexcept> #include <stdexcept>
// project includes // project includes
#include <hyperion/MessageForwarder.h>
#include <protoserver/ProtoServer.h> #include <protoserver/ProtoServer.h>
#include "protoserver/ProtoConnection.h"
#include "ProtoClientConnection.h" #include "ProtoClientConnection.h"
ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) : ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
@ -11,6 +13,20 @@ ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
_server(), _server(),
_openConnections() _openConnections()
{ {
MessageForwarder * forwarder = hyperion->getForwarder();
QStringList slaves = forwarder->getProtoSlaves();
for (int i = 0; i < slaves.size(); ++i) {
if ( QString("127.0.0.1:%1").arg(port) == slaves.at(i) ) {
throw std::runtime_error("Loop between proto server and forwarder detected. Fix your config!");
}
ProtoConnection* p = new ProtoConnection(slaves.at(i).toLocal8Bit().constData());
p->setSkipReply(true);
_proxy_connections << p;
}
if (!_server.listen(QHostAddress::Any, port)) if (!_server.listen(QHostAddress::Any, port))
{ {
throw std::runtime_error("Proto server could not bind to port"); throw std::runtime_error("Proto server could not bind to port");
@ -25,6 +41,9 @@ ProtoServer::~ProtoServer()
foreach (ProtoClientConnection * connection, _openConnections) { foreach (ProtoClientConnection * connection, _openConnections) {
delete connection; delete connection;
} }
while (!_proxy_connections.isEmpty())
delete _proxy_connections.takeFirst();
} }
uint16_t ProtoServer::getPort() const uint16_t ProtoServer::getPort() const
@ -44,9 +63,23 @@ void ProtoServer::newConnection()
// register slot for cleaning up after the connection closed // register slot for cleaning up after the connection closed
connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*))); connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*)));
connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*)));
} }
} }
void ProtoServer::newMessage(const proto::HyperionRequest * message)
{
for (int i = 0; i < _proxy_connections.size(); ++i)
_proxy_connections.at(i)->sendMessage(*message);
}
void ProtoServer::sendImageToProtoSlaves(int priority, const Image<ColorRgb> & image, int duration_ms)
{
for (int i = 0; i < _proxy_connections.size(); ++i)
_proxy_connections.at(i)->setImage(image, priority, duration_ms);
}
void ProtoServer::closedConnection(ProtoClientConnection *connection) void ProtoServer::closedConnection(ProtoClientConnection *connection)
{ {
std::cout << "Proto connection closed" << std::endl; std::cout << "Proto connection closed" << std::endl;

View File

@ -15,4 +15,9 @@ if(ENABLE_PROTOBUF)
if(ENABLE_X11) if(ENABLE_X11)
add_subdirectory(hyperion-x11) add_subdirectory(hyperion-x11)
endif() endif()
if(ENABLE_DISPMANX)
add_subdirectory(hyperion-dispmanx)
endif()
endif() endif()

View File

@ -0,0 +1,38 @@
// Hyperion-AmLogic includes
#include "AmlogicWrapper.h"
AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz) :
_timer(this),
_grabber(grabWidth, grabHeight)
{
_timer.setSingleShot(false);
_timer.setInterval(updateRate_Hz);
// Connect capturing to the timeout signal of the timer
connect(&_timer, SIGNAL(timeout()), this, SLOT(capture()));
}
const Image<ColorRgb> & AmlogicWrapper::getScreenshot()
{
capture();
return _screenshot_rgb;
}
void AmlogicWrapper::start()
{
_timer.start();
}
void AmlogicWrapper::stop()
{
_timer.stop();
}
void AmlogicWrapper::capture()
{
_grabber.grabFrame(_screenshot);
_screenshot.toRgb(_screenshot_rgb);
emit sig_screenshot(_screenshot_rgb);
}

View File

@ -0,0 +1,43 @@
// QT includes
#include <QTimer>
// Hyperion-Dispmanx includes
#include <grabber/AmlogicGrabber.h>
class AmlogicWrapper : public QObject
{
Q_OBJECT
public:
AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz);
const Image<ColorRgb> & getScreenshot();
///
/// Starts the timed capturing of screenshots
///
void start();
void stop();
signals:
void sig_screenshot(const Image<ColorRgb> & 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
AmlogicGrabber _grabber;
// image buffers
Image<ColorRgb> _screenshot_rgb;
Image<ColorBgr> _screenshot;
};

View File

@ -14,6 +14,7 @@ include_directories(
) )
set(Hyperion_AML_QT_HEADERS set(Hyperion_AML_QT_HEADERS
AmlogicWrapper.h
) )
set(Hyperion_AML_HEADERS set(Hyperion_AML_HEADERS
@ -21,12 +22,13 @@ set(Hyperion_AML_HEADERS
set(Hyperion_AML_SOURCES set(Hyperion_AML_SOURCES
hyperion-aml.cpp hyperion-aml.cpp
AmlogicWrapper.cpp
) )
if(ENABLE_QT5) if(ENABLE_QT5)
QT5_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS}) QT5_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS})
else(ENABLE_QT5) else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS}) QT4_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS})
endif(ENABLE_QT5) endif(ENABLE_QT5)
add_executable(hyperion-amlogic add_executable(hyperion-amlogic

View File

@ -7,16 +7,16 @@
// getoptPlusPLus includes // getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h> #include <getoptPlusPlus/getoptpp.h>
#include "../../libsrc/grabber/amlogic/AmlogicGrabber.h" #include <protoserver/ProtoConnectionWrapper.h>
#include "AmlogicWrapper.h"
using namespace vlofgren; using namespace vlofgren;
// save the image as screenshot // save the image as screenshot
void saveScreenshot(const char * filename, const Image<ColorBgr> & image) void saveScreenshot(const char * filename, const Image<ColorRgb> & image)
{ {
// store as PNG // store as PNG
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
pngImage = pngImage.rgbSwapped();
pngImage.save(filename); pngImage.save(filename);
} }
@ -30,16 +30,17 @@ int main(int argc, char ** argv)
OptionsParser optionParser("X11 capture application for Hyperion"); OptionsParser optionParser("X11 capture application for Hyperion");
ParameterSet & parameters = optionParser.getParameters(); ParameterSet & parameters = optionParser.getParameters();
//IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Capture frame rate [default=10]"); IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Capture frame rate [default=10]");
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Width of the captured image [default=128]"); IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Width of the captured image [default=128]");
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Height of the captured image [default=128]"); IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Height of the captured image [default=128]");
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]");
//SwitchParameter<> & argSkipReply = parameters.add<SwitchParameter<>> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); SwitchParameter<> & argSkipReply = parameters.add<SwitchParameter<>> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion");
SwitchParameter<> & argHelp = parameters.add<SwitchParameter<>> ('h', "help", "Show this help message and exit"); SwitchParameter<> & argHelp = parameters.add<SwitchParameter<>> ('h', "help", "Show this help message and exit");
// set defaults // set defaults
argFps.setDefault(10);
argWidth.setDefault(160); argWidth.setDefault(160);
argHeight.setDefault(160); argHeight.setDefault(160);
argAddress.setDefault("127.0.0.1:19445"); argAddress.setDefault("127.0.0.1:19445");
@ -57,29 +58,36 @@ int main(int argc, char ** argv)
int width = argWidth.getValue(); int width = argWidth.getValue();
int height = argHeight.getValue(); int height = argHeight.getValue();
if (width < 160 || height < 60) if (width < 160 || height < 160)
{ {
std::cout << "Minimum width and height is 160" << std::endl; std::cout << "Minimum width and height is 160" << std::endl;
width = std::max(160, width); width = std::max(160, width);
height = std::max(160, height); height = std::max(160, height);
} }
int grabInterval = 1000 / argFps.getValue();
AmlogicWrapper amlWrapper(argWidth.getValue(),argHeight.getValue(),grabInterval);
if (argScreenshot.isSet()) if (argScreenshot.isSet())
{ {
// Create the grabber
AmlogicGrabber amlGrabber(width, height);
// Capture a single screenshot and finish // Capture a single screenshot and finish
Image<ColorBgr> screenshot; const Image<ColorRgb> & screenshot = amlWrapper.getScreenshot();
amlGrabber.grabFrame(screenshot);
saveScreenshot("screenshot.png", screenshot); saveScreenshot("screenshot.png", screenshot);
} }
else else
{ {
// TODO[TvdZ]: Implement the proto-client mechanisme // Create the Proto-connection with hyperiond
std::cerr << "The PROTO-interface has not been implemented yet" << std::endl; ProtoConnectionWrapper protoWrapper(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet());
}
// Connect the screen capturing to the proto processing
QObject::connect(&amlWrapper, SIGNAL(sig_screenshot(const Image<ColorRgb> &)), &protoWrapper, SLOT(receiveImage(Image<ColorRgb>)));
// Start the capturing
amlWrapper.start();
// Start the application
app.exec();
}
} }
catch (const std::runtime_error & e) catch (const std::runtime_error & e)
{ {

View File

@ -0,0 +1,65 @@
# Configure minimum CMAKE version
cmake_minimum_required(VERSION 2.8)
# Set the project name
project(hyperion-dispmanx)
if(ENABLE_QT5)
find_package(Qt5Widgets REQUIRED)
else(ENABLE_QT5)
# find Qt4
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork )
endif(ENABLE_QT5)
# Find the BCM-package (VC control)
find_package(BCM REQUIRED)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver
${QT_INCLUDES}
${BCM_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
)
set(Hyperion_Dispmanx_QT_HEADERS
DispmanxWrapper.h)
set(Hyperion_Dispmanx_HEADERS
)
set(Hyperion_Dispmanx_SOURCES
hyperion-dispmanx.cpp
DispmanxWrapper.cpp
)
if(ENABLE_QT5)
QT5_WRAP_CPP(Hyperion_Dispmanx_HEADERS_MOC ${Hyperion_Dispmanx_QT_HEADERS})
else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_Dispmanx_HEADERS_MOC ${Hyperion_Dispmanx_QT_HEADERS})
endif(ENABLE_QT5)
add_executable( ${PROJECT_NAME}
${Hyperion_Dispmanx_HEADERS}
${Hyperion_Dispmanx_SOURCES}
${Hyperion_Dispmanx_HEADERS_MOC}
)
target_link_libraries( ${PROJECT_NAME}
getoptPlusPlus
blackborder
hyperion-utils
protoserver
dispmanx-grabber
${Dispmanx_LIBRARIES}
pthread
)
if(ENABLE_QT5)
qt5_use_modules(hyperion-dispmanx Widgets Core Gui Network)
else(ENABLE_QT5)
qt4_use_modules(hyperion-dispmanx
Core
Gui
Network
)
endif(ENABLE_QT5)

View File

@ -0,0 +1,38 @@
// Hyperion-Dispmanx includes
#include "DispmanxWrapper.h"
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz) :
_timer(this),
_grabber(grabWidth, grabHeight)
{
_timer.setSingleShot(false);
_timer.setInterval(updateRate_Hz);
// Connect capturing to the timeout signal of the timer
connect(&_timer, SIGNAL(timeout()), this, SLOT(capture()));
}
const Image<ColorRgb> & DispmanxWrapper::getScreenshot()
{
capture();
return _screenshot_rgb;
}
void DispmanxWrapper::start()
{
_timer.start();
}
void DispmanxWrapper::stop()
{
_timer.stop();
}
void DispmanxWrapper::capture()
{
_grabber.grabFrame(_screenshot);
_screenshot.toRgb(_screenshot_rgb);
emit sig_screenshot(_screenshot_rgb);
}

View File

@ -0,0 +1,42 @@
// QT includes
#include <QTimer>
// Hyperion-Dispmanx includes
#include <grabber/DispmanxFrameGrabber.h>
class DispmanxWrapper : public QObject
{
Q_OBJECT
public:
DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz);
const Image<ColorRgb> & getScreenshot();
///
/// Starts the timed capturing of screenshots
///
void start();
void stop();
signals:
void sig_screenshot(const Image<ColorRgb> & 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
DispmanxFrameGrabber _grabber;
Image<ColorRgb> _screenshot_rgb;
Image<ColorRgba> _screenshot;
};

View File

@ -0,0 +1,92 @@
// QT includes
#include <QCoreApplication>
#include <QImage>
// getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h>
#include <protoserver/ProtoConnectionWrapper.h>
#include "DispmanxWrapper.h"
using namespace vlofgren;
// save the image as screenshot
void saveScreenshot(const char * filename, const Image<ColorRgb> & 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("Dispmanx capture application for Hyperion");
ParameterSet & parameters = optionParser.getParameters();
IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Capture frame rate [default=10]");
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "The width of the grabbed frames [pixels]");
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "The height of the grabbed frames");
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]");
SwitchParameter<> & argSkipReply = parameters.add<SwitchParameter<>> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion");
SwitchParameter<> & argHelp = parameters.add<SwitchParameter<>> ('h', "help", "Show this help message and exit");
// set defaults
argFps.setDefault(10);
argWidth.setDefault(64);
argHeight.setDefault(64);
argAddress.setDefault("127.0.0.1:19445");
argPriority.setDefault(800);
// parse all options
optionParser.parse(argc, const_cast<const char **>(argv));
// check if we need to display the usage. exit if we do.
if (argHelp.isSet())
{
optionParser.usage();
return 0;
}
// Create the dispmanx grabbing stuff
int grabInterval = 1000 / argFps.getValue();
DispmanxWrapper dispmanxWrapper(argWidth.getValue(),argHeight.getValue(),grabInterval);
if (argScreenshot.isSet())
{
// Capture a single screenshot and finish
const Image<ColorRgb> & screenshot = dispmanxWrapper.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(&dispmanxWrapper, SIGNAL(sig_screenshot(const Image<ColorRgb> &)), &protoWrapper, SLOT(receiveImage(Image<ColorRgb>)));
// Start the capturing
dispmanxWrapper.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;
}

View File

@ -1,6 +1,7 @@
// C++ includes // C++ includes
#include <cassert> #include <cassert>
#include <csignal> #include <csignal>
#include <vector>
// QT includes // QT includes
#include <QCoreApplication> #include <QCoreApplication>
@ -188,6 +189,39 @@ int main(int argc, char** argv)
std::cout << "XBMC video checker created and started" << std::endl; std::cout << "XBMC video checker created and started" << std::endl;
} }
// ---- network services -----
// Create Json server if configuration is present
JsonServer * jsonServer = nullptr;
if (config.isMember("jsonServer"))
{
const Json::Value & jsonServerConfig = config["jsonServer"];
jsonServer = new JsonServer(&hyperion, jsonServerConfig["port"].asUInt());
std::cout << "Json server created and started on port " << jsonServer->getPort() << std::endl;
}
#ifdef ENABLE_PROTOBUF
// Create Proto server if configuration is present
ProtoServer * protoServer = nullptr;
if (config.isMember("protoServer"))
{
const Json::Value & protoServerConfig = config["protoServer"];
protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt() );
std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl;
}
#endif
// Create Boblight server if configuration is present
BoblightServer * boblightServer = nullptr;
if (config.isMember("boblightServer"))
{
const Json::Value & boblightServerConfig = config["boblightServer"];
boblightServer = new BoblightServer(&hyperion, boblightServerConfig["port"].asUInt());
std::cout << "Boblight server created and started on port " << boblightServer->getPort() << std::endl;
}
// ---- grabber -----
#ifdef ENABLE_DISPMANX #ifdef ENABLE_DISPMANX
// Construct and start the frame-grabber if the configuration is present // Construct and start the frame-grabber if the configuration is present
DispmanxWrapper * dispmanx = nullptr; DispmanxWrapper * dispmanx = nullptr;
@ -206,6 +240,10 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), dispmanx, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), dispmanx, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(dispmanx, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
dispmanx->start(); dispmanx->start();
std::cout << "Frame grabber created and started" << std::endl; std::cout << "Frame grabber created and started" << std::endl;
} }
@ -213,7 +251,7 @@ int main(int argc, char** argv)
#if !defined(ENABLE_OSX) && !defined(ENABLE_FB) #if !defined(ENABLE_OSX) && !defined(ENABLE_FB)
if (config.isMember("framegrabber")) if (config.isMember("framegrabber"))
{ {
std::cerr << "The dispmanx framegrabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The dispmanx framegrabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#endif #endif
@ -245,14 +283,19 @@ int main(int argc, char** argv)
grabberConfig.get("cropTop", 0).asInt(), grabberConfig.get("cropTop", 0).asInt(),
grabberConfig.get("cropBottom", 0).asInt()); grabberConfig.get("cropBottom", 0).asInt());
#ifdef ENABLE_PROTOBUF
QObject::connect(v4l2Grabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
v4l2Grabber->start(); v4l2Grabber->start();
std::cout << "V4l2 grabber created and started" << std::endl; std::cout << "V4l2 grabber created and started" << std::endl;
} }
#else #else
if (config.isMember("grabber-v4l2")) if (config.isMember("grabber-v4l2"))
{ {
std::cerr << "The v4l2 grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The v4l2 grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#ifdef ENABLE_AMLOGIC #ifdef ENABLE_AMLOGIC
@ -273,6 +316,10 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), amlGrabber, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), amlGrabber, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(amlGrabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
amlGrabber->start(); amlGrabber->start();
std::cout << "AMLOGIC grabber created and started" << std::endl; std::cout << "AMLOGIC grabber created and started" << std::endl;
} }
@ -302,18 +349,22 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), fbGrabber, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), fbGrabber, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(fbGrabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
fbGrabber->start(); fbGrabber->start();
std::cout << "Framebuffer grabber created and started" << std::endl; std::cout << "Framebuffer grabber created and started" << std::endl;
} }
#else #else
if (config.isMember("framebuffergrabber")) if (config.isMember("framebuffergrabber"))
{ {
std::cerr << "The framebuffer grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The framebuffer grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) #if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX)
else if (config.isMember("framegrabber")) else if (config.isMember("framegrabber"))
{ {
std::cerr << "The framebuffer grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The framebuffer grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#endif #endif
@ -336,6 +387,10 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), osxGrabber, SLOT(setGrabbingMode(GrabbingMode))); QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), osxGrabber, SLOT(setGrabbingMode(GrabbingMode)));
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), osxGrabber, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), osxGrabber, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(osxGrabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
osxGrabber->start(); osxGrabber->start();
std::cout << "OSX grabber created and started" << std::endl; std::cout << "OSX grabber created and started" << std::endl;
@ -343,44 +398,16 @@ int main(int argc, char** argv)
#else #else
if (config.isMember("osxgrabber")) if (config.isMember("osxgrabber"))
{ {
std::cerr << "The osx grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The osx grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_FB) #if !defined(ENABLE_DISPMANX) && !defined(ENABLE_FB)
else if (config.isMember("framegrabber")) else if (config.isMember("framegrabber"))
{ {
std::cerr << "The osx grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The osx grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#endif #endif
// Create Json server if configuration is present
JsonServer * jsonServer = nullptr;
if (config.isMember("jsonServer"))
{
const Json::Value & jsonServerConfig = config["jsonServer"];
jsonServer = new JsonServer(&hyperion, jsonServerConfig["port"].asUInt());
std::cout << "Json server created and started on port " << jsonServer->getPort() << std::endl;
}
#ifdef ENABLE_PROTOBUF
// Create Proto server if configuration is present
ProtoServer * protoServer = nullptr;
if (config.isMember("protoServer"))
{
const Json::Value & protoServerConfig = config["protoServer"];
protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt());
std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl;
}
#endif
// Create Boblight server if configuration is present
BoblightServer * boblightServer = nullptr;
if (config.isMember("boblightServer"))
{
const Json::Value & boblightServerConfig = config["boblightServer"];
boblightServer = new BoblightServer(&hyperion, boblightServerConfig["port"].asUInt());
std::cout << "Boblight server created and started on port " << boblightServer->getPort() << std::endl;
}
// run the application // run the application
int rc = app.exec(); int rc = app.exec();

View File

@ -4,22 +4,42 @@
// Utils includes // Utils includes
#include <utils/Image.h> #include <utils/Image.h>
#include <utils/ColorRgba.h>
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <utils/ColorBgr.h>
#include <hyperion/ImageProcessor.h>
int main() int main()
{ {
std::cout << "Constructing image" << std::endl; std::cout << "Constructing image" << std::endl;
Image<ColorRgb> image(64, 64, ColorRgb::BLACK); int width = 64;
int height = 64;
Image<ColorRgb> image_rgb(width, height, ColorRgb::BLACK);
Image<ColorBgr> image_bgr(image_rgb.width(), image_rgb.height(), ColorBgr::BLACK);
std::cout << "Writing image" << std::endl; std::cout << "Writing image" << std::endl;
for (unsigned y=0; y<64; ++y) unsigned l = width * height;
// BGR
for (unsigned i=0; i<l; ++i)
image_bgr.memptr()[i] = ColorBgr{0,128,255};
// to RGB
image_bgr.toRgb(image_rgb);
// test
for (unsigned i=0; i<l; ++i)
{ {
for (unsigned x=0; x<64; ++x) const ColorRgb rgb = image_rgb.memptr()[i];
{ if ( rgb.red != 255 || rgb.green != 128 || rgb.blue != 0 )
image(x,y) = ColorRgb::RED; std::cout << "RGB error idx " << i << " " << rgb << std::endl;
}
} }
std::cout << "Finished (destruction will be performed)" << std::endl; std::cout << "Finished (destruction will be performed)" << std::endl;
return 0; return 0;

View File

@ -10,7 +10,7 @@
#include <getoptPlusPlus/getoptpp.h> #include <getoptPlusPlus/getoptpp.h>
// Dispmanx grabber includes // Dispmanx grabber includes
#include <grabber/dispmanx/DispmanxFrameGrabber.h> #include <grabber/DispmanxFrameGrabber.h>
using namespace vlofgren; using namespace vlofgren;