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"
# 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
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
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
sudo port install qt4-mac
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
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
sudo cp ./bin/hyperion-remote /usr/bin/

View File

@ -23,6 +23,20 @@
"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.
/// The configuration contains a list of color-transforms. Each transform contains the
/// following fields:

View File

@ -19,6 +19,21 @@
"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.
/// The configuration contains a list of color-transforms. Each transform contains the
/// following fields:

View File

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

View File

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

View File

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

View File

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

View File

@ -44,6 +44,7 @@ public slots:
signals:
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:
void newFrame(const Image<ColorRgb> & image);

View File

@ -13,6 +13,7 @@
// Hyperion includes
#include <hyperion/LedString.h>
#include <hyperion/PriorityMuxer.h>
#include <hyperion/MessageForwarder.h>
// Effect engine includes
#include <effectengine/EffectDefinition.h>
@ -125,6 +126,10 @@ public slots:
/// Tell Hyperion that the transforms have changed and the leds need to be updated
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
/// lower priority channel (or off if no more channels are set)
@ -168,6 +173,7 @@ public:
static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig);
static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice);
static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig);
signals:
/// Signal which is emitted when a priority channel is actively cleared
@ -201,6 +207,9 @@ private:
/// Effect engine
EffectEngine * _effectEngine;
// proto and json Message forwarder
MessageForwarder * _messageForwarder;
/// The timer for handling priority channel timeouts
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 <QImage>
#include <QTcpSocket>
#include <QTimer>
#include <QMap>
// hyperion util
@ -19,8 +20,11 @@
///
/// Connection class to setup an connection to the hyperion server and execute commands
///
class ProtoConnection
class ProtoConnection : public QObject
{
Q_OBJECT
public:
///
/// Constructor
@ -67,10 +71,6 @@ public:
///
void clearAll();
private:
/// Try to connect to the Hyperion host
void connectToHost();
///
/// Send a command message and receive its reply
///
@ -78,6 +78,13 @@ private:
///
void sendMessage(const proto::HyperionRequest & message);
private slots:
/// Try to connect to the Hyperion host
void connectToHost();
private:
///
/// Parse a reply message
///
@ -99,4 +106,7 @@ private:
/// Skip receiving reply messages from Hyperion if set
bool _skipReply;
QTimer _timer;
QAbstractSocket::SocketState _prevSocketState;
};

View File

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

View File

@ -5,6 +5,8 @@
#include <cstdint>
#include <cstring>
#include <algorithm>
#include <utils/ColorRgb.h>
template <typename Pixel_T>
class Image
@ -183,6 +185,25 @@ public:
{
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:
///

View File

@ -13,7 +13,7 @@
#include <sys/types.h>
// 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
#define AMVIDEOCAP_IOC_MAGIC 'V'

View File

@ -9,7 +9,7 @@
// Amlogic grabber includes
#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) :
@ -26,6 +26,7 @@ AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeig
// Configure the timer to generate events every n milliseconds
_timer.setInterval(_updateInterval_ms);
_timer.setSingleShot(false);
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
_processor->setSize(grabWidth, grabHeight);
@ -55,8 +56,14 @@ void AmlogicWrapper::action()
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);
}

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(AmlogicHEADERS
${CURRENT_SOURCE_DIR}/AmlogicGrabber.h
${CURRENT_HEADER_DIR}/AmlogicGrabber.h
)
SET(AmlogicSOURCES

View File

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

View File

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

View File

@ -9,7 +9,7 @@
// Dispmanx grabber includes
#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) :
@ -28,6 +28,7 @@ DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHe
_timer.setSingleShot(false);
_processor->setSize(grabWidth, grabHeight);
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
// Connect the QTimer to this
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(action()));
@ -51,10 +52,17 @@ void DispmanxWrapper::action()
// Grab frame into the allocated 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);
}
void DispmanxWrapper::stop()
{
// Stop the timer, effectivly stopping the process

View File

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

View File

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

View File

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

View File

@ -1,66 +1,68 @@
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
# Group the headers that go through the MOC compiler
SET(Hyperion_QT_HEADERS
${CURRENT_HEADER_DIR}/Hyperion.h
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h
)
SET(Hyperion_HEADERS
${CURRENT_HEADER_DIR}/ImageProcessor.h
${CURRENT_HEADER_DIR}/ImageProcessorFactory.h
${CURRENT_HEADER_DIR}/ImageToLedsMap.h
${CURRENT_HEADER_DIR}/LedString.h
${CURRENT_HEADER_DIR}/PriorityMuxer.h
${CURRENT_SOURCE_DIR}/MultiColorTransform.h
)
SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/Hyperion.cpp
${CURRENT_SOURCE_DIR}/ImageProcessor.cpp
${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp
${CURRENT_SOURCE_DIR}/LedString.cpp
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp
)
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")
else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
endif(ENABLE_QT5)
add_library(hyperion
${Hyperion_HEADERS}
${Hyperion_QT_HEADERS}
${Hyperion_HEADERS_MOC}
${Hyperion_SOURCES}
${Hyperion_RESOURCES_RCC}
)
if(ENABLE_QT5)
qt5_use_modules(hyperion Widgets)
endif(ENABLE_QT5)
target_link_libraries(hyperion
blackborder
hyperion-utils
leddevice
effectengine
serialport
${QT_LIBRARIES}
)
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
# Group the headers that go through the MOC compiler
SET(Hyperion_QT_HEADERS
${CURRENT_HEADER_DIR}/Hyperion.h
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h
)
SET(Hyperion_HEADERS
${CURRENT_HEADER_DIR}/ImageProcessor.h
${CURRENT_HEADER_DIR}/ImageProcessorFactory.h
${CURRENT_HEADER_DIR}/ImageToLedsMap.h
${CURRENT_HEADER_DIR}/LedString.h
${CURRENT_HEADER_DIR}/PriorityMuxer.h
${CURRENT_SOURCE_DIR}/MultiColorTransform.h
${CURRENT_HEADER_DIR}/MessageForwarder.h
)
SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/Hyperion.cpp
${CURRENT_SOURCE_DIR}/ImageProcessor.cpp
${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp
${CURRENT_SOURCE_DIR}/LedString.cpp
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp
${CURRENT_SOURCE_DIR}/MessageForwarder.cpp
)
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")
else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
endif(ENABLE_QT5)
add_library(hyperion
${Hyperion_HEADERS}
${Hyperion_QT_HEADERS}
${Hyperion_HEADERS_MOC}
${Hyperion_SOURCES}
${Hyperion_RESOURCES_RCC}
)
if(ENABLE_QT5)
qt5_use_modules(hyperion Widgets)
endif(ENABLE_QT5)
target_link_libraries(hyperion
blackborder
hyperion-utils
leddevice
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) :
_ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))),
_muxer(_ledString.leds().size()),
_raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])),
_device(LedDeviceFactory::construct(jsonConfig["device"])),
_effectEngine(nullptr),
_messageForwarder(createMessageForwarder(jsonConfig["forwarder"])),
_timer()
{
if (!_raw2ledTransform->verifyTransforms())
@ -314,6 +347,9 @@ Hyperion::~Hyperion()
// delete the color transform
delete _raw2ledTransform;
// delete the message forwarder
delete _messageForwarder;
}
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
#include <hyperion/ImageProcessorFactory.h>
#include <hyperion/ImageProcessor.h>
#include <hyperion/MessageForwarder.h>
#include <hyperion/ColorTransform.h>
#include <utils/ColorRgb.h>
@ -250,8 +251,27 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
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)
{
forwardJsonMessage(message);
// extract parameters
int priority = message["priority"].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)
{
forwardJsonMessage(message);
// extract parameters
int priority = message["priority"].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)
{
forwardJsonMessage(message);
// extract parameters
int priority = message["priority"].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)
{
forwardJsonMessage(message);
// extract parameters
int priority = message["priority"].asInt();
@ -428,8 +454,10 @@ void JsonClientConnection::handleClearCommand(const Json::Value &message)
sendSuccessReply();
}
void JsonClientConnection::handleClearallCommand(const Json::Value &)
void JsonClientConnection::handleClearallCommand(const Json::Value & message)
{
forwardJsonMessage(message);
// clear priority
_hyperion->clearall();
@ -530,10 +558,51 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
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()
{
// create reply

View File

@ -124,6 +124,7 @@ private:
/// @param message The JSON message to send
///
void sendMessage(const Json::Value & message);
void sendMessage(const Json::Value & message, QTcpSocket * socket);
///
/// Send a standard reply indicating success
@ -147,6 +148,11 @@ private:
///
void handleWebSocketFrame();
///
/// forward json message
///
void forwardJsonMessage(const Json::Value & message);
private:
///
/// 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");
}
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
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));

View File

@ -1,8 +1,8 @@
#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 ssize_t OPC_SET_PIXELS = 0; // OPC command codes
static const ssize_t OPC_HEADER_SIZE = 4; // OPC header size
static const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds
static const unsigned OPC_SET_PIXELS = 0; // OPC command codes
static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
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)
{
// forward messages
emit newMessage(&message);
switch (message.command())
{
case proto::HyperionRequest::COLOR:

View File

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

View File

@ -9,7 +9,8 @@
ProtoConnection::ProtoConnection(const std::string & a) :
_socket(),
_skipReply(false)
_skipReply(false),
_prevSocketState(QAbstractSocket::UnconnectedState)
{
QString address(a.c_str());
QStringList parts = address.split(":");
@ -29,10 +30,18 @@ ProtoConnection::ProtoConnection(const std::string & a) :
// try to connect to host
std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
connectToHost();
// start the connection timer
_timer.setInterval(5000);
_timer.setSingleShot(false);
connect(&_timer,SIGNAL(timeout()), this, SLOT(connectToHost()) );
_timer.start();
}
ProtoConnection::~ProtoConnection()
{
_timer.stop();
_socket.close();
}
@ -91,20 +100,37 @@ void ProtoConnection::clearAll()
void ProtoConnection::connectToHost()
{
_socket.connectToHost(_host, _port);
if (_socket.waitForConnected()) {
std::cout << "Connected to Hyperion host" << std::endl;
// try connection only when
if (_socket.state() == QAbstractSocket::UnconnectedState)
{
_socket.connectToHost(_host, _port);
//_socket.waitForConnected(1000);
}
}
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;
connectToHost();
switch (_socket.state() )
{
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)
{
return;

View File

@ -2,7 +2,9 @@
#include <stdexcept>
// project includes
#include <hyperion/MessageForwarder.h>
#include <protoserver/ProtoServer.h>
#include "protoserver/ProtoConnection.h"
#include "ProtoClientConnection.h"
ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
@ -11,6 +13,20 @@ ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
_server(),
_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))
{
throw std::runtime_error("Proto server could not bind to port");
@ -25,6 +41,9 @@ ProtoServer::~ProtoServer()
foreach (ProtoClientConnection * connection, _openConnections) {
delete connection;
}
while (!_proxy_connections.isEmpty())
delete _proxy_connections.takeFirst();
}
uint16_t ProtoServer::getPort() const
@ -44,9 +63,23 @@ void ProtoServer::newConnection()
// register slot for cleaning up after the connection closed
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)
{
std::cout << "Proto connection closed" << std::endl;

View File

@ -15,4 +15,9 @@ if(ENABLE_PROTOBUF)
if(ENABLE_X11)
add_subdirectory(hyperion-x11)
endif()
if(ENABLE_DISPMANX)
add_subdirectory(hyperion-dispmanx)
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
AmlogicWrapper.h
)
set(Hyperion_AML_HEADERS
@ -21,12 +22,13 @@ set(Hyperion_AML_HEADERS
set(Hyperion_AML_SOURCES
hyperion-aml.cpp
AmlogicWrapper.cpp
)
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)
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)
add_executable(hyperion-amlogic

View File

@ -7,16 +7,16 @@
// getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h>
#include "../../libsrc/grabber/amlogic/AmlogicGrabber.h"
#include <protoserver/ProtoConnectionWrapper.h>
#include "AmlogicWrapper.h"
using namespace vlofgren;
// 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
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
pngImage = pngImage.rgbSwapped();
pngImage.save(filename);
}
@ -30,16 +30,17 @@ int main(int argc, char ** argv)
OptionsParser optionParser("X11 capture application for Hyperion");
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 & 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");
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<> & 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(160);
argHeight.setDefault(160);
argAddress.setDefault("127.0.0.1:19445");
@ -57,29 +58,36 @@ int main(int argc, char ** argv)
int width = argWidth.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;
width = std::max(160, width);
height = std::max(160, height);
}
int grabInterval = 1000 / argFps.getValue();
AmlogicWrapper amlWrapper(argWidth.getValue(),argHeight.getValue(),grabInterval);
if (argScreenshot.isSet())
{
// Create the grabber
AmlogicGrabber amlGrabber(width, height);
// Capture a single screenshot and finish
Image<ColorBgr> screenshot;
amlGrabber.grabFrame(screenshot);
const Image<ColorRgb> & screenshot = amlWrapper.getScreenshot();
saveScreenshot("screenshot.png", screenshot);
}
else
{
// TODO[TvdZ]: Implement the proto-client mechanisme
std::cerr << "The PROTO-interface has not been implemented yet" << std::endl;
}
// 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(&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)
{

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
#include <cassert>
#include <csignal>
#include <vector>
// QT includes
#include <QCoreApplication>
@ -188,6 +189,39 @@ int main(int argc, char** argv)
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
// Construct and start the frame-grabber if the configuration is present
DispmanxWrapper * dispmanx = nullptr;
@ -206,6 +240,10 @@ int main(int argc, char** argv)
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();
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 (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
@ -245,14 +283,19 @@ int main(int argc, char** argv)
grabberConfig.get("cropTop", 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();
std::cout << "V4l2 grabber created and started" << std::endl;
}
#else
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
#ifdef ENABLE_AMLOGIC
@ -273,6 +316,10 @@ int main(int argc, char** argv)
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();
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)));
}
#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();
std::cout << "Framebuffer grabber created and started" << std::endl;
}
#else
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)
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
@ -336,6 +387,10 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), osxGrabber, SLOT(setGrabbingMode(GrabbingMode)));
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();
std::cout << "OSX grabber created and started" << std::endl;
@ -343,44 +398,16 @@ int main(int argc, char** argv)
#else
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)
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
// 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
int rc = app.exec();

View File

@ -4,22 +4,42 @@
// Utils includes
#include <utils/Image.h>
#include <utils/ColorRgba.h>
#include <utils/ColorRgb.h>
#include <utils/ColorBgr.h>
#include <hyperion/ImageProcessor.h>
int main()
{
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;
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)
{
image(x,y) = ColorRgb::RED;
}
const ColorRgb rgb = image_rgb.memptr()[i];
if ( rgb.red != 255 || rgb.green != 128 || rgb.blue != 0 )
std::cout << "RGB error idx " << i << " " << rgb << std::endl;
}
std::cout << "Finished (destruction will be performed)" << std::endl;
return 0;

View File

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