From 38950edf35c76bd7920b46572f5162f1f910dab8 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Sun, 30 Dec 2018 22:07:53 +0100 Subject: [PATCH] remove protobuf (part 2) --- include/api/JsonAPI.h | 2 +- include/flatbufserver/FlatBufferConnection.h | 61 ++--- include/flatbufserver/FlatBufferServer.h | 4 +- include/ssdp/SSDPDiscover.h | 56 +++++ include/ssdp/SSDPHandler.h | 81 +++++++ include/ssdp/SSDPServer.h | 98 ++++++++ include/utils/Components.h | 4 - include/utils/Image.h | 2 +- include/utils/settings.h | 3 - include/webserver/WebServer.h | 29 ++- libsrc/CMakeLists.txt | 2 +- libsrc/api/JsonAPI.cpp | 8 +- libsrc/bonjour/bonjourbrowserwrapper.cpp | 3 + libsrc/effectengine/Effect.cpp | 3 + libsrc/flatbufserver/FlatBufferClient.cpp | 148 ++++++------ libsrc/flatbufserver/FlatBufferClient.h | 27 ++- libsrc/flatbufserver/FlatBufferConnection.cpp | 126 +++++----- libsrc/flatbufserver/FlatBufferServer.cpp | 1 + libsrc/flatbufserver/hyperion_reply.fbs | 15 +- libsrc/flatbufserver/hyperion_request.fbs | 64 ++--- libsrc/hyperion/LinearColorSmoothing.cpp | 2 - libsrc/hyperion/MessageForwarder.cpp | 2 +- libsrc/hyperion/SettingsManager.cpp | 2 +- libsrc/hyperion/hyperion.schema.json | 4 - libsrc/hyperion/resource.qrc | 1 - libsrc/leddevice/CMakeLists.txt | 1 + libsrc/ssdp/CMakeLists.txt | 14 ++ libsrc/ssdp/SSDPDescription.h | 41 ++++ libsrc/ssdp/SSDPDiscover.cpp | 169 +++++++++++++ libsrc/ssdp/SSDPHandler.cpp | 144 +++++++++++ libsrc/ssdp/SSDPServer.cpp | 227 ++++++++++++++++++ libsrc/udplistener/UDPListener.cpp | 2 - libsrc/webserver/QtHttpServer.h | 2 +- libsrc/webserver/StaticFileServing.cpp | 43 +++- libsrc/webserver/StaticFileServing.h | 17 +- libsrc/webserver/WebServer.cpp | 46 ++-- src/hyperion-aml/CMakeLists.txt | 8 +- src/hyperion-aml/hyperion-aml.cpp | 30 ++- src/hyperion-dispmanx/CMakeLists.txt | 8 +- src/hyperion-dispmanx/hyperion-dispmanx.cpp | 30 ++- src/hyperion-framebuffer/CMakeLists.txt | 8 +- .../hyperion-framebuffer.cpp | 30 ++- src/hyperion-osx/CMakeLists.txt | 8 +- src/hyperion-osx/hyperion-osx.cpp | 64 +++-- src/hyperion-v4l2/CMakeLists.txt | 8 +- src/hyperion-v4l2/hyperion-v4l2.cpp | 34 ++- src/hyperion-x11/CMakeLists.txt | 8 +- src/hyperion-x11/hyperion-x11.cpp | 35 ++- src/hyperiond/CMakeLists.txt | 2 +- src/hyperiond/hyperiond.cpp | 53 ++-- src/hyperiond/hyperiond.h | 10 +- src/hyperiond/main.cpp | 2 +- src/hyperiond/systray.cpp | 15 +- src/hyperiond/systray.h | 11 +- 54 files changed, 1441 insertions(+), 377 deletions(-) create mode 100644 include/ssdp/SSDPDiscover.h create mode 100644 include/ssdp/SSDPHandler.h create mode 100644 include/ssdp/SSDPServer.h create mode 100644 libsrc/ssdp/CMakeLists.txt create mode 100644 libsrc/ssdp/SSDPDescription.h create mode 100644 libsrc/ssdp/SSDPDiscover.cpp create mode 100644 libsrc/ssdp/SSDPHandler.cpp create mode 100644 libsrc/ssdp/SSDPServer.cpp diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 580975d2..a7619be4 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -56,7 +56,7 @@ public: /// /// @param message the incoming message as string /// - void handleMessage(const QString & message, const QString& httpAuthHeader = ""); + void handleMessage(const QString & message); public slots: /// _timer_ledcolors requests ledcolor updates (if enabled) diff --git a/include/flatbufserver/FlatBufferConnection.h b/include/flatbufserver/FlatBufferConnection.h index a9cc9eed..e0259548 100644 --- a/include/flatbufserver/FlatBufferConnection.h +++ b/include/flatbufserver/FlatBufferConnection.h @@ -28,23 +28,29 @@ class FlatBufferConnection : public QObject public: /// - /// Constructor - /// + /// @brief Constructor /// @param address The address of the Hyperion server (for example "192.168.0.32:19444) + /// @param skipReply If true skip reply /// - FlatBufferConnection(const QString & address); + FlatBufferConnection(const QString& origin, const QString & address, const int& priority, const bool& skipReply); /// - /// Destructor + /// @brief Destructor /// ~FlatBufferConnection(); - /// Do not read reply messages from Hyperion if set to true + /// @brief Do not read reply messages from Hyperion if set to true void setSkipReply(const bool& skip); /// - /// Set all leds to the specified color + /// @brief Register a new priority with given origin + /// @param origin The user friendly origin string + /// @param priority The priority to register /// + void setRegister(const QString& origin, int priority); + + /// + /// @brief Set all leds to the specified color /// @param color The color /// @param priority The priority /// @param duration The duration in milliseconds @@ -52,64 +58,63 @@ public: void setColor(const ColorRgb & color, int priority, int duration = 1); /// - /// Set the leds according to the given image (assume the image is stretched to the display size) - /// - /// @param image The image - /// @param priority The priority - /// @param duration The duration in milliseconds - /// - void setImage(const Image & image, int priority, int duration = -1); - - /// - /// Clear the given priority channel - /// + /// @brief Clear the given priority channel /// @param priority The priority /// void clear(int priority); /// - /// Clear all priority channels + /// @brief Clear all priority channels /// void clearAll(); /// - /// Send a command message and receive its reply - /// + /// @brief Send a command message and receive its reply /// @param message The message to send /// void sendMessage(const uint8_t* buffer, uint32_t size); +public slots: + /// + /// @brief Set the leds according to the given image + /// @param image The image + /// + void setImage(const Image &image); + private slots: - /// Try to connect to the Hyperion host + /// + /// @brief Try to connect to the Hyperion host + /// void connectToHost(); /// - /// Slot called when new data has arrived + /// @brief Slot called when new data has arrived /// void readData(); signals: /// - /// emits when a new videoMode was requested from flatbuf client + /// @brief emits when a new videoMode was requested from flatbuf client /// void setVideoMode(const VideoMode videoMode); private: /// - /// Parse a reply message - /// + /// @brief Parse a reply message /// @param reply The received reply - /// /// @return true if the reply indicates success /// - bool parseReply(const flatbuf::HyperionReply * reply); + bool parseReply(const hyperionnet::Reply *reply); private: /// The TCP-Socket with the connection to the server QTcpSocket _socket; + QString _origin; + int _priority; + /// Host address QString _host; @@ -124,4 +129,6 @@ private: Logger * _log; flatbuffers::FlatBufferBuilder _builder; + + bool _registered; }; diff --git a/include/flatbufserver/FlatBufferServer.h b/include/flatbufserver/FlatBufferServer.h index eb751213..5f01b7cf 100644 --- a/include/flatbufserver/FlatBufferServer.h +++ b/include/flatbufserver/FlatBufferServer.h @@ -24,8 +24,8 @@ public: public slots: /// /// @brief Handle settings update - /// @param type The type from enum - /// @param config The configuration + /// @param type The type from enum + /// @param config The configuration /// void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config); diff --git a/include/ssdp/SSDPDiscover.h b/include/ssdp/SSDPDiscover.h new file mode 100644 index 00000000..f3d669a2 --- /dev/null +++ b/include/ssdp/SSDPDiscover.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +class QUdpSocket; + +enum searchType{ + STY_WEBSERVER, + STY_FLATBUFSERVER +}; + +/// +/// @brief Search for SSDP sessions, used by standalone capture binaries +/// +class SSDPDiscover : public QObject +{ + Q_OBJECT + +public: + SSDPDiscover(QObject* parent = nullptr); + + /// + /// @brief Search for specified service, results will be returned by signal newService(). Calling this method again will reset all found usns and search again + /// @param st The service to search for + /// + void searchForService(const QString& st = "urn:hyperion-project.org:device:basic:1"); + + /// + /// @brief Search for specified searchTarget, the method will block until a server has been found or a timeout happend + /// @param type The address type one of struct searchType + /// @param st The service to search for + /// @param timeout_ms The timeout in ms + /// @return The address+port of webserver or empty if timed out + /// + const QString getFirstService(const searchType& type = STY_WEBSERVER,const QString& st = "urn:hyperion-project.org:device:basic:1", const int& timeout_ms = 3000); + +signals: + /// + /// @brief Emits whenever a new service has ben found, search started with searchForService() + /// @param webServer The address+port of webserver "192.168.0.10:8090" + /// + void newService(const QString webServer); + +private slots: + void readPendingDatagrams(); + +private: + void sendSearch(const QString& st); + +private: + Logger* _log; + QUdpSocket* _udpSocket; + QString _searchTarget; + QStringList _usnList; +}; diff --git a/include/ssdp/SSDPHandler.h b/include/ssdp/SSDPHandler.h new file mode 100644 index 00000000..abd60cec --- /dev/null +++ b/include/ssdp/SSDPHandler.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include + +// utils +#include + +class WebServer; +class QNetworkConfigurationManager; + +/// +/// Manage SSDP discovery. SimpleServiceDisoveryProtocol is the discovery subset of UPnP. Implemented is spec V1.0. +/// As SSDP requires a webserver, this class depends on it +/// UPnP 1.0: spec: http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf +/// + +class SSDPHandler : public SSDPServer{ + Q_OBJECT +public: + SSDPHandler(WebServer* webserver, const quint16& flatBufPort, QObject * parent = nullptr); + +public slots: + /// + /// @brief Init SSDP after thread start + /// + void initServer(); + + /// + /// @brief get state changes from webserver + /// + void handleWebServerStateChange(const bool newState); + + /// + /// @brief Handle settings update from Hyperion Settingsmanager emit + /// @param type settingyType from enum + /// @param config configuration object + /// + void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config); + +private: + /// + /// @brief Build http url for current ip:port/desc.xml + /// + const QString getDescAddress(); + + /// + /// @brief Get the base address + /// + const QString getBaseAddress(); + + /// + /// @brief Build the ssdp description (description.xml) + /// + const QString buildDesc(); + + /// + /// @brief Get the local address of interface + /// @return the address, might be empty + /// + const QString getLocalAddress(); + +private slots: + /// + /// @brief Handle the mSeach request from SSDPServer + /// @param target The ST service type + /// @param mx Answer with delay in s + /// @param address The ip of the caller + /// @param port The port of the caller + /// + void handleMSearchRequest(const QString& target, const QString& mx, const QString address, const quint16 & port); + + void handleNetworkConfigurationChanged(const QNetworkConfiguration &config); + +private: + WebServer* _webserver; + QString _localAddress; + QNetworkConfigurationManager* _NCA; + quint16 _flatbufPort; +}; diff --git a/include/ssdp/SSDPServer.h b/include/ssdp/SSDPServer.h new file mode 100644 index 00000000..71fe02b7 --- /dev/null +++ b/include/ssdp/SSDPServer.h @@ -0,0 +1,98 @@ +#pragma once + +#include + +class QUdpSocket; + +/// +/// @brief The SSDP Server sends and receives (parses) SSDP requests +/// +class SSDPServer : public QObject { + Q_OBJECT + +public: + friend class SSDPHandler; + /// + /// @brief Construct the server, listen on default ssdp address/port with multicast + /// @param parent The parent object + /// + SSDPServer(QObject* parent = nullptr); + virtual ~SSDPServer(); + + /// + /// @brief Prepare server after thread start + /// + void initServer(); + + /// + /// @brief Start SSDP + /// @return false if already running or bind failure + /// + const bool start(); + + /// + /// @brief Stop SSDP + /// + void stop(); + + /// + /// @brief Send an answer to mSearch requester + /// @param st the searchTarget + /// @param senderIp Ip address of the sender + /// @param senderPort The port of the sender + /// + void sendMSearchResponse(const QString& st, const QString& senderIp, const quint16& senderPort); + + /// + /// @brief Send ByeBye notification (on SSDP stop) (repeated 3 times) + /// @param st Search target + /// + void sendByeBye(const QString& st); + + /// + /// @brief Send a NOTIFY msg on SSDP startup to notify our presence (repeated 3 times) + /// @param st The search target + /// + void sendAlive(const QString& st); + + /// + /// @brief Send a NOTIFY msg as ssdp:update to notify about changes + /// @param st The search target + /// + void sendUpdate(const QString& st); + + /// + /// @brief Overwrite description address + /// @param addr new address + /// + void setDescriptionAddress(const QString& addr) { _descAddress = addr; }; + + /// + /// @brief set new flatbuffer server port + /// + void setFlatBufPort(const quint16& port) { _fbsPort = QString::number(port); }; + +signals: + /// + /// @brief Emits whenever a new SSDP search "man : ssdp:discover" is received along with the service type + /// @param target The ST service type + /// @param mx Answer with delay in s + /// @param address The ip of the caller + /// @param port The port of the caller + /// + void msearchRequestReceived(const QString& target, const QString& mx, const QString address, const quint16 & port); + +private: + Logger* _log; + QUdpSocket* _udpSocket; + + QString _serverHeader; + QString _uuid; + QString _fbsPort; + QString _descAddress; + bool _running; + +private slots: + void readPendingDatagrams(); + +}; diff --git a/include/utils/Components.h b/include/utils/Components.h index 1840f9a3..ba2350b6 100644 --- a/include/utils/Components.h +++ b/include/utils/Components.h @@ -21,7 +21,6 @@ enum Components COMP_COLOR, COMP_IMAGE, COMP_EFFECT, - COMP_PROTOSERVER, COMP_LEDDEVICE, COMP_FLATBUFSERVER }; @@ -41,7 +40,6 @@ inline const char* componentToString(Components c) case COMP_COLOR: return "Solid color"; case COMP_EFFECT: return "Effect"; case COMP_IMAGE: return "Image"; - case COMP_PROTOSERVER: return "Proto Server"; case COMP_LEDDEVICE: return "LED device"; case COMP_FLATBUFSERVER: return "Image Receiver"; default: return ""; @@ -63,7 +61,6 @@ inline const char* componentToIdString(Components c) case COMP_COLOR: return "COLOR"; case COMP_EFFECT: return "EFFECT"; case COMP_IMAGE: return "IMAGE"; - case COMP_PROTOSERVER: return "PROTOSERVER"; case COMP_LEDDEVICE: return "LEDDEVICE"; case COMP_FLATBUFSERVER: return "FLATBUFSERVER"; default: return ""; @@ -84,7 +81,6 @@ inline Components stringToComponent(QString component) if (component == "COLOR") return COMP_COLOR; if (component == "EFFECT") return COMP_EFFECT; if (component == "IMAGE") return COMP_IMAGE; - if (component == "PROTOSERVER") return COMP_PROTOSERVER; if (component == "LEDDEVICE") return COMP_LEDDEVICE; if (component == "FLATBUFSERVER") return COMP_FLATBUFSERVER; return COMP_INVALID; diff --git a/include/utils/Image.h b/include/utils/Image.h index 633dddf8..369659b7 100644 --- a/include/utils/Image.h +++ b/include/utils/Image.h @@ -240,7 +240,7 @@ public: /// /// get size of buffer // - ssize_t size() + ssize_t size() const { return _width * _height * sizeof(Pixel_T); } diff --git a/include/utils/settings.h b/include/utils/settings.h index 9191b012..607a3719 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -23,7 +23,6 @@ enum type { LEDCONFIG, LEDS, LOGGER, - PROTOSERVER, SMOOTHING, UDPLISTENER, WEBSERVER, @@ -57,7 +56,6 @@ inline QString typeToString(const type& type) case LEDCONFIG: return "ledConfig"; case LEDS: return "leds"; case LOGGER: return "logger"; - case PROTOSERVER: return "protoServer"; case SMOOTHING: return "smoothing"; case UDPLISTENER: return "udpListener"; case WEBSERVER: return "webConfig"; @@ -90,7 +88,6 @@ inline type stringToType(const QString& type) else if (type == "ledConfig") return LEDCONFIG; else if (type == "leds") return LEDS; else if (type == "logger") return LOGGER; - else if (type == "protoServer") return PROTOSERVER; else if (type == "smoothing") return SMOOTHING; else if (type == "udpListener") return UDPLISTENER; else if (type == "webConfig") return WEBSERVER; diff --git a/include/webserver/WebServer.h b/include/webserver/WebServer.h index d719f38c..f4aaffc8 100644 --- a/include/webserver/WebServer.h +++ b/include/webserver/WebServer.h @@ -5,7 +5,7 @@ #include #include -// utils include +// hyperion / utils #include // settings @@ -28,7 +28,32 @@ public: quint16 getPort() { return _port; }; + /// check if server has been inited + const bool isInited() { return _inited; }; + + /// + /// @brief Set a new description, if empty the description is NotFound for clients + /// + void setSSDPDescription(const QString & desc); + +signals: + /// + /// @emits whenever server is started or stopped (to sync with SSDPHandler) + /// @param newState True when started, false when stopped + /// + void stateChange(const bool newState); + + /// + /// @brief Emits whenever the port changes (doesn't compare prev <> now) + /// + void portChanged(const quint16& port); + public slots: + /// + /// @brief Init server after thread start + /// + void initServer(); + void onServerStopped (void); void onServerStarted (quint16 port); void onServerError (QString msg); @@ -41,11 +66,13 @@ public slots: void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config); private: + QJsonDocument _config; Logger* _log; QString _baseUrl; quint16 _port; StaticFileServing* _staticFileServing; QtHttpServer* _server; + bool _inited = false; const QString WEBSERVER_DEFAULT_PATH = ":/webconfig"; const quint16 WEBSERVER_DEFAULT_PORT = 8090; diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index 9173e1cd..4ff02b3b 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -7,9 +7,9 @@ add_subdirectory(hyperion) add_subdirectory(commandline) add_subdirectory(blackborder) add_subdirectory(jsonserver) -add_subdirectory(protoserver) add_subdirectory(flatbufserver) add_subdirectory(bonjour) +add_subdirectory(ssdp) add_subdirectory(boblightserver) add_subdirectory(udplistener) add_subdirectory(leddevice) diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 29a9319f..88ff4893 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -51,6 +51,7 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListe { // the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage); + // notify hyperion about a jsonMessageForward connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage); @@ -59,7 +60,7 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListe _image_stream_mutex.unlock(); } -void JsonAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader) +void JsonAPI::handleMessage(const QString& messageString) { const QString ident = "JsonRpc@"+_peerAddress; Q_INIT_RESOURCE(JSONRPC_schemas); @@ -859,7 +860,8 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString & { _streaming_leds_reply["success"] = true; _streaming_leds_reply["command"] = command+"-ledstream-update"; - _streaming_leds_reply["tan"] = tan; + _streaming_leds_reply["tan"] = tan; + _timer_ledcolors.setInterval(125); _timer_ledcolors.start(125); } else if (subcommand == "ledstream-stop") @@ -870,7 +872,7 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString & { _streaming_image_reply["success"] = true; _streaming_image_reply["command"] = command+"-imagestream-update"; - _streaming_image_reply["tan"] = tan; + _streaming_image_reply["tan"] = tan; connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection); } else if (subcommand == "imagestream-stop") diff --git a/libsrc/bonjour/bonjourbrowserwrapper.cpp b/libsrc/bonjour/bonjourbrowserwrapper.cpp index d3d94bbc..8b8c8a67 100644 --- a/libsrc/bonjour/bonjourbrowserwrapper.cpp +++ b/libsrc/bonjour/bonjourbrowserwrapper.cpp @@ -14,6 +14,9 @@ BonjourBrowserWrapper::BonjourBrowserWrapper(QObject * parent) , _bonjourResolver(new BonjourServiceResolver(this)) , _timerBonjourResolver( new QTimer(this)) { + // register meta + qRegisterMetaType>("QMap"); + BonjourBrowserWrapper::instance = this; connect(_bonjourResolver, &BonjourServiceResolver::bonjourRecordResolved, this, &BonjourBrowserWrapper::bonjourRecordResolved); diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index b2d0b845..86e61675 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -58,6 +58,9 @@ Effect::~Effect() void Effect::run() { + // we probably need to wait until mainThreadState is available + while(mainThreadState == nullptr){}; + // get global lock PyEval_RestoreThread(mainThreadState); diff --git a/libsrc/flatbufserver/FlatBufferClient.cpp b/libsrc/flatbufserver/FlatBufferClient.cpp index e1c4b36c..5ab24d81 100644 --- a/libsrc/flatbufserver/FlatBufferClient.cpp +++ b/libsrc/flatbufserver/FlatBufferClient.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -11,7 +12,7 @@ FlatBufferClient::FlatBufferClient(QTcpSocket* socket, const int &timeout, QObje : QObject(parent) , _log(Logger::getInstance("FLATBUFSERVER")) , _socket(socket) - , _clientAddress(socket->peerAddress().toString()) + , _clientAddress("@"+socket->peerAddress().toString()) , _timeoutTimer(new QTimer(this)) , _timeout(timeout * 1000) , _priority() @@ -49,12 +50,12 @@ void FlatBufferClient::readyRead() const QByteArray msg = _receiveBuffer.right(messageSize); _receiveBuffer.remove(0, messageSize + 4); - const uint8_t* msgData = reinterpret_cast(msg.constData()); + const auto* msgData = reinterpret_cast(msg.constData()); flatbuffers::Verifier verifier(msgData, messageSize); - if (flatbuf::VerifyHyperionRequestBuffer(verifier)) + if (hyperionnet::VerifyRequestBuffer(verifier)) { - auto message = flatbuf::GetHyperionRequest(msgData); + auto message = hyperionnet::GetRequest(msgData); handleMessage(message); continue; } @@ -83,79 +84,92 @@ void FlatBufferClient::disconnected() emit clientDisconnected(); } -void FlatBufferClient::handleMessage(const flatbuf::HyperionRequest * message) +void FlatBufferClient::handleMessage(const hyperionnet::Request * req) { - switch (message->command()) - { - case flatbuf::Command_COLOR: - qDebug()<<"handle colorReuest"; - if (!flatbuffers::IsFieldPresent(message, flatbuf::HyperionRequest::VT_COLORREQUEST)) - { - sendErrorReply("Received COLOR command without ColorRequest"); - break; - } - //handleColorCommand(message->colorRequest()); - break; - case flatbuf::Command_IMAGE: - qDebug()<<"handle imageReuest"; - if (!flatbuffers::IsFieldPresent(message, flatbuf::HyperionRequest::VT_IMAGEREQUEST)) - { - sendErrorReply("Received IMAGE command without ImageRequest"); - break; - } - handleImageCommand(message->imageRequest()); - break; - case flatbuf::Command_CLEAR: - if (!flatbuffers::IsFieldPresent(message, flatbuf::HyperionRequest::VT_CLEARREQUEST)) - { - sendErrorReply("Received CLEAR command without ClearRequest"); - break; - } - //handleClearCommand(message->clearRequest()); - break; - case flatbuf::Command_CLEARALL: - //handleClearallCommand(); - break; - default: - qDebug()<<"handleNotImplemented"; - handleNotImplemented(); + const void* reqPtr; + if ((reqPtr = req->command_as_Color()) != nullptr) { + handleColorCommand(static_cast(reqPtr)); + } else if ((reqPtr = req->command_as_Image()) != nullptr) { + handleImageCommand(static_cast(reqPtr)); + } else if ((reqPtr = req->command_as_Clear()) != nullptr) { + handleClearCommand(static_cast(reqPtr)); + } else if ((reqPtr = req->command_as_Register()) != nullptr) { + handleRegisterCommand(static_cast(reqPtr)); + } else { + sendErrorReply("Received invalid packet."); } } -void FlatBufferClient::handleImageCommand(const flatbuf::ImageRequest *message) +void FlatBufferClient::handleColorCommand(const hyperionnet::Color *colorReq) { // extract parameters - int priority = message->priority(); - int duration = message->duration(); - int width = message->imagewidth(); - int height = message->imageheight(); - const auto & imageData = message->imagedata(); + const int32_t rgbData = colorReq->data(); + ColorRgb color; + color.red = qRed(rgbData); + color.green = qGreen(rgbData); + color.blue = qBlue(rgbData); - // make sure the prio is registered before setInput() - if(priority != _priority) - { - _hyperion->clear(_priority); - _hyperion->registerInput(priority, hyperion::COMP_FLATBUFSERVER, "proto@"+_clientAddress); - _priority = priority; - } - - // check consistency of the size of the received data - if ((int) imageData->size() != width*height*3) - { - sendErrorReply("Size of image data does not match with the width and height"); - return; - } - - // create ImageRgb - Image image(width, height); - memcpy(image.memptr(), imageData->data(), imageData->size()); - - _hyperion->setInputImage(_priority, image, duration); + // set output + _hyperion->setColor(_priority, color, colorReq->duration()); // send reply sendSuccessReply(); } +void FlatBufferClient::handleRegisterCommand(const hyperionnet::Register *regReq) +{ + _priority = regReq->priority(); + _hyperion->registerInput(_priority, hyperion::COMP_FLATBUFSERVER, regReq->origin()->c_str()+_clientAddress); +} + +void FlatBufferClient::handleImageCommand(const hyperionnet::Image *image) +{ + // extract parameters + int duration = image->duration(); + + const void* reqPtr; + if ((reqPtr = image->data_as_RawImage()) != nullptr) + { + const auto *img = static_cast(reqPtr); + const auto & imageData = img->data(); + const int width = img->width(); + const int height = img->height(); + + if ((int) imageData->size() != width*height*3) + { + sendErrorReply("Size of image data does not match with the width and height"); + return; + } + + Image image(width, height); + memmove(image.memptr(), imageData->data(), imageData->size()); + _hyperion->setInputImage(_priority, image, duration); + } + + // send reply + sendSuccessReply(); +} + + +void FlatBufferClient::handleClearCommand(const hyperionnet::Clear *clear) +{ + // extract parameters + const int priority = clear->priority(); + + if (priority == -1) { + _hyperion->clearall(); + } + else { + // Check if we are clearing ourselves. + if (priority == _priority) { + _priority = -1; + } + + _hyperion->clear(priority); + } + + sendSuccessReply(); +} void FlatBufferClient::handleNotImplemented() { @@ -175,7 +189,7 @@ void FlatBufferClient::sendMessage() void FlatBufferClient::sendSuccessReply() { - auto reply = flatbuf::CreateHyperionReplyDirect(_builder, flatbuf::Type_REPLY, true); + auto reply = hyperionnet::CreateReplyDirect(_builder); _builder.Finish(reply); // send reply @@ -185,7 +199,7 @@ void FlatBufferClient::sendSuccessReply() void FlatBufferClient::sendErrorReply(const std::string &error) { // create reply - auto reply = flatbuf::CreateHyperionReplyDirect(_builder, flatbuf::Type_REPLY, false, error.c_str()); + auto reply = hyperionnet::CreateReplyDirect(_builder, error.c_str()); _builder.Finish(reply); // send reply diff --git a/libsrc/flatbufserver/FlatBufferClient.h b/libsrc/flatbufserver/FlatBufferClient.h index cbddfdac..e0157c31 100644 --- a/libsrc/flatbufserver/FlatBufferClient.h +++ b/libsrc/flatbufserver/FlatBufferClient.h @@ -75,14 +75,31 @@ private: /// /// @brief Handle the received message /// - void handleMessage(const flatbuf::HyperionRequest *message); + void handleMessage(const hyperionnet::Request * req); + + /// + /// Register new priority + /// + void handleRegisterCommand(const hyperionnet::Register *regReq); + + /// + /// @brief Hande Color message + /// + void handleColorCommand(const hyperionnet::Color *colorReq); /// /// Handle an incoming Image message /// - /// @param message the incoming message + /// @param image the incoming image /// - void handleImageCommand(const flatbuf::ImageRequest * message); + void handleImageCommand(const hyperionnet::Image *image); + + /// + /// @brief Handle clear command + /// + /// @param clear the incoming clear request + /// + void handleClearCommand(const hyperionnet::Clear *clear); /// /// Send handle not implemented @@ -108,12 +125,12 @@ private: private: Logger *_log; - QTcpSocket *_socket; + QTcpSocket *_socket; const QString _clientAddress; QTimer *_timeoutTimer; int _timeout; int _priority; - Hyperion* _hyperion; + Hyperion* _hyperion; QByteArray _receiveBuffer; diff --git a/libsrc/flatbufserver/FlatBufferConnection.cpp b/libsrc/flatbufserver/FlatBufferConnection.cpp index c758c6d5..870d6b07 100644 --- a/libsrc/flatbufserver/FlatBufferConnection.cpp +++ b/libsrc/flatbufserver/FlatBufferConnection.cpp @@ -7,10 +7,13 @@ // protoserver includes #include -FlatBufferConnection::FlatBufferConnection(const QString & address) +FlatBufferConnection::FlatBufferConnection(const QString& origin, const QString & address, const int& priority, const bool& skipReply) : _socket() + , _origin(origin) + , _priority(priority) , _prevSocketState(QAbstractSocket::UnconnectedState) , _log(Logger::getInstance("FLATBUFCONNECTION")) + , _registered(false) { QStringList parts = address.split(":"); if (parts.size() != 2) @@ -26,6 +29,9 @@ FlatBufferConnection::FlatBufferConnection(const QString & address) throw std::runtime_error(QString("FLATBUFCONNECTION ERROR: Unable to parse the port (%1)").arg(parts[1]).toStdString()); } + if(!skipReply) + connect(&_socket, &QTcpSocket::readyRead, this, &FlatBufferConnection::readData, Qt::UniqueConnection); + // init connect Info(_log, "Connecting to Hyperion: %s:%d", _host.toStdString().c_str(), _port); connectToHost(); @@ -66,10 +72,9 @@ void FlatBufferConnection::readData() const uint8_t* msgData = reinterpret_cast(msg.constData()); flatbuffers::Verifier verifier(msgData, messageSize); - if (flatbuf::VerifyHyperionReplyBuffer(verifier)) + if (hyperionnet::VerifyReplyBuffer(verifier)) { - auto message = flatbuf::GetHyperionReply(msgData); - parseReply(message); + parseReply(hyperionnet::GetReply(msgData)); continue; } Error(_log, "Unable to parse reply"); @@ -84,28 +89,39 @@ void FlatBufferConnection::setSkipReply(const bool& skip) connect(&_socket, &QTcpSocket::readyRead, this, &FlatBufferConnection::readData, Qt::UniqueConnection); } -void FlatBufferConnection::setColor(const ColorRgb & color, int priority, int duration) +void FlatBufferConnection::setRegister(const QString& origin, int priority) { - auto colorReq = flatbuf::CreateColorRequest(_builder, priority, (color.red << 16) | (color.green << 8) | color.blue, duration); - auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_COLOR, colorReq); + auto registerReq = hyperionnet::CreateRegister(_builder, _builder.CreateString(QSTRING_CSTR(origin)), priority); + auto req = hyperionnet::CreateRequest(_builder, hyperionnet::Command_Register, registerReq.Union()); _builder.Finish(req); sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); } -void FlatBufferConnection::setImage(const Image &image, int priority, int duration) +void FlatBufferConnection::setColor(const ColorRgb & color, int priority, int duration) { - /* #TODO #BROKEN auto imgData = _builder.CreateVector>(image.memptr(), image.width() * image.height() * 3); - auto imgReq = flatbuf::CreateImageRequest(_builder, priority, image.width(), image.height(), imgData, duration); - auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_IMAGE,0,imgReq); + auto colorReq = hyperionnet::CreateColor(_builder, (color.red << 16) | (color.green << 8) | color.blue, duration); + auto req = hyperionnet::CreateRequest(_builder, hyperionnet::Command_Color, colorReq.Union()); + _builder.Finish(req); - sendMessage(_builder.GetBufferPointer(), _builder.GetSize());*/ + sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); +} + +void FlatBufferConnection::setImage(const Image &image) +{ + auto imgData = _builder.CreateVector(reinterpret_cast(image.memptr()), image.size()); + auto rawImg = hyperionnet::CreateRawImage(_builder, imgData, image.width(), image.height()); + auto imageReq = hyperionnet::CreateImage(_builder, hyperionnet::ImageType_RawImage, rawImg.Union(), -1); + auto req = hyperionnet::CreateRequest(_builder,hyperionnet::Command_Image,imageReq.Union()); + + _builder.Finish(req); + sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); } void FlatBufferConnection::clear(int priority) { - auto clearReq = flatbuf::CreateClearRequest(_builder, priority); - auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_CLEAR,0,0,clearReq); + auto clearReq = hyperionnet::CreateClear(_builder, priority); + auto req = hyperionnet::CreateRequest(_builder,hyperionnet::Command_Clear, clearReq.Union()); _builder.Finish(req); sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); @@ -113,11 +129,7 @@ void FlatBufferConnection::clear(int priority) void FlatBufferConnection::clearAll() { - auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_CLEARALL); - - // send command message - _builder.Finish(req); - sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); + clear(-1); } void FlatBufferConnection::connectToHost() @@ -135,26 +147,30 @@ void FlatBufferConnection::sendMessage(const uint8_t* buffer, uint32_t size) // print out connection message only when state is changed if (_socket.state() != _prevSocketState ) { - switch (_socket.state() ) - { - case QAbstractSocket::UnconnectedState: - Info(_log, "No connection to Hyperion: %s:%d", _host.toStdString().c_str(), _port); - break; - - case QAbstractSocket::ConnectedState: - Info(_log, "Connected to Hyperion: %s:%d", _host.toStdString().c_str(), _port); - break; - - default: - Debug(_log, "Connecting to Hyperion: %s:%d", _host.toStdString().c_str(), _port); - break; + switch (_socket.state() ) + { + case QAbstractSocket::UnconnectedState: + Info(_log, "No connection to Hyperion: %s:%d", _host.toStdString().c_str(), _port); + break; + case QAbstractSocket::ConnectedState: + Info(_log, "Connected to Hyperion: %s:%d", _host.toStdString().c_str(), _port); + _registered = false; + break; + default: + Debug(_log, "Connecting to Hyperion: %s:%d", _host.toStdString().c_str(), _port); + break; } _prevSocketState = _socket.state(); } if (_socket.state() != QAbstractSocket::ConnectedState) + return; + + if(!_registered) { + _registered = true; + setRegister(_origin, _priority); return; } @@ -168,46 +184,22 @@ void FlatBufferConnection::sendMessage(const uint8_t* buffer, uint32_t size) int count = 0; count += _socket.write(reinterpret_cast(header), 4); count += _socket.write(reinterpret_cast(buffer), size); - if (!_socket.waitForBytesWritten()) - { - Error(_log, "Error while writing data to host"); - return; - } + _socket.flush(); + _builder.Clear(); } -bool FlatBufferConnection::parseReply(const flatbuf::HyperionReply *reply) +bool FlatBufferConnection::parseReply(const hyperionnet::Reply *reply) { - bool success = false; - - switch (reply->type()) + if (!reply->error()) { - case flatbuf::Type_REPLY: - { - if (!reply->success()) - { - if (flatbuffers::IsFieldPresent(reply, flatbuf::HyperionReply::VT_ERROR)) - { - throw std::runtime_error("PROTOCONNECTION ERROR: " + reply->error()->str()); - } - else - { - throw std::runtime_error("PROTOCONNECTION ERROR: No error info"); - } - } - else - { - success = true; - } - - break; - } - case flatbuf::Type_VIDEO: - { - VideoMode vMode = (VideoMode)reply->video(); - emit setVideoMode(vMode); - break; + // no error set must be a success or video + const auto videoMode = reply->video(); + if (videoMode != -1) { + // We got a video reply. + emit setVideoMode(static_cast(videoMode)); } + return true; } - return success; + return false; } diff --git a/libsrc/flatbufserver/FlatBufferServer.cpp b/libsrc/flatbufserver/FlatBufferServer.cpp index 95694729..5522588a 100644 --- a/libsrc/flatbufserver/FlatBufferServer.cpp +++ b/libsrc/flatbufserver/FlatBufferServer.cpp @@ -58,6 +58,7 @@ void FlatBufferServer::newConnection() { if(QTcpSocket* socket = _server->nextPendingConnection()) { + Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this); // internal diff --git a/libsrc/flatbufserver/hyperion_reply.fbs b/libsrc/flatbufserver/hyperion_reply.fbs index 878f2612..5d9c3f69 100644 --- a/libsrc/flatbufserver/hyperion_reply.fbs +++ b/libsrc/flatbufserver/hyperion_reply.fbs @@ -1,15 +1,8 @@ -namespace flatbuf; +namespace hyperionnet; -enum Type : int { - REPLY = 0, - VIDEO = 1, -} - -table HyperionReply { - type:Type; - success:bool; +table Reply { error:string; - video:int; + video:int = -1; } -root_type HyperionReply; +root_type Reply; diff --git a/libsrc/flatbufserver/hyperion_request.fbs b/libsrc/flatbufserver/hyperion_request.fbs index 3ce9f423..1c6e3d49 100644 --- a/libsrc/flatbufserver/hyperion_request.fbs +++ b/libsrc/flatbufserver/hyperion_request.fbs @@ -1,35 +1,37 @@ -namespace flatbuf; +namespace hyperionnet; -enum Command : int { - COLOR = 0, - IMAGE = 1, - CLEAR = 2, - CLEARALL = 3, -} - -table HyperionRequest { - command:Command; - colorRequest:flatbuf.ColorRequest; - imageRequest:flatbuf.ImageRequest; - clearRequest:flatbuf.ClearRequest; -} - -table ColorRequest { - priority:int; - RgbColor:int; - duration:int; -} - -table ImageRequest { - priority:int; - imagewidth:int; - imageheight:int; - imagedata:[ubyte]; - duration:int; -} - -table ClearRequest { +// A priority value of -1 clears all priorities +table Register { + origin:string (required); priority:int; } -root_type HyperionRequest; +table RawImage { + data:[ubyte]; + width:int = -1; + height:int = -1; +} + +union ImageType {RawImage} + +table Image { + data:ImageType (required); + duration:int = -1; +} + +table Clear { + priority:int; +} + +table Color { + data:int = -1; + duration:int = -1; +} + +union Command {Color, Image, Clear, Register} + +table Request { + command:Command (required); +} + +root_type Request; diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index f5e8e5c6..2cd73b88 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -23,8 +23,6 @@ LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, const QJsonDo , _pause(false) , _currentConfigId(0) { - Debug(_log, "Instance created"); - // set initial state to true, as LedDevice::enabled() is true by default _hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, true); diff --git a/libsrc/hyperion/MessageForwarder.cpp b/libsrc/hyperion/MessageForwarder.cpp index abc0ed12..3ba11d99 100644 --- a/libsrc/hyperion/MessageForwarder.cpp +++ b/libsrc/hyperion/MessageForwarder.cpp @@ -98,7 +98,7 @@ void MessageForwarder::addProtoSlave(QString slave) } // verify loop with protoserver - const QJsonObject& obj = _hyperion->getSetting(settings::PROTOSERVER).object(); + const QJsonObject& obj = _hyperion->getSetting(settings::FLATBUFSERVER).object(); if(QHostAddress(parts[0]) == QHostAddress::LocalHost && parts[1].toInt() == obj["port"].toInt()) { Error(_log, "Loop between ProtoServer and Forwarder! (%s)",QSTRING_CSTR(slave)); diff --git a/libsrc/hyperion/SettingsManager.cpp b/libsrc/hyperion/SettingsManager.cpp index 6de6929c..e024aa1b 100644 --- a/libsrc/hyperion/SettingsManager.cpp +++ b/libsrc/hyperion/SettingsManager.cpp @@ -20,11 +20,11 @@ SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, con : _hyperion(hyperion) , _log(Logger::getInstance("SettingsManager")) { - Q_INIT_RESOURCE(resource); connect(this, &SettingsManager::settingsChanged, _hyperion, &Hyperion::settingsChanged); // get schema if(schemaJson.isEmpty()) { + Q_INIT_RESOURCE(resource); try { schemaJson = QJsonFactory::readSchema(":/hyperion-schema"); diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index 864a0485..8450773f 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -51,10 +51,6 @@ { "$ref": "schema-jsonServer.json" }, - "protoServer" : - { - "$ref": "schema-protoServer.json" - }, "flatbufServer": { "$ref": "schema-flatbufServer.json" diff --git a/libsrc/hyperion/resource.qrc b/libsrc/hyperion/resource.qrc index ad3a1993..5eeb9163 100644 --- a/libsrc/hyperion/resource.qrc +++ b/libsrc/hyperion/resource.qrc @@ -15,7 +15,6 @@ schema/schema-backgroundEffect.json schema/schema-forwarder.json schema/schema-jsonServer.json - schema/schema-protoServer.json schema/schema-flatbufServer.json schema/schema-boblightServer.json schema/schema-udpListener.json diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index e2169677..eb82a123 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -77,6 +77,7 @@ ENDFOREACH() add_library(leddevice ${CMAKE_BINARY_DIR}/LedDevice_headers.h ${Leddevice_SOURCES} ) target_link_libraries(leddevice + hyperion hyperion-utils ${CMAKE_THREAD_LIBS_INIT} Qt5::Network diff --git a/libsrc/ssdp/CMakeLists.txt b/libsrc/ssdp/CMakeLists.txt new file mode 100644 index 00000000..939d59a7 --- /dev/null +++ b/libsrc/ssdp/CMakeLists.txt @@ -0,0 +1,14 @@ +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/ssdp) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/ssdp) + +FILE ( GLOB SSDP_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) + +add_library(ssdp + ${SSDP_SOURCES} +) + +target_link_libraries(ssdp + Qt5::Network + webserver +) diff --git a/libsrc/ssdp/SSDPDescription.h b/libsrc/ssdp/SSDPDescription.h new file mode 100644 index 00000000..70b37da6 --- /dev/null +++ b/libsrc/ssdp/SSDPDescription.h @@ -0,0 +1,41 @@ +#pragma once + +/// +/// The xml to fill with data +/// %1 base url http://192.168.0.177:80/ +/// %2 friendly name Hyperion 2.0.0 (192.168.0.177) +/// %3 modelNumber 2.0.0 +/// %4 serialNumber / UDN (H ID) Fjsa723dD0.... +/// +/// def URN urn:schemas-upnp-org:device:Basic:1 + +static const QString SSDP_DESCRIPTION = "" + "" + "" + "1" + "0" + "" + "%1" + "" + "urn:schemas-upnp-org:device:Basic:1" + "%2" + "Hyperion Open Source Ambient Lighting" + "https://www.hyperion-project.org" + "Hyperion Open Source Ambient Light" + "Hyperion" + "%3" + "https://www.hyperion-project.org" + "%4" + "uuid:%4" + "index.html" + "" + "" + "image/png" + "100" + "100" + "32" + "img/hyperion/ssdp_icon.png" + "" + "" + "" + ""; diff --git a/libsrc/ssdp/SSDPDiscover.cpp b/libsrc/ssdp/SSDPDiscover.cpp new file mode 100644 index 00000000..87130f24 --- /dev/null +++ b/libsrc/ssdp/SSDPDiscover.cpp @@ -0,0 +1,169 @@ +#include + +// qt inc +#include +#include + +static const QHostAddress SSDP_ADDR("239.255.255.250"); +static const quint16 SSDP_PORT(1900); + +// as per upnp spec 1.1, section 1.2.2. +// TODO: Make IP and port below another #define and replace message below +static const QString UPNP_DISCOVER_MESSAGE = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: 1\r\n" + "ST: %1\r\n" + "\r\n"; + +SSDPDiscover::SSDPDiscover(QObject* parent) + : QObject(parent) + , _log(Logger::getInstance("SSDPDISCOVER")) + , _udpSocket(new QUdpSocket(this)) +{ + +} + +void SSDPDiscover::searchForService(const QString& st) +{ + _searchTarget = st; + _usnList.clear(); + // setup socket + connect(_udpSocket, &QUdpSocket::readyRead, this, &SSDPDiscover::readPendingDatagrams, Qt::UniqueConnection); + + sendSearch(st); +} + +const QString SSDPDiscover::getFirstService(const searchType& type, const QString& st, const int& timeout_ms) +{ + Info(_log, "Search for Hyperion server..."); + _searchTarget = st; + + // search + sendSearch(st); + + _udpSocket->waitForReadyRead(timeout_ms); + + while (_udpSocket->hasPendingDatagrams()) + { + QByteArray datagram; + datagram.resize(_udpSocket->pendingDatagramSize()); + QHostAddress sender; + quint16 senderPort; + + _udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + + QString data(datagram); + QMap headers; + QString address; + // parse request + QStringList entries = data.split("\n", QString::SkipEmptyParts); + for(auto entry : entries) + { + // http header parse skip + if(entry.contains("HTTP/1.1")) + continue; + + // split into key:vale, be aware that value field may contain also a ":" + entry = entry.simplified(); + int pos = entry.indexOf(":"); + if(pos == -1) + continue; + + headers[entry.left(pos).trimmed().toLower()] = entry.mid(pos+1).trimmed(); + } + + // verify ssdp spec + if(!headers.contains("st")) + continue; + + // usn duplicates + if (_usnList.contains(headers.value("usn"))) + continue; + + if (headers.value("st") == _searchTarget) + { + _usnList << headers.value("usn"); + QUrl url(headers.value("location")); + //Info(_log, "Received msearch response from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st"))); + if(type == STY_WEBSERVER) + { + Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), url.port()); + + return url.host()+":"+QString::number(url.port()); + } + else if(type == STY_FLATBUFSERVER) + { + const QString fbsport = headers.value("hyperion-fbs-port"); + if(fbsport.isEmpty()) + { + continue; + } + else + { + Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), fbsport); + return url.host()+":"+fbsport; + } + } + } + } + Info(_log,"Search timeout, no Hyperion server found"); + return QString(); +} + +void SSDPDiscover::readPendingDatagrams() +{ + while (_udpSocket->hasPendingDatagrams()) { + + QByteArray datagram; + datagram.resize(_udpSocket->pendingDatagramSize()); + QHostAddress sender; + quint16 senderPort; + + _udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + + QString data(datagram); + QMap headers; + // parse request + QStringList entries = data.split("\n", QString::SkipEmptyParts); + for(auto entry : entries) + { + // http header parse skip + if(entry.contains("HTTP/1.1")) + continue; + + // split into key:vale, be aware that value field may contain also a ":" + entry = entry.simplified(); + int pos = entry.indexOf(":"); + if(pos == -1) + continue; + + headers[entry.left(pos).trimmed().toLower()] = entry.mid(pos+1).trimmed(); + } + + // verify ssdp spec + if(!headers.contains("st")) + continue; + + // usn duplicates + if (_usnList.contains(headers.value("usn"))) + continue; + + if (headers.value("st") == _searchTarget) + { + _usnList << headers.value("usn"); + //Info(_log, "Received msearch response from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st"))); + QUrl url(headers.value("location")); + emit newService(url.host()+":"+QString::number(url.port())); + } + } +} + +void SSDPDiscover::sendSearch(const QString& st) +{ + const QString msg = UPNP_DISCOVER_MESSAGE.arg(st); + + _udpSocket->writeDatagram(msg.toUtf8(), + QHostAddress(SSDP_ADDR), + SSDP_PORT); +} diff --git a/libsrc/ssdp/SSDPHandler.cpp b/libsrc/ssdp/SSDPHandler.cpp new file mode 100644 index 00000000..9ce8a47f --- /dev/null +++ b/libsrc/ssdp/SSDPHandler.cpp @@ -0,0 +1,144 @@ +#include + +#include +#include "SSDPDescription.h" +#include +#include + +#include +#include + +SSDPHandler::SSDPHandler(WebServer* webserver, const quint16& flatBufPort, QObject * parent) + : SSDPServer(parent) + , _webserver(webserver) + , _localAddress() + , _NCA(nullptr) +{ + _flatbufPort = flatBufPort; + setFlatBufPort(_flatbufPort); +} + +void SSDPHandler::initServer() +{ + // prep server + SSDPServer::initServer(); + + _NCA = new QNetworkConfigurationManager(this); + + // listen for mSearchRequestes + connect(this, &SSDPServer::msearchRequestReceived, this, &SSDPHandler::handleMSearchRequest); + + connect(_NCA, &QNetworkConfigurationManager::configurationChanged, this, &SSDPHandler::handleNetworkConfigurationChanged); + + // get localAddress from interface + if(!getLocalAddress().isEmpty()) + { + _localAddress = getLocalAddress(); + } + + // startup if localAddress is found + if(!_localAddress.isEmpty() && _webserver->isInited()) + { + handleWebServerStateChange(true); + } +} + +void SSDPHandler::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config) +{ + if(type == settings::FLATBUFSERVER) + { + const QJsonObject& obj = config.object(); + if(obj["port"].toInt() != _flatbufPort) + { + _flatbufPort = obj["port"].toInt(); + setFlatBufPort(_flatbufPort); + } + } +} + +void SSDPHandler::handleWebServerStateChange(const bool newState) +{ + if(newState) + { + // refresh info + _webserver->setSSDPDescription(buildDesc()); + setDescriptionAddress(getDescAddress()); + if(start()) + { + sendAlive("upnp:rootdevice"); + sendAlive("urn:schemas-upnp-org:device:basic:1"); + sendAlive("urn:hyperion-project.org:device:basic:1"); + } + } + else + { + _webserver->setSSDPDescription(""); + stop(); + } +} + +void SSDPHandler::handleNetworkConfigurationChanged(const QNetworkConfiguration &config) +{ + // get localAddress from interface + if(!getLocalAddress().isEmpty()) + { + QString localAddress = getLocalAddress(); + if(_localAddress != localAddress) + { + // revoke old ip + sendByeBye("upnp:rootdevice"); + sendByeBye("urn:schemas-upnp-org:device:basic:1"); + sendByeBye("urn:hyperion-project.org:device:basic:1"); + + // update desc & notify new ip + _localAddress = localAddress; + _webserver->setSSDPDescription(buildDesc()); + setDescriptionAddress(getDescAddress()); + sendAlive("upnp:rootdevice"); + sendAlive("urn:schemas-upnp-org:device:basic:1"); + sendAlive("urn:hyperion-project.org:device:basic:1"); + } + } +} + +const QString SSDPHandler::getLocalAddress() +{ + // get the first valid IPv4 address. This is probably not that one we actually want to announce + for( const auto & address : QNetworkInterface::allAddresses()) + { + // is valid when, no loopback, IPv4 + if (!address.isLoopback() && address.protocol() == QAbstractSocket::IPv4Protocol ) + { + return address.toString(); + } + } + return QString(); +} + +void SSDPHandler::handleMSearchRequest(const QString& target, const QString& mx, const QString address, const quint16 & port) +{ + // TODO Response delay according to MX field (sec) random between 0 and MX + + // when searched for all devices / root devices / basic device + if(target == "ssdp:all" || target == "upnp:rootdevice" || target == "urn:schemas-upnp-org:device:basic:1" || target == "urn:hyperion-project.org:device:basic:1") + sendMSearchResponse(target, address, port); +} + +const QString SSDPHandler::getDescAddress() +{ + return getBaseAddress()+"description.xml"; +} + +const QString SSDPHandler::getBaseAddress() +{ + return "http://"+_localAddress+":"+QString::number(_webserver->getPort())+"/"; +} + +const QString SSDPHandler::buildDesc() +{ + /// %1 base url http://192.168.0.177:80/ + /// %2 friendly name Hyperion 2.0.0 (192.168.0.177) + /// %3 modelNumber 2.0.0 + /// %4 serialNumber / UDN (H ID) Fjsa723dD0.... + return SSDP_DESCRIPTION.arg(getBaseAddress(), QString("Hyperion (%2)").arg(_localAddress), QString(HYPERION_VERSION), Hyperion::getInstance()->getId()); +} diff --git a/libsrc/ssdp/SSDPServer.cpp b/libsrc/ssdp/SSDPServer.cpp new file mode 100644 index 00000000..01a586f0 --- /dev/null +++ b/libsrc/ssdp/SSDPServer.cpp @@ -0,0 +1,227 @@ +#include + +// util +#include +#include +#include + +#include +#include + +static const QHostAddress SSDP_ADDR("239.255.255.250"); +static const quint16 SSDP_PORT(1900); +static const QString SSDP_MAX_AGE("1800"); + +// as per upnp spec 1.1, section 1.2.2. +// - BOOTID.UPNP.ORG +// - CONFIGID.UPNP.ORG +// - SEARCHPORT.UPNP.ORG (optional) +// TODO: Make IP and port below another #define and replace message below +static const QString UPNP_ALIVE_MESSAGE = "NOTIFY * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "CACHE-CONTROL: max-age=%1\r\n" + "LOCATION: %2\r\n" + "NT: %3\r\n" + "NTS: ssdp:alive\r\n" + "SERVER: %4\r\n" + "USN: uuid:%5\r\n" + "HYPERION-FBS-PORT: %6\r\n" + "\r\n"; + +// Implement ssdp:update as per spec 1.1, section 1.2.4 +// and use the below define to build the message, where +// SEARCHPORT.UPNP.ORG are optional. +// TODO: Make IP and port below another #define and replace message below +static const QString UPNP_UPDATE_MESSAGE = "NOTIFY * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "LOCATION: %1\r\n" + "NT: %2\r\n" + "NTS: ssdp:update\r\n" + "USN: uuid:%3\r\n" +/* "CONFIGID.UPNP.ORG: %4\r\n" +UPNP spec = 1.1 "NEXTBOOTID.UPNP.ORG: %5\r\n" + "SEARCHPORT.UPNP.ORG: %6\r\n" +*/ "\r\n"; + +// TODO: Add this two fields commented below in the BYEBYE MESSAGE +// as per upnp spec 1.1, section 1.2.2 and 1.2.3. +// - BOOTID.UPNP.ORG +// - CONFIGID.UPNP.ORG +// TODO: Make IP and port below another #define and replace message below +static const QString UPNP_BYEBYE_MESSAGE = "NOTIFY * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "NT: %1\r\n" + "NTS: ssdp:byebye\r\n" + "USN: uuid:%2\r\n" + "\r\n"; + +// TODO: Add this three fields commented below in the MSEARCH_RESPONSE +// as per upnp spec 1.1, section 1.3.3. +// - BOOTID.UPNP.ORG +// - CONFIGID.UPNP.ORG +// - SEARCHPORT.UPNP.ORG (optional) +static const QString UPNP_MSEARCH_RESPONSE = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age = %1\r\n" + "DATE: %2\r\n" + "EXT: \r\n" + "LOCATION: %3\r\n" + "SERVER: %4\r\n" + "ST: %5\r\n" + "USN: uuid:%6\r\n" + "HYPERION-FBS-PORT: %7\r\n" + "\r\n"; + +SSDPServer::SSDPServer(QObject * parent) + : QObject(parent) + , _log(Logger::getInstance("SSDP")) + , _udpSocket(nullptr) + , _running(false) +{ + +} + +SSDPServer::~SSDPServer() +{ + stop(); +} + +void SSDPServer::initServer() +{ + _udpSocket = new QUdpSocket(this); + + // get system info + SysInfo::HyperionSysInfo data = SysInfo::get(); + + // create SERVER String + _serverHeader = data.prettyName+"/"+data.productVersion+" UPnP/1.0 Hyperion/"+QString(HYPERION_VERSION); + + // usn uuid + _uuid = Hyperion::getInstance()->getId(); + + connect(_udpSocket, &QUdpSocket::readyRead, this, &SSDPServer::readPendingDatagrams); +} + +const bool SSDPServer::start() +{ + if(!_running && _udpSocket->bind(QHostAddress::AnyIPv4, SSDP_PORT, QAbstractSocket::ShareAddress)) + { + _udpSocket->joinMulticastGroup(SSDP_ADDR); + _running = true; + return true; + } + return false; +} + +void SSDPServer::stop() +{ + if(_running) + { + // send BYEBYE Msg + sendByeBye("upnp:rootdevice"); + sendByeBye("urn:schemas-upnp-org:device:basic:1"); + sendByeBye("urn:hyperion-project.org:device:basic:1"); + _udpSocket->close(); + _running = false; + } +} + +void SSDPServer::readPendingDatagrams() +{ + while (_udpSocket->hasPendingDatagrams()) { + + QByteArray datagram; + datagram.resize(_udpSocket->pendingDatagramSize()); + QHostAddress sender; + quint16 senderPort; + + _udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + + QString data(datagram); + QMap headers; + // parse request + QStringList entries = data.split("\n", QString::SkipEmptyParts); + for(auto entry : entries) + { + // http header parse skip + if(entry.contains("HTTP/1.1")) + continue; + + // split into key:vale, be aware that value field may contain also a ":" + entry = entry.simplified(); + int pos = entry.indexOf(":"); + if(pos == -1) + continue; + + headers[entry.left(pos).trimmed().toLower()] = entry.mid(pos+1).trimmed(); + } + + // verify ssdp spec + if(!headers.contains("man")) + continue; + + if (headers.value("man") == "\"ssdp:discover\"") + { + //Debug(_log, "Received msearch from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st"))); + emit msearchRequestReceived(headers.value("st"), headers.value("mx"), sender.toString(), senderPort); + } + } +} + +void SSDPServer::sendMSearchResponse(const QString& st, const QString& senderIp, const quint16& senderPort) +{ + QString message = UPNP_MSEARCH_RESPONSE.arg(SSDP_MAX_AGE + , QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy HH:mm:ss GMT") + , _descAddress + , _serverHeader + , st + , _uuid + , _fbsPort ); + + _udpSocket->writeDatagram(message.toUtf8(), + QHostAddress(senderIp), + senderPort); +} + +void SSDPServer::sendByeBye(const QString& st) +{ + QString message = UPNP_BYEBYE_MESSAGE.arg(st, _uuid+"::"+st ); + + // we repeat 3 times + quint8 rep = 0; + while(rep < 3) { + _udpSocket->writeDatagram(message.toUtf8(), + QHostAddress(SSDP_ADDR), + SSDP_PORT); + rep++; + } +} + +void SSDPServer::sendAlive(const QString& st) +{ + QString message = UPNP_ALIVE_MESSAGE.arg(SSDP_MAX_AGE + , _descAddress + , st + , _serverHeader + , _uuid+"::"+st + , _fbsPort); + + // we repeat 3 times + quint8 rep = 0; + while(rep < 3) { + _udpSocket->writeDatagram(message.toUtf8(), + QHostAddress(SSDP_ADDR), + SSDP_PORT); + rep++; + } +} + +void SSDPServer::sendUpdate(const QString& st) +{ + QString message = UPNP_UPDATE_MESSAGE.arg(_descAddress + , st + , _uuid+"::"+st ); + + _udpSocket->writeDatagram(message.toUtf8(), + QHostAddress(SSDP_ADDR), + SSDP_PORT); +} diff --git a/libsrc/udplistener/UDPListener.cpp b/libsrc/udplistener/UDPListener.cpp index 4888ea52..498ead9f 100644 --- a/libsrc/udplistener/UDPListener.cpp +++ b/libsrc/udplistener/UDPListener.cpp @@ -22,8 +22,6 @@ UDPListener::UDPListener(const QJsonDocument& config) : _isActive(false), _listenPort(0) { - Debug(_log, "Instance created"); - // init handleSettingsUpdate(settings::UDPLISTENER, config); } diff --git a/libsrc/webserver/QtHttpServer.h b/libsrc/webserver/QtHttpServer.h index 50264470..6a949e64 100644 --- a/libsrc/webserver/QtHttpServer.h +++ b/libsrc/webserver/QtHttpServer.h @@ -49,7 +49,7 @@ public: quint16 getServerPort (void) const; QString getErrorString (void) const; -// const bool isListening(void) { return m_sockServer->isListening(); }; + const bool isListening(void) { return m_sockServer->isListening(); }; public slots: void start (quint16 port = 0); diff --git a/libsrc/webserver/StaticFileServing.cpp b/libsrc/webserver/StaticFileServing.cpp index bf11cbc5..ad260374 100644 --- a/libsrc/webserver/StaticFileServing.cpp +++ b/libsrc/webserver/StaticFileServing.cpp @@ -32,6 +32,14 @@ void StaticFileServing::setBaseUrl(const QString& url) _cgi.setBaseUrl(url); } +void StaticFileServing::setSSDPDescription(const QString& desc) +{ + if(desc.isEmpty()) + _ssdpDescription.clear(); + else + _ssdpDescription = desc.toLocal8Bit(); +} + void StaticFileServing::printErrorToReply (QtHttpReply * reply, QtHttpReply::StatusCode code, QString errorMessage) { reply->setStatusCode(code); @@ -76,24 +84,33 @@ void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpRepl QStringList uri_parts = path.split('/', QString::SkipEmptyParts); // special uri handling for server commands - if ( ! uri_parts.empty() && uri_parts.at(0) == "cgi" ) + if ( ! uri_parts.empty() ) { - uri_parts.removeAt(0); - try + if(uri_parts.at(0) == "cgi") { - _cgi.exec(uri_parts, request, reply); + uri_parts.removeAt(0); + try + { + _cgi.exec(uri_parts, request, reply); + } + catch(int err) + { + Error(_log,"Exception while executing cgi %s : %d", path.toStdString().c_str(), err); + printErrorToReply (reply, QtHttpReply::InternalError, "script failed (" % path % ")"); + } + catch(std::exception &e) + { + Error(_log,"Exception while executing cgi %s : %s", path.toStdString().c_str(), e.what()); + printErrorToReply (reply, QtHttpReply::InternalError, "script failed (" % path % ")"); + } + return; } - catch(int err) + else if(uri_parts.at(0) == "description.xml" && !_ssdpDescription.isNull()) { - Error(_log,"Exception while executing cgi %s : %d", path.toStdString().c_str(), err); - printErrorToReply (reply, QtHttpReply::InternalError, "script failed (" % path % ")"); + reply->addHeader ("Content-Type", "text/xml"); + reply->appendRawData (_ssdpDescription); + return; } - catch(std::exception &e) - { - Error(_log,"Exception while executing cgi %s : %s", path.toStdString().c_str(), e.what()); - printErrorToReply (reply, QtHttpReply::InternalError, "script failed (" % path % ")"); - } - return; } QFileInfo info(_baseUrl % "/" % path); diff --git a/libsrc/webserver/StaticFileServing.h b/libsrc/webserver/StaticFileServing.h index 88821cbf..c44f1a65 100644 --- a/libsrc/webserver/StaticFileServing.h +++ b/libsrc/webserver/StaticFileServing.h @@ -1,16 +1,14 @@ #ifndef STATICFILESERVING_H #define STATICFILESERVING_H -// locales includes -#include "CgiHandler.h" - -// qt includes #include + +//#include "QtHttpServer.h" #include "QtHttpRequest.h" #include "QtHttpReply.h" #include "QtHttpHeader.h" +#include "CgiHandler.h" -//utils includes #include class StaticFileServing : public QObject { @@ -20,7 +18,15 @@ public: explicit StaticFileServing (QObject * parent = nullptr); virtual ~StaticFileServing (void); + /// + /// @brief Overwrite current base url + /// void setBaseUrl(const QString& url); + /// + /// @brief Set a new SSDP description, if empty the description will be unset and clients will get a NotFound + /// @param The description + /// + void setSSDPDescription(const QString& desc); public slots: void onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply); @@ -30,6 +36,7 @@ private: QMimeDatabase * _mimeDb; CgiHandler _cgi; Logger * _log; + QByteArray _ssdpDescription; void printErrorToReply (QtHttpReply * reply, QtHttpReply::StatusCode code, QString errorMessage); diff --git a/libsrc/webserver/WebServer.cpp b/libsrc/webserver/WebServer.cpp index 90be5709..876a025c 100644 --- a/libsrc/webserver/WebServer.cpp +++ b/libsrc/webserver/WebServer.cpp @@ -1,23 +1,34 @@ #include "webserver/WebServer.h" #include "StaticFileServing.h" - -// qt includes #include "QtHttpServer.h" + #include #include -// bonjour includes +// bonjour #include -#include -// utils includes +// netUtil #include + WebServer::WebServer(const QJsonDocument& config, QObject * parent) : QObject(parent) + , _config(config) , _log(Logger::getInstance("WEBSERVER")) - , _server(new QtHttpServer (this)) + , _server() { + +} + +WebServer::~WebServer() +{ + stop(); +} + +void WebServer::initServer() +{ + _server = new QtHttpServer (this); _server->setServerName (QStringLiteral ("Hyperion Webserver")); connect (_server, &QtHttpServer::started, this, &WebServer::onServerStarted); @@ -28,18 +39,14 @@ WebServer::WebServer(const QJsonDocument& config, QObject * parent) _staticFileServing = new StaticFileServing (this); connect(_server, &QtHttpServer::requestNeedsReply, _staticFileServing, &StaticFileServing::onRequestNeedsReply); - Debug(_log, "Instance created"); // init - handleSettingsUpdate(settings::WEBSERVER, config); -} - -WebServer::~WebServer() -{ - stop(); + handleSettingsUpdate(settings::WEBSERVER, _config); } void WebServer::onServerStarted (quint16 port) { + _inited= true; + Info(_log, "Started on port %d name '%s'", port ,_server->getServerName().toStdString().c_str()); if(_serviceRegister == nullptr) @@ -53,10 +60,12 @@ void WebServer::onServerStarted (quint16 port) _serviceRegister = new BonjourServiceRegister(this); _serviceRegister->registerService("_hyperiond-http._tcp", port); } + emit stateChange(true); } void WebServer::onServerStopped () { Info(_log, "Stopped %s", _server->getServerName().toStdString().c_str()); + emit stateChange(false); } void WebServer::onServerError (QString msg) @@ -72,6 +81,7 @@ void WebServer::handleSettingsUpdate(const settings::type& type, const QJsonDocu _baseUrl = obj["document_root"].toString(WEBSERVER_DEFAULT_PATH); + if ( (_baseUrl != ":/webconfig") && !_baseUrl.trimmed().isEmpty()) { QFileInfo info(_baseUrl); @@ -94,8 +104,11 @@ void WebServer::handleSettingsUpdate(const settings::type& type, const QJsonDocu } // eval if the port is available, will be incremented if not - NetUtils::portAvailable(_port, _log); + if(!_server->isListening()) + NetUtils::portAvailable(_port, _log); + start(); + emit portChanged(_port); } } @@ -108,3 +121,8 @@ void WebServer::stop() { _server->stop(); } + +void WebServer::setSSDPDescription(const QString & desc) +{ + _staticFileServing->setSSDPDescription(desc); +} diff --git a/src/hyperion-aml/CMakeLists.txt b/src/hyperion-aml/CMakeLists.txt index feddffd1..4d7c1315 100644 --- a/src/hyperion-aml/CMakeLists.txt +++ b/src/hyperion-aml/CMakeLists.txt @@ -4,8 +4,8 @@ project(hyperion-aml) find_package(Qt5Widgets REQUIRED) include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver - ${PROTOBUF_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver + ${FLATBUFFERS_INCLUDE_DIRS} ) set(Hyperion_AML_HEADERS @@ -27,9 +27,11 @@ target_link_libraries(${PROJECT_NAME} commandline blackborder hyperion-utils - protoclient + flatbufserver + flatbuffers amlogic-grabber framebuffer-grabber + ssdp Qt5::Core Qt5::Gui Qt5::Network diff --git a/src/hyperion-aml/hyperion-aml.cpp b/src/hyperion-aml/hyperion-aml.cpp index 17077ffc..21f387ca 100644 --- a/src/hyperion-aml/hyperion-aml.cpp +++ b/src/hyperion-aml/hyperion-aml.cpp @@ -4,12 +4,15 @@ #include #include -#include +#include #include "AmlogicWrapper.h" #include "HyperionConfig.h" #include +// ssdp discover +#include + using namespace commandline; // save the image as screenshot @@ -32,7 +35,7 @@ int main(int argc, char ** argv) try { // create the option parser and initialize all parser - Parser parser("AmLogic capture application for Hyperion"); + Parser parser("AmLogic capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used."); IntOption & argFps = parser.add ('f', "framerate", "Capture frame rate [default: %1]", "10"); IntOption & argWidth = parser.add (0x0, "width", "Width of the captured image [default: %1]", "160", 160, 4096); @@ -62,11 +65,26 @@ int main(int argc, char ** argv) } else { - // Create the Proto-connection with hyperiond - ProtoConnectionWrapper protoWrapper(argAddress.value(parser), argPriority.getInt(parser), 1000, parser.isSet(argSkipReply)); + // server searching by ssdp + QString address; + if(parser.isSet(argAddress)) + { + address = argAddress.value(parser); + } + else + { + SSDPDiscover discover; + address = discover.getFirstService(STY_FLATBUFSERVER); + if(address.isEmpty()) + { + address = argAddress.value(parser); + } + } + // Create the Flabuf-connection + FlatBufferConnection flatbuf("AML Standalone", address, argPriority.getInt(parser), parser.isSet(argSkipReply)); - // Connect the screen capturing to the proto processing - QObject::connect(&amlWrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(receiveImage(Image))); + // Connect the screen capturing to flatbuf connection processing + QObject::connect(&amlWrapper, SIGNAL(sig_screenshot(const Image &)), &flatbuf, SLOT(setImage(Image))); // Start the capturing amlWrapper.start(); diff --git a/src/hyperion-dispmanx/CMakeLists.txt b/src/hyperion-dispmanx/CMakeLists.txt index 4d4e2425..52300bce 100644 --- a/src/hyperion-dispmanx/CMakeLists.txt +++ b/src/hyperion-dispmanx/CMakeLists.txt @@ -10,9 +10,9 @@ ELSE() ENDIF() include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver + ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver ${BCM_INCLUDE_DIRS} - ${PROTOBUF_INCLUDE_DIRS} + ${FLATBUFFERS_INCLUDE_DIRS} ) set(Hyperion_Dispmanx_HEADERS @@ -34,9 +34,11 @@ target_link_libraries( ${PROJECT_NAME} commandline blackborder hyperion-utils - protoclient + flatbufserver + flatbuffers dispmanx-grabber ${Dispmanx_LIBRARIES} + ssdp Qt5::Core Qt5::Gui Qt5::Network diff --git a/src/hyperion-dispmanx/hyperion-dispmanx.cpp b/src/hyperion-dispmanx/hyperion-dispmanx.cpp index 6ea13816..0f626023 100644 --- a/src/hyperion-dispmanx/hyperion-dispmanx.cpp +++ b/src/hyperion-dispmanx/hyperion-dispmanx.cpp @@ -3,12 +3,15 @@ #include #include -#include +#include #include "DispmanxWrapper.h" #include "HyperionConfig.h" #include +// ssdp discover +#include + using namespace commandline; // save the image as screenshot @@ -31,7 +34,7 @@ int main(int argc, char ** argv) try { // create the option parser and initialize all parameters - Parser parser("Dispmanx capture application for Hyperion"); + Parser parser("Dispmanx capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used."); IntOption & argFps = parser.add ('f', "framerate", "Capture frame rate [default: %1]", "10"); IntOption & argWidth = parser.add (0x0, "width", "Width of the captured image [default: %1]", "64", 32, 4096); @@ -90,11 +93,26 @@ int main(int argc, char ** argv) } else { - // Create the Proto-connection with hyperiond - ProtoConnectionWrapper protoWrapper(argAddress.value(parser), argPriority.getInt(parser), 1000, parser.isSet(argSkipReply)); + // server searching by ssdp + QString address; + if(parser.isSet(argAddress)) + { + address = argAddress.value(parser); + } + else + { + SSDPDiscover discover; + address = discover.getFirstService(STY_FLATBUFSERVER); + if(address.isEmpty()) + { + address = argAddress.value(parser); + } + } + // Create the Flabuf-connection + FlatBufferConnection flatbuf("Dispmanx Standalone", address, argPriority.getInt(parser), parser.isSet(argSkipReply)); - // Connect the screen capturing to the proto processing - QObject::connect(&dispmanxWrapper, SIGNAL(sig_screenshot(const Image &)), &protoWrapper, SLOT(receiveImage(Image))); + // Connect the screen capturing to flatbuf connection processing + QObject::connect(&dispmanxWrapper, SIGNAL(sig_screenshot(const Image &)), &flatbuf, SLOT(setImage(Image))); // Start the capturing dispmanxWrapper.start(); diff --git a/src/hyperion-framebuffer/CMakeLists.txt b/src/hyperion-framebuffer/CMakeLists.txt index 83f69f72..a2ca60d7 100644 --- a/src/hyperion-framebuffer/CMakeLists.txt +++ b/src/hyperion-framebuffer/CMakeLists.txt @@ -4,8 +4,8 @@ project(hyperion-framebuffer) find_package(Qt5Widgets REQUIRED) include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver - ${PROTOBUF_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver + ${FLATBUFFERS_INCLUDE_DIRS} ) set(Hyperion_FB_HEADERS @@ -27,8 +27,10 @@ target_link_libraries( ${PROJECT_NAME} commandline blackborder hyperion-utils - protoclient + flatbufserver + flatbuffers framebuffer-grabber + ssdp Qt5::Core Qt5::Gui Qt5::Network diff --git a/src/hyperion-framebuffer/hyperion-framebuffer.cpp b/src/hyperion-framebuffer/hyperion-framebuffer.cpp index c6fc396d..e3987022 100644 --- a/src/hyperion-framebuffer/hyperion-framebuffer.cpp +++ b/src/hyperion-framebuffer/hyperion-framebuffer.cpp @@ -4,10 +4,13 @@ #include #include -#include +#include #include "FramebufferWrapper.h" #include +// ssdp discover +#include + using namespace commandline; // save the image as screenshot @@ -25,7 +28,7 @@ int main(int argc, char ** argv) try { // create the option parser and initialize all parameters - Parser parser("FrameBuffer capture application for Hyperion"); + Parser parser("FrameBuffer capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used."); Option & argDevice = parser.add