diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json
index 64ea652c7..aaa907e2b 100644
--- a/assets/webconfig/i18n/en.json
+++ b/assets/webconfig/i18n/en.json
@@ -1143,8 +1143,10 @@
"perf_undervoltage" : "Undervoltage detected",
"perf_no" : "No",
"perf_invalid_frames" : "invalid frames",
- "edt_conf_fbs_tonemapping_expl": "If enabled, HyperHDR will apply LUT correction for the incoming stream. Make sure you have got the correct LUT table. Default 3D LUT file for HDR10: 'lut_lin_tables.3d' is already included. You can generate one and preview the effect using link in the 'Advanced menu'.
Your typical hidden configuration folder to upload that file in is (check 'Logs' page to confirm):
Rpi→/home/pi/.hyperhdr
Windows→c:/Users/NAME/.hyperhdr",
+ "edt_conf_fbs_tonemapping_expl": "If enabled, HyperHDR will apply LUT correction for the incoming stream. Make sure you have got the correct LUT table. Default 3D LUT file for HDR10: 'lut_lin_tables.3d' (filename 'flat_lut_lin_tables.3d' is also accepted) is already included. You can generate one and preview the effect using link in the 'Advanced menu'.
Your typical hidden configuration folder to upload that file in is (check 'Logs' page to confirm):
Rpi→/home/pi/.hyperhdr
Windows→c:/Users/NAME/.hyperhdr",
"edt_conf_fbs_tonemapping_title": "HDR to SDR tone mapping",
"edt_conf_fbs_hdrToneMappingMode_title": "Area for LUT mode effect",
- "edt_conf_fbs_hdrToneMappingMode_expl": "Fullscreen or faster Border Mode"
+ "edt_conf_fbs_hdrToneMappingMode_expl": "Fullscreen or faster Border Mode",
+ "general_comp_RAWUDPSERVER" : "UDP raw receiver",
+ "edt_udp_raw_server" : "A lightweight server for remote synchronization of HyperHDR instances using UDP and raw RGB LED colors. Use the 'udpraw' light source in the sender. Important: both instances should have the same number of LEDs and same geometry for this to work."
}
diff --git a/assets/webconfig/js/network.js b/assets/webconfig/js/network.js
index 6f1c039fa..43a41f7e8 100644
--- a/assets/webconfig/js/network.js
+++ b/assets/webconfig/js/network.js
@@ -10,6 +10,7 @@ $(document).ready( function() {
var conf_editor_fbs = null;
var conf_editor_bobl = null;
var conf_editor_forw = null;
+ var conf_editor_rawUdp = null;
{
$('#conf_cont').append(createOptPanel('fa-wrench', $.i18n("edt_conf_webc_heading_title"), 'editor_container', 'btn_submit_www'));
@@ -34,6 +35,9 @@ $(document).ready( function() {
$('#conf_cont').append(createHelpTable(window.schema.protoServer.properties, $.i18n("edt_conf_pbs_heading_title")));
}
+ $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("general_comp_RAWUDPSERVER"), 'editor_container_rawUdpServer', 'btn_submit_rawUdpServer'));
+ $('#conf_cont').append(createHelpTable(window.schema.rawUdpServer.properties, $.i18n("general_comp_RAWUDPSERVER")));
+
//boblight
if (BOBLIGHT_ENABLED)
{
@@ -123,6 +127,20 @@ $(document).ready( function() {
});
}
+ conf_editor_rawUdp = createJsonEditor('editor_container_rawUdpServer', {
+ rawUdpServer : window.schema.rawUdpServer
+ }, true, true);
+
+ conf_editor_rawUdp.on('change',function() {
+ conf_editor_rawUdp.validate().length || window.readOnlyMode ? $('#btn_submit_rawUdpServer').attr('disabled', true) : $('#btn_submit_rawUdpServer').attr('disabled', false);
+ });
+
+ $('#btn_submit_rawUdpServer').off().on('click',function() {
+ requestWriteConfig(conf_editor_rawUdp.getValue());
+ });
+
+ document.getElementById('editor_container_rawUdpServer').parentElement.firstElementChild.classList.add('is-instance');
+
//boblight
if (BOBLIGHT_ENABLED)
{
@@ -137,6 +155,8 @@ $(document).ready( function() {
$('#btn_submit_boblightserver').off().on('click',function() {
requestWriteConfig(conf_editor_bobl.getValue());
});
+
+ document.getElementById('editor_container_boblightserver').parentElement.firstElementChild.classList.add('is-instance');
}
if(storedAccess != 'default')
@@ -162,6 +182,7 @@ $(document).ready( function() {
createHint("intro", $.i18n('conf_network_net_intro'), "editor_container_net");
createHint("intro", $.i18n('conf_network_json_intro'), "editor_container_jsonserver");
createHint("intro", $.i18n('conf_network_fbs_intro'), "editor_container_fbserver");
+ createHint("intro", $.i18n('edt_udp_raw_server'), "editor_container_rawUdpServer");
if (window.serverInfo.hasPROTOBUF == 1)
{
@@ -239,5 +260,11 @@ $(document).ready( function() {
checkApiTokenState(window.serverConfig.network.apiAuth);
+ var instHeaders = document.getElementsByClassName("card-header");
+ Array.prototype.forEach.call(instHeaders, function(instHeader, index) {
+ if (instHeader.classList.contains('is-instance'))
+ putInstanceName(instHeader);
+ });
+
removeOverlay();
});
diff --git a/assets/webconfig/js/remote.js b/assets/webconfig/js/remote.js
index d457a9d7b..9e642574c 100644
--- a/assets/webconfig/js/remote.js
+++ b/assets/webconfig/js/remote.js
@@ -207,6 +207,9 @@ $(document).ready(function()
case "PROTOSERVER":
owner = $.i18n('general_comp_PROTOSERVER');
break;
+ case "RAWUDPSERVER":
+ owner = $.i18n('general_comp_RAWUDPSERVER');
+ break;
}
if (duration && compId != "GRABBER" && compId != "FLATBUFSERVER" && compId != "PROTOSERVER")
diff --git a/include/base/HyperHdrInstance.h b/include/base/HyperHdrInstance.h
index cc7f1d988..912fa2c5b 100644
--- a/include/base/HyperHdrInstance.h
+++ b/include/base/HyperHdrInstance.h
@@ -47,6 +47,7 @@ class BGEffectHandler;
class VideoControl;
class SystemControl;
class BoblightServer;
+class RawUdpServer;
class LedDeviceWrapper;
class ImageProcessingUnit;
class Logger;
@@ -519,6 +520,7 @@ private slots:
/// Boblight instance
BoblightServer* _boblightServer;
+ RawUdpServer* _rawUdpServer;
QString _name;
diff --git a/include/utils/Components.h b/include/utils/Components.h
index 8c89088a9..9c5618111 100644
--- a/include/utils/Components.h
+++ b/include/utils/Components.h
@@ -23,6 +23,7 @@ namespace hyperhdr
COMP_EFFECT,
COMP_LEDDEVICE,
COMP_FLATBUFSERVER,
+ COMP_RAWUDPSERVER,
COMP_PROTOSERVER
};
@@ -43,6 +44,7 @@ namespace hyperhdr
case COMP_IMAGE: return "Image";
case COMP_LEDDEVICE: return "LED device";
case COMP_FLATBUFSERVER: return "Image Receiver";
+ case COMP_RAWUDPSERVER: return "Raw RGB UDP Server";
case COMP_PROTOSERVER: return "Proto Server";
default: return "";
}
@@ -65,6 +67,7 @@ namespace hyperhdr
case COMP_IMAGE: return "IMAGE";
case COMP_LEDDEVICE: return "LEDDEVICE";
case COMP_FLATBUFSERVER: return "FLATBUFSERVER";
+ case COMP_RAWUDPSERVER: return "RAWUDPSERVER";
case COMP_PROTOSERVER: return "PROTOSERVER";
default: return "";
}
@@ -86,6 +89,7 @@ namespace hyperhdr
if (cmp == "IMAGE") return COMP_IMAGE;
if (cmp == "LEDDEVICE") return COMP_LEDDEVICE;
if (cmp == "FLATBUFSERVER") return COMP_FLATBUFSERVER;
+ if (cmp == "RAWUDPSERVER") return COMP_RAWUDPSERVER;
if (cmp == "PROTOSERVER") return COMP_PROTOSERVER;
return COMP_INVALID;
}
diff --git a/include/utils/RawUdpServer.h b/include/utils/RawUdpServer.h
new file mode 100644
index 000000000..420d16a1e
--- /dev/null
+++ b/include/utils/RawUdpServer.h
@@ -0,0 +1,85 @@
+#pragma once
+
+/* RawUdpServer.h
+*
+* MIT License
+*
+* Copyright (c) 2022 awawa-dev
+*
+* Project homesite: https://github.com/awawa-dev/HyperHDR
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+
+// utils
+#include
+#include
+
+// qt
+#include
+
+class QUdpSocket;
+class NetOrigin;
+class HyperHdrInstance;
+class QTimer;
+
+class RawUdpServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ RawUdpServer(HyperHdrInstance* hyperhdr, const QJsonDocument& config, QObject* parent = nullptr);
+ ~RawUdpServer() override;
+
+public slots:
+ ///
+ /// @brief Handle settings update
+ /// @param type The type from enum
+ /// @param config The configuration
+ ///
+ void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
+
+ void initServer();
+
+ void readPendingDatagrams();
+
+ void dataTimeout();
+
+private:
+ ///
+ /// @brief Start the server with current _port
+ ///
+ void startServer();
+
+ ///
+ /// @brief Stop server
+ ///
+ void stopServer();
+
+private:
+ QUdpSocket* _server;
+ Logger* _log;
+ quint16 _port;
+ int _priority;
+ bool _initialized;
+
+ HyperHdrInstance* _hyperhdr;
+ const QJsonDocument _config;
+ QTimer* _inactiveTimer;
+};
diff --git a/include/utils/settings.h b/include/utils/settings.h
index 0e87ef20f..d6a5d6f23 100644
--- a/include/utils/settings.h
+++ b/include/utils/settings.h
@@ -31,6 +31,7 @@ namespace settings {
VIDEODETECTION,
NETWORK,
FLATBUFSERVER,
+ RAWUDPSERVER,
PROTOSERVER,
INVALID
};
@@ -67,6 +68,7 @@ namespace settings {
case type::VIDEODETECTION:return "videoDetection";
case type::NETWORK: return "network";
case type::FLATBUFSERVER: return "flatbufServer";
+ case type::RAWUDPSERVER: return "rawUdpServer";
case type::PROTOSERVER: return "protoServer";
default: return "invalid";
}
@@ -102,6 +104,7 @@ namespace settings {
else if (type == "videoDetection") return type::VIDEODETECTION;
else if (type == "network") return type::NETWORK;
else if (type == "flatbufServer") return type::FLATBUFSERVER;
+ else if (type == "rawUdpServer") return type::RAWUDPSERVER;
else if (type == "protoServer") return type::PROTOSERVER;
else return type::INVALID;
}
diff --git a/sources/base/HyperHdrInstance.cpp b/sources/base/HyperHdrInstance.cpp
index c5b54ec71..85612f6fc 100644
--- a/sources/base/HyperHdrInstance.cpp
+++ b/sources/base/HyperHdrInstance.cpp
@@ -49,6 +49,8 @@
class BoblightServer {};
#endif
+#include
+
HyperHdrInstance::HyperHdrInstance(quint8 instance, bool readonlyMode, QString name)
@@ -74,6 +76,7 @@ HyperHdrInstance::HyperHdrInstance(quint8 instance, bool readonlyMode, QString n
, _systemControl(nullptr)
, _globalLedBuffer(_ledString.leds().size(), ColorRgb::BLACK)
, _boblightServer(nullptr)
+ , _rawUdpServer(nullptr)
, _name((name.isEmpty()) ? QString("INSTANCE%1").arg(instance):name)
, _readOnlyMode(readonlyMode)
@@ -191,6 +194,9 @@ void HyperHdrInstance::start()
connect(this, &HyperHdrInstance::settingsChanged, _boblightServer, &BoblightServer::handleSettingsUpdate);
#endif
+ _rawUdpServer = new RawUdpServer(this, getSetting(settings::type::RAWUDPSERVER));
+ connect(this, &HyperHdrInstance::settingsChanged, _rawUdpServer, &RawUdpServer::handleSettingsUpdate);
+
// instance initiated, enter thread event loop
emit started();
}
@@ -212,6 +218,7 @@ void HyperHdrInstance::freeObjects()
// delete components on exit
delete _boblightServer;
+ delete _rawUdpServer;
delete _videoControl;
delete _systemControl;
delete _effectEngine;
diff --git a/sources/base/hyperhdr.schema.json b/sources/base/hyperhdr.schema.json
index 9cb38ff86..ea43bbcd5 100644
--- a/sources/base/hyperhdr.schema.json
+++ b/sources/base/hyperhdr.schema.json
@@ -59,6 +59,10 @@
{
"$ref": "schema-flatbufServer.json"
},
+ "rawUdpServer":
+ {
+ "$ref": "schema-rawUdpServer.json"
+ },
"protoServer" :
{
"$ref": "schema-protoServer.json"
diff --git a/sources/base/resource.qrc b/sources/base/resource.qrc
index 0c6f722f0..3171c0b06 100644
--- a/sources/base/resource.qrc
+++ b/sources/base/resource.qrc
@@ -15,6 +15,7 @@
schema/schema-forwarder.json
schema/schema-jsonServer.json
schema/schema-flatbufServer.json
+ schema/schema-rawUdpServer.json
schema/schema-protoServer.json
schema/schema-boblightServer.json
schema/schema-webConfig.json
diff --git a/sources/base/schema/schema-rawUdpServer.json b/sources/base/schema/schema-rawUdpServer.json
new file mode 100644
index 000000000..17daf29c6
--- /dev/null
+++ b/sources/base/schema/schema-rawUdpServer.json
@@ -0,0 +1,37 @@
+{
+ "type" : "object",
+ "title" : "general_comp_RAWUDPSERVER",
+ "properties" :
+ {
+ "enable" :
+ {
+ "type" : "boolean",
+ "format": "checkbox",
+ "title" : "edt_conf_general_enable_title",
+ "default" : false,
+ "required" : true,
+ "propertyOrder" : 1
+ },
+ "port" :
+ {
+ "type" : "integer",
+ "required" : true,
+ "title" : "edt_conf_general_port_title",
+ "default" : 5568,
+ "minimum" : 1024,
+ "maximum" : 65535,
+ "propertyOrder" : 2
+ },
+ "priority" :
+ {
+ "type" : "integer",
+ "title" : "edt_conf_general_priority_title",
+ "minimum" : 100,
+ "maximum" : 254,
+ "default" : 109,
+ "required" : true,
+ "propertyOrder" : 3
+ }
+ },
+ "additionalProperties" : false
+}
diff --git a/sources/utils/RawUdpServer.cpp b/sources/utils/RawUdpServer.cpp
new file mode 100644
index 000000000..352823b67
--- /dev/null
+++ b/sources/utils/RawUdpServer.cpp
@@ -0,0 +1,176 @@
+/* RawUdpServer.cpp
+*
+* MIT License
+*
+* Copyright (c) 2022 awawa-dev
+*
+* Project homesite: https://github.com/awawa-dev/HyperHDR
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+
+// util
+#include
+#include
+#include
+#include
+
+// qt
+#include
+#include
+#include
+#include
+#include
+
+RawUdpServer::RawUdpServer(HyperHdrInstance* hyperhdr, const QJsonDocument& config, QObject* parent)
+ : QObject(parent)
+ , _hyperhdr(hyperhdr)
+ , _server(new QUdpSocket(this))
+ , _log(Logger::getInstance("RAW_UDP_SERVER"))
+ , _port(0)
+ , _priority(0)
+ , _initialized(false)
+ , _config(config)
+ , _inactiveTimer(new QTimer(this))
+{
+ initServer();
+}
+
+RawUdpServer::~RawUdpServer()
+{
+ stopServer();
+
+ delete _server;
+}
+
+void RawUdpServer::initServer()
+{
+ if (_server != nullptr)
+ {
+ connect(_server, &QUdpSocket::readyRead, this, &RawUdpServer::readPendingDatagrams);
+
+ connect(_inactiveTimer, &QTimer::timeout, this, &RawUdpServer::dataTimeout);
+ _inactiveTimer->setSingleShot(true);
+ _inactiveTimer->setInterval(3000);
+ }
+
+ // apply config
+ handleSettingsUpdate(settings::type::RAWUDPSERVER, _config);
+}
+
+void RawUdpServer::dataTimeout()
+{
+ _hyperhdr->setInputInactive(_priority);
+}
+
+
+void RawUdpServer::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
+{
+ if (type == settings::type::RAWUDPSERVER)
+ {
+ const QJsonObject& obj = config.object();
+
+ quint16 port = obj["port"].toInt(5568);
+
+ // port check
+ if (_server != nullptr && _server->localPort() != port && _initialized)
+ {
+ stopServer();
+ }
+
+ _port = port;
+ _priority = obj["priority"].toInt(109);
+
+ // enable check
+ if (obj["enable"].toBool(true))
+ {
+ startServer();
+ }
+ else
+ {
+ if (_initialized)
+ _hyperhdr->setInputInactive(_priority);
+
+ _inactiveTimer->stop();
+
+ stopServer();
+ }
+ }
+}
+
+void RawUdpServer::readPendingDatagrams()
+{
+ while (_server->hasPendingDatagrams()) {
+ QNetworkDatagram datagram = _server->receiveDatagram();
+
+ auto dataLen = datagram.data().length();
+
+ if (dataLen % 3 > 0 || dataLen > 1500 || dataLen == 0)
+ continue;
+
+ if (_hyperhdr->getPriorityInfo(_priority).componentId != hyperhdr::COMP_RAWUDPSERVER)
+ _hyperhdr->registerInput(_priority, hyperhdr::COMP_RAWUDPSERVER, QString("%1").arg(datagram.senderAddress().toString()));
+
+ std::vector _ledColors;
+
+ for(int i = 0; i < dataLen;)
+ {
+ ColorRgb c;
+ c.red = datagram.data().at(i++);
+ c.green = datagram.data().at(i++);
+ c.blue = datagram.data().at(i++);
+ _ledColors.push_back(c);
+ }
+
+ _hyperhdr->setInput(_priority, _ledColors);
+
+ _inactiveTimer->start();
+ }
+}
+
+void RawUdpServer::startServer()
+{
+ if (_server != nullptr && !_initialized)
+ {
+ if (!_server->bind(QHostAddress::Any, _port))
+ {
+ Error(_log, "Failed to bind port %d", _port);
+ }
+ else
+ {
+ _initialized = true;
+
+ Info(_log, "Started on port %d. Using network interface: %s", _server->localPort(), QSTRING_CSTR(_server->localAddress().toString()));
+ }
+ }
+}
+
+void RawUdpServer::stopServer()
+{
+ if (_server != nullptr && _initialized)
+ {
+ // closing
+ if (_server != nullptr)
+ _server->abort();
+
+ _initialized = false;
+
+ Info(_log, "Stopped");
+ }
+}