From 0e97e30557d72613aa35860009a28849cb8ed750 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Sun, 17 Feb 2019 13:29:36 +0100 Subject: [PATCH 01/74] Updated rpi_ws281x submodule version to latest commit --- dependencies/external/rpi_ws281x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/external/rpi_ws281x b/dependencies/external/rpi_ws281x index f5807772..6c5ade93 160000 --- a/dependencies/external/rpi_ws281x +++ b/dependencies/external/rpi_ws281x @@ -1 +1 @@ -Subproject commit f580777219062568d2e43e998ecb0950deff9e99 +Subproject commit 6c5ade93d1af78cd19e60ee5ecc34adfd111b186 From 777caafb2411aa5f72faef64e3799ee70dca0eac Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Sun, 17 Feb 2019 13:44:26 +0100 Subject: [PATCH 02/74] Fixed failed QWidget linking for Systray without QT Grabber --- src/hyperiond/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index b755e106..bcce3312 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -1,4 +1,5 @@ find_package(PythonLibs 3.4 REQUIRED) +find_package(Qt5Widgets REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..) add_executable(hyperiond @@ -66,6 +67,8 @@ if (ENABLE_QT) target_link_libraries(hyperiond qt-grabber) endif () +target_link_libraries(hyperiond Qt5::Widgets) + install ( TARGETS hyperiond DESTINATION "share/hyperion/bin/" COMPONENT "${PLATFORM}" ) install ( DIRECTORY ${CMAKE_SOURCE_DIR}/bin/service DESTINATION "share/hyperion/" COMPONENT "${PLATFORM}" ) install ( FILES ${CMAKE_SOURCE_DIR}/effects/readme.txt DESTINATION "share/hyperion/effects" COMPONENT "${PLATFORM}" ) From 7ff9d7037b7d9a5e851615aee09dc748818fbe85 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Sun, 17 Feb 2019 15:26:11 +0100 Subject: [PATCH 03/74] ProtoBuffer, UDPListener ... Protocol Buffer reimplemented to receive image data from third-party apps The status of the component "UDPListener" is now displayed correctly in WebUI Global signal names for WebUI added --- .gitmodules | 5 + CMakeLists.txt | 4 + assets/webconfig/i18n/de.json | 7 +- assets/webconfig/i18n/en.json | 7 +- assets/webconfig/js/content_dashboard.js | 2 +- assets/webconfig/js/content_network.js | 20 ++ assets/webconfig/js/content_remote.js | 5 +- config/hyperion.config.json.commented | 9 + config/hyperion.config.json.default | 7 + dependencies/CMakeLists.txt | 112 +++++++++ dependencies/external/protobuf | 1 + include/hyperion/CaptureCont.h | 6 +- include/hyperion/GrabberWrapper.h | 4 +- include/hyperion/Hyperion.h | 2 +- include/hyperion/MessageForwarder.h | 2 +- include/protoserver/ProtoServer.h | 67 +++++ include/udplistener/UDPListener.h | 1 - include/utils/Components.h | 8 +- include/utils/GlobalSignals.h | 6 +- include/utils/settings.h | 7 +- libsrc/CMakeLists.txt | 1 + libsrc/flatbufserver/FlatBufferClient.h | 6 +- libsrc/flatbufserver/FlatBufferServer.cpp | 2 - libsrc/grabber/v4l2/V4L2Wrapper.cpp | 2 +- libsrc/hyperion/CaptureCont.cpp | 16 +- libsrc/hyperion/MessageForwarder.cpp | 2 +- libsrc/hyperion/hyperion.schema.json | 4 + libsrc/hyperion/resource.qrc | 1 + .../hyperion/schema/schema-protoServer.json | 36 +++ libsrc/protoserver/CMakeLists.txt | 44 ++++ libsrc/protoserver/ProtoClientConnection.cpp | 238 ++++++++++++++++++ libsrc/protoserver/ProtoClientConnection.h | 135 ++++++++++ libsrc/protoserver/ProtoServer.cpp | 104 ++++++++ libsrc/protoserver/message.proto | 80 ++++++ libsrc/udplistener/UDPListener.cpp | 8 +- src/hyperiond/CMakeLists.txt | 1 + src/hyperiond/hyperiond.cpp | 40 ++- src/hyperiond/hyperiond.h | 52 ++-- 38 files changed, 990 insertions(+), 64 deletions(-) create mode 160000 dependencies/external/protobuf create mode 100644 include/protoserver/ProtoServer.h create mode 100644 libsrc/hyperion/schema/schema-protoServer.json create mode 100644 libsrc/protoserver/CMakeLists.txt create mode 100644 libsrc/protoserver/ProtoClientConnection.cpp create mode 100644 libsrc/protoserver/ProtoClientConnection.h create mode 100644 libsrc/protoserver/ProtoServer.cpp create mode 100644 libsrc/protoserver/message.proto diff --git a/.gitmodules b/.gitmodules index 981fff4d..31ddf173 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,8 @@ [submodule "dependencies/external/flatbuffers"] path = dependencies/external/flatbuffers url = https://github.com/google/flatbuffers + branch = master +[submodule "dependencies/external/protobuf"] + path = dependencies/external/protobuf + url = https://github.com/hyperion-project/protobuf.git + branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt index 6615eee7..a8b24eca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ SET ( DEFAULT_QT ON ) SET ( DEFAULT_WS281XPWM OFF ) SET ( DEFAULT_USE_SHARED_AVAHI_LIBS ON ) SET ( DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF ) +SET ( DEFAULT_USE_SYSTEM_PROTO_LIBS OFF ) SET ( DEFAULT_TESTS OFF ) IF ( ${CMAKE_SYSTEM} MATCHES "Linux" ) @@ -164,6 +165,9 @@ message(STATUS "ENABLE_PROFILER = ${ENABLE_PROFILER}") SET ( FLATBUFFERS_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/flatbuf ) SET ( FLATBUFFERS_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/flatbuf ) +SET ( PROTOBUF_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/proto ) +SET ( PROTOBUF_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/proto ) + # check all json files FILE ( GLOB_RECURSE HYPERION_SCHEMAS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libsrc/*schema*.json ) SET( JSON_FILES diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json index 373dd697..bfced0ff 100644 --- a/assets/webconfig/i18n/de.json +++ b/assets/webconfig/i18n/de.json @@ -21,6 +21,7 @@ "general_comp_UDPLISTENER" : "UDP Listener", "general_comp_BOBLIGHTSERVER" : "Boblight Server", "general_comp_FLATBUFSERVER" : "Flatbuffers Server", + "general_comp_PROTOSERVER" : "Protocol Buffers Server", "general_comp_GRABBER" : "Plattform Aufnahme", "general_comp_V4L" : "USB Aufnahme", "general_comp_LEDDEVICE" : "LED Hardware", @@ -48,7 +49,7 @@ "dashboard_infobox_label_latesthyp" : "Aktuellste Hyperion Version:", "dashboard_infobox_label_platform" : "Plattform:", "dashboard_infobox_label_instance" : "Instanz:", - "dashboard_infobox_label_ports" : "Port flatbuf:", + "dashboard_infobox_label_ports" : "Ports (flat|proto):", "dashboard_infobox_message_updatewarning" : "Eine aktuellere Version von Hyperion ist verfügbar! (V$1)", "dashboard_infobox_message_updatesuccess" : "Du nutzt die aktuellste Version von Hyperion.", "dashboard_infobox_label_statush" : "Hyperion Status:", @@ -169,6 +170,7 @@ "conf_network_bobl_intro" : "Boblight Empfänger", "conf_network_udpl_intro" : "UDP Empfänger", "conf_network_fbs_intro" : "Google Flatbuffers Empfänger. Wird genutzt für schnellen Bildempfang.", + "conf_network_proto_intro" : "Der PROTO-Port dieser Hyperion-Instanz, wird genutzt für \"Bildstreams\" (HyperionScreenCap, Kodi Addon, ...)", "conf_network_forw_intro" : "Leite alles an eine zweite Hyperion Instanz weiter, diese kann dann mit einer anderen LED Steuerung genutzt werden", "conf_logging_label_intro" : "Überprüfe die Meldungen im Prokotoll um zu erfahren was Hyperion gerade beschäftigt. Je nach eingestellter Protokoll-Stufe siehst du mehr oder weniger Informationen.", "conf_logging_btn_pbupload" : "Bericht für Supportanfrage hochladen", @@ -572,6 +574,9 @@ "edt_conf_fbs_heading_title" : "Flatbuffers Server", "edt_conf_fbs_timeout_title" : "Zeitüberschreitung", "edt_conf_fbs_timeout_expl" : "Wenn für die angegebene Zeit keine Daten empfangen werden, wird die Komponente (vorübergehend) deaktiviert", + "edt_conf_pbs_heading_title" : "Protocol Buffers Server", + "edt_conf_pbs_timeout_title" : "Zeitüberschreitung", + "edt_conf_pbs_timeout_expl" : "Wenn für die angegebene Zeit keine Daten empfangen werden, wird die Komponente (vorübergehend) deaktiviert", "edt_conf_bobls_heading_title" : "Boblight Server", "edt_conf_udpl_heading_title" : "UDP Listener", "edt_conf_udpl_address_title" : "Adresse", diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 6dd08730..496c7e76 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -21,6 +21,7 @@ "general_comp_UDPLISTENER" : "UDP Listener", "general_comp_BOBLIGHTSERVER" : "Boblight Server", "general_comp_FLATBUFSERVER" : "Flatbuffers Server", + "general_comp_PROTOSERVER" : "Protocol Buffers Server", "general_comp_GRABBER" : "Platform Capture", "general_comp_V4L" : "USB Capture", "general_comp_LEDDEVICE" : "LED device", @@ -48,7 +49,7 @@ "dashboard_infobox_label_latesthyp" : "Latest Hyperion version:", "dashboard_infobox_label_platform" : "Platform:", "dashboard_infobox_label_instance" : "Instance:", - "dashboard_infobox_label_ports" : "Port flatbuf:", + "dashboard_infobox_label_ports" : "Ports (flat|proto):", "dashboard_infobox_message_updatewarning" : "A newer version of Hyperion is available! ($1)", "dashboard_infobox_message_updatesuccess" : "You run the latest version of Hyperion.", "dashboard_infobox_label_statush" : "Hyperion status:", @@ -169,6 +170,7 @@ "conf_network_bobl_intro" : "Receiver for Boblight", "conf_network_udpl_intro" : "Receiver for UDP", "conf_network_fbs_intro" : "Google Flatbuffers Receiver. Used for fast image transmission.", + "conf_network_proto_intro" : "The PROTO-Port of this Hyperion instance, used for picture streams (HyperionScreenCap, Kodi Addon, ...)", "conf_network_forw_intro" : "Forward all input to a second Hyperion instance which could be driven with another led controller", "conf_logging_label_intro" : "Area to check log messages, depending on loglevel setting you see more or less information.", "conf_logging_btn_pbupload" : "Upload report for support request", @@ -573,6 +575,9 @@ "edt_conf_fbs_heading_title" : "Flatbuffers Server", "edt_conf_fbs_timeout_title" : "Timeout", "edt_conf_fbs_timeout_expl" : "If no data are received for the given period, the component will be (soft) disabled.", + "edt_conf_pbs_heading_title" : "Protocol Buffers Server", + "edt_conf_pbs_timeout_title" : "Timeout", + "edt_conf_pbs_timeout_expl" : "If no data are received for the given period, the component will be (soft) disabled.", "edt_conf_bobls_heading_title" : "Boblight Server", "edt_conf_udpl_heading_title" : "UDP Listener", "edt_conf_udpl_address_title" : "Address", diff --git a/assets/webconfig/js/content_dashboard.js b/assets/webconfig/js/content_dashboard.js index b43ca531..949edab9 100644 --- a/assets/webconfig/js/content_dashboard.js +++ b/assets/webconfig/js/content_dashboard.js @@ -77,7 +77,7 @@ $(document).ready( function() { $('#dash_leddevice').html(serverInfo.ledDevices.active); $('#dash_currv').html(currentVersion); $('#dash_instance').html(serverConfig.general.name); - $('#dash_ports').html(serverConfig.flatbufServer.port); + $('#dash_ports').html(serverConfig.flatbufServer.port+' | '+serverConfig.protoServer.port); $.get( "https://raw.githubusercontent.com/hyperion-project/hyperion.ng/master/version.json", function( data ) { parsedUpdateJSON = JSON.parse(data); diff --git a/assets/webconfig/js/content_network.js b/assets/webconfig/js/content_network.js index 7c747121..3776e18b 100644 --- a/assets/webconfig/js/content_network.js +++ b/assets/webconfig/js/content_network.js @@ -21,6 +21,11 @@ $(document).ready( function() { $('#conf_cont_flatbuf').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fbs_heading_title"), 'editor_container_fbserver', 'btn_submit_fbserver')); $('#conf_cont_flatbuf').append(createHelpTable(schema.flatbufServer.properties, $.i18n("edt_conf_fbs_heading_title"))); + //protoserver + $('#conf_cont').append(createRow('conf_cont_proto')) + $('#conf_cont_proto').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_pbs_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver')); + $('#conf_cont_proto').append(createHelpTable(schema.protoServer.properties, $.i18n("edt_conf_pbs_heading_title"))); + //boblight $('#conf_cont').append(createRow('conf_cont_bobl')) $('#conf_cont_bobl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver')); @@ -44,6 +49,7 @@ $(document).ready( function() { $('#conf_cont').addClass('row'); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_js_heading_title"), 'editor_container_jsonserver', 'btn_submit_jsonserver')); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fbs_heading_title"), 'editor_container_fbserver', 'btn_submit_fbserver')); + $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_pbs_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver')); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver')); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_udpl_heading_title"), 'editor_container_udplistener', 'btn_submit_udplistener')); if(storedAccess != 'default') @@ -76,6 +82,19 @@ $(document).ready( function() { requestWriteConfig(conf_editor_fbs.getValue()); }); + //protobuffer + conf_editor_proto = createJsonEditor('editor_container_protoserver', { + protoServer : schema.protoServer + }, true, true); + + conf_editor_proto.on('change',function() { + conf_editor_proto.validate().length ? $('#btn_submit_protoserver').attr('disabled', true) : $('#btn_submit_protoserver').attr('disabled', false); + }); + + $('#btn_submit_protoserver').off().on('click',function() { + requestWriteConfig(conf_editor_proto.getValue()); + }); + //boblight conf_editor_bobl = createJsonEditor('editor_container_boblightserver', { boblightServer : schema.boblightServer @@ -123,6 +142,7 @@ $(document).ready( function() { { createHint("intro", $.i18n('conf_network_json_intro'), "editor_container_jsonserver"); createHint("intro", $.i18n('conf_network_fbs_intro'), "editor_container_fbserver"); + createHint("intro", $.i18n('conf_network_proto_intro'), "editor_container_protoserver"); createHint("intro", $.i18n('conf_network_bobl_intro'), "editor_container_boblightserver"); createHint("intro", $.i18n('conf_network_udpl_intro'), "editor_container_udplistener"); createHint("intro", $.i18n('conf_network_forw_intro'), "editor_container_forwarder"); diff --git a/assets/webconfig/js/content_remote.js b/assets/webconfig/js/content_remote.js index eab3dca6..ab341632 100644 --- a/assets/webconfig/js/content_remote.js +++ b/assets/webconfig/js/content_remote.js @@ -156,9 +156,12 @@ $(document).ready(function() { case "FLATBUFSERVER": owner = $.i18n('general_comp_FLATBUFSERVER'); break; + case "PROTOSERVER": + owner = $.i18n('general_comp_PROTOSERVER'); + break; } - if(duration && compId != "GRABBER" && compId != "PROTOSERVER") + if(duration && compId != "GRABBER" && compId != "FLATBUFSERVER" && compId != "PROTOSERVER") owner += '
'+$.i18n('remote_input_duration')+' '+duration.toFixed(0)+$.i18n('edt_append_s')+''; var btn = ''; diff --git a/config/hyperion.config.json.commented b/config/hyperion.config.json.commented index 422b7145..80b8d873 100644 --- a/config/hyperion.config.json.commented +++ b/config/hyperion.config.json.commented @@ -247,6 +247,15 @@ "timeout" : 5 }, + /// The configuration of the Protobuffer server which enables the Protobuffer remote interface + /// * port : Port at which the protobuffer server is started + "protoServer" : + { + "enable" : true, + "port" : 19445, + "timeout" : 5 + }, + /// The configuration of the boblight server which enables the boblight remote interface /// * enable : Enable or disable the boblight server (true/false) /// * port : Port at which the boblight server is started diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 866879e0..b8b9f394 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -140,6 +140,13 @@ "timeout" : 5 }, + "protoServer" : + { + "enable" : true, + "port" : 19445, + "timeout" : 5 + }, + "boblightServer" : { "enable" : false, diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 1ff24f50..66fcde99 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -9,6 +9,10 @@ if(ENABLE_WS281XPWM) external/rpi_ws281x/rpihw.c) endif() +#============================================================================= +# FLATBUFFER +#============================================================================= + set(USE_SYSTEM_FLATBUFFERS_LIBS ${DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS} CACHE BOOL "use flatbuffers library from system") if (USE_SYSTEM_FLATBUFFERS_LIBS) @@ -63,3 +67,111 @@ function(compile_flattbuffer_schema SRC_FBS OUTPUT_DIR) DEPENDS flatc) endif() endfunction() + +#============================================================================= +# PROTOBUFFER +#============================================================================= + +set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use protobuf library from system") + +if (USE_SYSTEM_PROTO_LIBS) + find_package(Protobuf REQUIRED) + include_directories(${PROTOBUF_INCLUDE_DIRS}) +else () + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared protobuf library") + add_subdirectory(external/protobuf) + + if(CMAKE_CROSSCOMPILING) + # when crosscompiling import the protoc executable targets from a file generated by a native build + option(IMPORT_PROTOC "Protoc export file (protoc_export.cmake) from a native build" "IMPORT_PROTOC-FILE_NOT_FOUND") + include(${IMPORT_PROTOC}) + else() + # export the protoc compiler so it can be used when cross compiling + export(TARGETS protoc_compiler FILE "${CMAKE_BINARY_DIR}/protoc_export.cmake") + endif() + + # define the include for the protobuf library at the parent scope + set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src") + set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS} PARENT_SCOPE) + + # define the protoc executable at the parent scope + get_property(PROTOBUF_PROTOC_EXECUTABLE TARGET protoc_compiler PROPERTY LOCATION) + set(PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE} PARENT_SCOPE) +endif() + +message(STATUS "Using protobuf compiler: " ${PROTOBUF_PROTOC_EXECUTABLE}) + +#============================================================================= +# Copyright 2009 Kitware, Inc. +# Copyright 2009-2011 Philip Lowman +# Copyright 2008 Esben Mose Hansen, Ange Optimization ApS +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) +function(PROTOBUF_GENERATE_CPP SRCS HDRS) + if(NOT ARGN) + message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files") + return() + endif() + + if(PROTOBUF_GENERATE_CPP_APPEND_PATH) + # Create an include path for each file specified + foreach(FIL ${ARGN}) + get_filename_component(ABS_FIL ${FIL} ABSOLUTE) + get_filename_component(ABS_PATH ${ABS_FIL} PATH) + list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) + if(${_contains_already} EQUAL -1) + list(APPEND _protobuf_include_path -I ${ABS_PATH}) + endif() + endforeach() + else() + set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + if(DEFINED PROTOBUF_IMPORT_DIRS) + foreach(DIR ${PROTOBUF_IMPORT_DIRS}) + get_filename_component(ABS_PATH ${DIR} ABSOLUTE) + list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) + if(${_contains_already} EQUAL -1) + list(APPEND _protobuf_include_path -I ${ABS_PATH}) + endif() + endforeach() + endif() + + if(CMAKE_CROSSCOMPILING OR USE_SYSTEM_PROTO_LIBS) + set(PROTOC_DEPENDENCY ${PROTOBUF_PROTOC_EXECUTABLE}) + else() + set(PROTOC_DEPENDENCY protoc_compiler) + endif() + + set(${SRCS}) + set(${HDRS}) + foreach(FIL ${ARGN}) + get_filename_component(ABS_FIL ${FIL} ABSOLUTE) + get_filename_component(FIL_WE ${FIL} NAME_WE) + + list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc") + list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h") + + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc" + "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h" + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL} + DEPENDS ${ABS_FIL} ${PROTOC_DEPENDENCY} + COMMENT "Running C++ protocol buffer compiler on ${FIL}" + VERBATIM + ) + endforeach() + + set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE) + set(${SRCS} ${${SRCS}} PARENT_SCOPE) + set(${HDRS} ${${HDRS}} PARENT_SCOPE) +endfunction() diff --git a/dependencies/external/protobuf b/dependencies/external/protobuf new file mode 160000 index 00000000..adce8a99 --- /dev/null +++ b/dependencies/external/protobuf @@ -0,0 +1 @@ +Subproject commit adce8a99fdab90f290d659b6b3bf2d09b721e24a diff --git a/include/hyperion/CaptureCont.h b/include/hyperion/CaptureCont.h index a5b5059e..3c7e866c 100644 --- a/include/hyperion/CaptureCont.h +++ b/include/hyperion/CaptureCont.h @@ -41,13 +41,13 @@ private slots: /// @brief forward system image /// @param image The image /// - void handleSystemImage(const Image& image); + void handleSystemImage(const QString& name, const Image& image); /// /// @brief forward v4l image /// @param image The image /// - void handleV4lImage(const Image & image); + void handleV4lImage(const QString& name, const Image & image); /// /// @brief Is called from _v4lInactiveTimer to set source after specific time to inactive @@ -66,10 +66,12 @@ private: /// Reflect state of System capture and prio bool _systemCaptEnabled; quint8 _systemCaptPrio; + QString _systemCaptName; QTimer* _systemInactiveTimer; /// Reflect state of v4l capture and prio bool _v4lCaptEnabled; quint8 _v4lCaptPrio; + QString _v4lCaptName; QTimer* _v4lInactiveTimer; }; diff --git a/include/hyperion/GrabberWrapper.h b/include/hyperion/GrabberWrapper.h index 57c92808..7c3bb53b 100644 --- a/include/hyperion/GrabberWrapper.h +++ b/include/hyperion/GrabberWrapper.h @@ -54,7 +54,7 @@ public: int ret = grabber.grabFrame(_image); if (ret >= 0) { - emit systemImage(_image); + emit systemImage(_grabberName, _image); return true; } return false; @@ -92,7 +92,7 @@ signals: /// /// @brief Emit the final processed image /// - void systemImage(const Image& image); + void systemImage(const QString& name, const Image& image); protected: diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 7440020b..e72dde98 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -411,7 +411,7 @@ signals: void forwardJsonMessage(QJsonObject); /// Signal which is emitted, when a new proto image should be forwarded - void forwardProtoMessage(Image); + void forwardProtoMessage(const QString, const Image); /// /// @brief Is emitted from clients who request a videoMode change diff --git a/include/hyperion/MessageForwarder.h b/include/hyperion/MessageForwarder.h index 58b3254f..4a37fb76 100644 --- a/include/hyperion/MessageForwarder.h +++ b/include/hyperion/MessageForwarder.h @@ -70,7 +70,7 @@ private slots: /// @brief Forward image to all proto slaves /// @param image The PROTO image to send /// - void forwardProtoMessage(const Image &image); + void forwardProtoMessage(const QString& name, const Image &image); /// /// @brief Forward message to a single json slave diff --git a/include/protoserver/ProtoServer.h b/include/protoserver/ProtoServer.h new file mode 100644 index 00000000..25cbd5a7 --- /dev/null +++ b/include/protoserver/ProtoServer.h @@ -0,0 +1,67 @@ +#pragma once + +// util +#include +#include + +// qt +#include + +class QTcpServer; +class ProtoClientConnection; + +/// +/// @brief This class creates a TCP server which accepts connections wich can then send +/// in Protocol Buffer encoded commands. This interface to Hyperion is used by various +/// third-party applications +/// +class ProtoServer : public QObject +{ + Q_OBJECT + +public: + ProtoServer(const QJsonDocument& config, QObject* parent = nullptr); + ~ProtoServer(); + +public slots: + /// + /// @brief Handle settings update + /// @param type The type from enum + /// @param config The configuration + /// + void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config); + + void initServer(); + +private slots: + /// + /// @brief Is called whenever a new socket wants to connect + /// + void newConnection(); + + /// + /// @brief is called whenever a client disconnected + /// + void clientDisconnected(); + +private: + /// + /// @brief Start the server with current _port + /// + void startServer(); + + /// + /// @brief Stop server + /// + void stopServer(); + + +private: + QTcpServer* _server; + Logger* _log; + int _timeout; + quint16 _port; + const QJsonDocument _config; + + QVector _openConnections; +}; diff --git a/include/udplistener/UDPListener.h b/include/udplistener/UDPListener.h index 83b50599..167ccb7d 100644 --- a/include/udplistener/UDPListener.h +++ b/include/udplistener/UDPListener.h @@ -84,7 +84,6 @@ private slots: void processTheDatagram(const QByteArray * datagram, const QHostAddress * sender); private: - /// The UDP server object QUdpSocket * _server; diff --git a/include/utils/Components.h b/include/utils/Components.h index ba2350b6..2222a9f7 100644 --- a/include/utils/Components.h +++ b/include/utils/Components.h @@ -22,7 +22,8 @@ enum Components COMP_IMAGE, COMP_EFFECT, COMP_LEDDEVICE, - COMP_FLATBUFSERVER + COMP_FLATBUFSERVER, + COMP_PROTOSERVER }; inline const char* componentToString(Components c) @@ -41,7 +42,8 @@ inline const char* componentToString(Components c) case COMP_EFFECT: return "Effect"; case COMP_IMAGE: return "Image"; case COMP_LEDDEVICE: return "LED device"; - case COMP_FLATBUFSERVER: return "Image Receiver"; + case COMP_FLATBUFSERVER: return "Image Receiver"; + case COMP_PROTOSERVER: return "Proto Server"; default: return ""; } } @@ -63,6 +65,7 @@ inline const char* componentToIdString(Components c) case COMP_IMAGE: return "IMAGE"; case COMP_LEDDEVICE: return "LEDDEVICE"; case COMP_FLATBUFSERVER: return "FLATBUFSERVER"; + case COMP_PROTOSERVER: return "PROTOSERVER"; default: return ""; } } @@ -83,6 +86,7 @@ inline Components stringToComponent(QString component) if (component == "IMAGE") return COMP_IMAGE; if (component == "LEDDEVICE") return COMP_LEDDEVICE; if (component == "FLATBUFSERVER") return COMP_FLATBUFSERVER; + if (component == "PROTOSERVER") return COMP_PROTOSERVER; return COMP_INVALID; } diff --git a/include/utils/GlobalSignals.h b/include/utils/GlobalSignals.h index e3fd8efd..f97302c8 100644 --- a/include/utils/GlobalSignals.h +++ b/include/utils/GlobalSignals.h @@ -29,13 +29,15 @@ public: signals: /// /// @brief PIPE SystemCapture images from GrabberWrapper to Hyperion class + /// @param name The name of the platform capture that is currently active /// @param image The prepared image /// - void setSystemImage(const Image& image); + void setSystemImage(const QString& name, const Image& image); /// /// @brief PIPE v4lCapture images from v4lCapture over HyperionDaemon to Hyperion class + /// @param name The name of the v4l capture (path) that is currently active /// @param image The prepared image /// - void setV4lImage(const Image & image); + void setV4lImage(const QString& name, const Image & image); }; diff --git a/include/utils/settings.h b/include/utils/settings.h index 607a3719..ef96fa1d 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -29,6 +29,7 @@ enum type { INSTCAPTURE, NETWORK, FLATBUFSERVER, + PROTOSERVER, INVALID }; @@ -62,6 +63,7 @@ inline QString typeToString(const type& type) case INSTCAPTURE: return "instCapture"; case NETWORK: return "network"; case FLATBUFSERVER: return "flatbufServer"; + case PROTOSERVER: return "protoServer"; default: return "invalid"; } } @@ -73,7 +75,7 @@ inline QString typeToString(const type& type) /// inline type stringToType(const QString& type) { - if (type == "backgroundEffect") return BGEFFECT; + if (type == "backgroundEffect") return BGEFFECT; else if (type == "foregroundEffect") return FGEFFECT; else if (type == "blackborderdetector") return BLACKBORDER; else if (type == "boblightServer") return BOBLSERVER; @@ -94,6 +96,7 @@ inline type stringToType(const QString& type) else if (type == "instCapture") return INSTCAPTURE; else if (type == "network") return NETWORK; else if (type == "flatbufServer") return FLATBUFSERVER; - else return INVALID; + else if (type == "protoServer") return PROTOSERVER; + else return INVALID; } }; diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index 4ff02b3b..f858b74d 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(commandline) add_subdirectory(blackborder) add_subdirectory(jsonserver) add_subdirectory(flatbufserver) +add_subdirectory(protoserver) add_subdirectory(bonjour) add_subdirectory(ssdp) add_subdirectory(boblightserver) diff --git a/libsrc/flatbufserver/FlatBufferClient.h b/libsrc/flatbufserver/FlatBufferClient.h index cc934b32..9fe6f36e 100644 --- a/libsrc/flatbufserver/FlatBufferClient.h +++ b/libsrc/flatbufserver/FlatBufferClient.h @@ -31,7 +31,7 @@ public: /// @param timeout The timeout when a client is automatically disconnected and the priority unregistered /// @param parent The parent /// - explicit FlatBufferClient(QTcpSocket* socket, const int &timeout, QObject *parent = nullptr); + explicit FlatBufferClient(QTcpSocket* socket, const int &timeout, QObject *parent = nullptr); signals: /// @@ -59,12 +59,12 @@ private slots: /// /// @brief Is called whenever the socket got new data to read /// - void readyRead(); + void readyRead(); /// /// @brief Is called when the socket closed the connection, also requests thread exit /// - void disconnected(); + void disconnected(); private: /// diff --git a/libsrc/flatbufserver/FlatBufferServer.cpp b/libsrc/flatbufserver/FlatBufferServer.cpp index ab87eb3e..0dc953fe 100644 --- a/libsrc/flatbufserver/FlatBufferServer.cpp +++ b/libsrc/flatbufserver/FlatBufferServer.cpp @@ -62,8 +62,6 @@ void FlatBufferServer::newConnection() FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this); // internal connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected); - // forward data - //connect(clientThread, &FlatBufferClient::); _openConnections.append(client); } } diff --git a/libsrc/grabber/v4l2/V4L2Wrapper.cpp b/libsrc/grabber/v4l2/V4L2Wrapper.cpp index 987b016b..320dccd9 100644 --- a/libsrc/grabber/v4l2/V4L2Wrapper.cpp +++ b/libsrc/grabber/v4l2/V4L2Wrapper.cpp @@ -53,7 +53,7 @@ void V4L2Wrapper::setSignalDetectionOffset(double verticalMin, double horizontal void V4L2Wrapper::newFrame(const Image &image) { - emit systemImage(image); + emit systemImage(_grabberName, image); } void V4L2Wrapper::readError(const char* err) diff --git a/libsrc/hyperion/CaptureCont.cpp b/libsrc/hyperion/CaptureCont.cpp index 12c5cd32..0eb75ccc 100644 --- a/libsrc/hyperion/CaptureCont.cpp +++ b/libsrc/hyperion/CaptureCont.cpp @@ -13,8 +13,10 @@ CaptureCont::CaptureCont(Hyperion* hyperion) : QObject() , _hyperion(hyperion) , _systemCaptEnabled(false) + , _systemCaptName() , _systemInactiveTimer(new QTimer(this)) , _v4lCaptEnabled(false) + , _v4lCaptName() , _v4lInactiveTimer(new QTimer(this)) { // settings changes @@ -41,14 +43,24 @@ CaptureCont::~CaptureCont() { } -void CaptureCont::handleV4lImage(const Image & image) +void CaptureCont::handleV4lImage(const QString& name, const Image & image) { + if(_v4lCaptName != name) + { + _hyperion->registerInput(_v4lCaptPrio, hyperion::COMP_V4L, "System", name); + _v4lCaptName = name; + } _v4lInactiveTimer->start(); _hyperion->setInputImage(_v4lCaptPrio, image); } -void CaptureCont::handleSystemImage(const Image& image) +void CaptureCont::handleSystemImage(const QString& name, const Image& image) { + if(_systemCaptName != name) + { + _hyperion->registerInput(_systemCaptPrio, hyperion::COMP_GRABBER, "System", name); + _systemCaptName = name; + } _systemInactiveTimer->start(); _hyperion->setInputImage(_systemCaptPrio, image); } diff --git a/libsrc/hyperion/MessageForwarder.cpp b/libsrc/hyperion/MessageForwarder.cpp index a8fc4594..ef1423f7 100644 --- a/libsrc/hyperion/MessageForwarder.cpp +++ b/libsrc/hyperion/MessageForwarder.cpp @@ -210,7 +210,7 @@ void MessageForwarder::forwardJsonMessage(const QJsonObject &message) } } -void MessageForwarder::forwardProtoMessage(const Image &image) +void MessageForwarder::forwardProtoMessage(const QString& name, const Image &image) { if (_forwarder_enabled) { diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index 8450773f..ca911742 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -55,6 +55,10 @@ { "$ref": "schema-flatbufServer.json" }, + "protoServer" : + { + "$ref": "schema-protoServer.json" + }, "boblightServer" : { "$ref": "schema-boblightServer.json" diff --git a/libsrc/hyperion/resource.qrc b/libsrc/hyperion/resource.qrc index 2d8c1c36..575c2d48 100644 --- a/libsrc/hyperion/resource.qrc +++ b/libsrc/hyperion/resource.qrc @@ -15,6 +15,7 @@ schema/schema-forwarder.json schema/schema-jsonServer.json schema/schema-flatbufServer.json + schema/schema-protoServer.json schema/schema-boblightServer.json schema/schema-udpListener.json schema/schema-webConfig.json diff --git a/libsrc/hyperion/schema/schema-protoServer.json b/libsrc/hyperion/schema/schema-protoServer.json new file mode 100644 index 00000000..6a70e3f9 --- /dev/null +++ b/libsrc/hyperion/schema/schema-protoServer.json @@ -0,0 +1,36 @@ +{ + "type" : "object", + "required" : true, + "title" : "edt_conf_pbs_heading_title", + "properties" : + { + "enable" : + { + "type" : "boolean", + "required" : true, + "title" : "edt_conf_general_enable_title", + "default" : true, + "propertyOrder" : 1 + }, + "port" : + { + "type" : "integer", + "required" : true, + "title" : "edt_conf_general_port_title", + "minimum" : 1024, + "maximum" : 65535, + "default" : 19445 + }, + "timeout" : + { + "type" : "integer", + "required" : true, + "title" : "edt_conf_pbs_timeout_title", + "append" : "edt_append_s", + "minimum" : 1, + "default" : 5, + "propertyOrder" : 3 + } + }, + "additionalProperties" : false +} diff --git a/libsrc/protoserver/CMakeLists.txt b/libsrc/protoserver/CMakeLists.txt new file mode 100644 index 00000000..541d6045 --- /dev/null +++ b/libsrc/protoserver/CMakeLists.txt @@ -0,0 +1,44 @@ + +# Define the current source locations +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${PROTOBUF_INCLUDE_DIRS} +) + +set(ProtoServer_PROTOS ${CURRENT_SOURCE_DIR}/message.proto ) + +protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS ${ProtoServer_PROTOS} ) + +### Split protoclient from protoserver as protoserver relates to HyperionDaemon and standalone capture binarys can't link to it + +add_library(protoclient + ${CURRENT_SOURCE_DIR}/ProtoClientConnection.h + ${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp + ${ProtoServer_PROTO_SRCS} + ${ProtoServer_PROTO_HDRS} +) + +add_library(protoserver + ${CURRENT_HEADER_DIR}/ProtoServer.h + ${CURRENT_SOURCE_DIR}/ProtoServer.cpp +) + +# disable warnings for auto generated proto files, we can't change the files .... +SET_SOURCE_FILES_PROPERTIES ( ${ProtoServer_PROTO_SRCS} ${ProtoServer_PROTO_HDRS} ${ProtoServer_PROTOS} PROPERTIES COMPILE_FLAGS -w ) + +target_link_libraries(protoclient + hyperion + hyperion-utils + protobuf + Qt5::Gui +) + +target_link_libraries(protoserver + hyperion + hyperion-utils + protoclient + Qt5::Gui +) diff --git a/libsrc/protoserver/ProtoClientConnection.cpp b/libsrc/protoserver/ProtoClientConnection.cpp new file mode 100644 index 00000000..5f67a0be --- /dev/null +++ b/libsrc/protoserver/ProtoClientConnection.cpp @@ -0,0 +1,238 @@ +// project includes +#include "ProtoClientConnection.h" + +// qt +#include +#include +#include +#include + +// Hyperion includes +#include + + + +ProtoClientConnection::ProtoClientConnection(QTcpSocket* socket, const int &timeout, QObject *parent) + : QObject(parent) + , _log(Logger::getInstance("PROTOSERVER")) + , _socket(socket) + , _clientAddress(socket->peerAddress().toString()) + , _timeoutTimer(new QTimer(this)) + , _timeout(timeout * 1000) + , _priority() + , _hyperion(Hyperion::getInstance()) +{ + // timer setup + _timeoutTimer->setSingleShot(true); + _timeoutTimer->setInterval(_timeout); + connect(_timeoutTimer, &QTimer::timeout, this, &ProtoClientConnection::forceClose); + + // connect socket signals + connect(_socket, &QTcpSocket::readyRead, this, &ProtoClientConnection::readyRead); + connect(_socket, &QTcpSocket::disconnected, this, &ProtoClientConnection::disconnected); +} + +void ProtoClientConnection::readyRead() +{ + _receiveBuffer += _socket->readAll(); + + // check if we can read a message size + if (_receiveBuffer.size() <= 4) + { + return; + } + + // read the message size + uint32_t messageSize = + ((_receiveBuffer[0]<<24) & 0xFF000000) | + ((_receiveBuffer[1]<<16) & 0x00FF0000) | + ((_receiveBuffer[2]<< 8) & 0x0000FF00) | + ((_receiveBuffer[3] ) & 0x000000FF); + + // check if we can read a complete message + if ((uint32_t) _receiveBuffer.size() < messageSize + 4) + { + return; + } + + // read a message + proto::HyperionRequest message; + if (!message.ParseFromArray(_receiveBuffer.data() + 4, messageSize)) + { + sendErrorReply("Unable to parse message"); + } + + // handle the message + handleMessage(message); + + // remove message data from buffer + _receiveBuffer = _receiveBuffer.mid(messageSize + 4); +} + +void ProtoClientConnection::forceClose() +{ + _socket->close(); +} + +void ProtoClientConnection::disconnected() +{ + Debug(_log, "Socket Closed"); + _socket->deleteLater(); + _hyperion->clear(_priority); + emit clientDisconnected(); +} + +void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message) +{ + switch (message.command()) + { + case proto::HyperionRequest::COLOR: + if (!message.HasExtension(proto::ColorRequest::colorRequest)) + { + sendErrorReply("Received COLOR command without ColorRequest"); + break; + } + handleColorCommand(message.GetExtension(proto::ColorRequest::colorRequest)); + break; + case proto::HyperionRequest::IMAGE: + if (!message.HasExtension(proto::ImageRequest::imageRequest)) + { + sendErrorReply("Received IMAGE command without ImageRequest"); + break; + } + handleImageCommand(message.GetExtension(proto::ImageRequest::imageRequest)); + break; + case proto::HyperionRequest::CLEAR: + if (!message.HasExtension(proto::ClearRequest::clearRequest)) + { + sendErrorReply("Received CLEAR command without ClearRequest"); + break; + } + handleClearCommand(message.GetExtension(proto::ClearRequest::clearRequest)); + break; + case proto::HyperionRequest::CLEARALL: + handleClearallCommand(); + break; + default: + handleNotImplemented(); + } +} + +void ProtoClientConnection::handleColorCommand(const proto::ColorRequest &message) +{ + // extract parameters + int priority = message.priority(); + int duration = message.has_duration() ? message.duration() : -1; + ColorRgb color; + color.red = qRed(message.rgbcolor()); + color.green = qGreen(message.rgbcolor()); + color.blue = qBlue(message.rgbcolor()); + + // make sure the prio is registered before setColor() + if(priority != _priority) + { + _hyperion->clear(_priority); + _hyperion->registerInput(priority, hyperion::COMP_PROTOSERVER, "Proto@"+_clientAddress); + _priority = priority; + } + + // set output + _hyperion->setColor(_priority, color, duration); + + // send reply + sendSuccessReply(); +} + +void ProtoClientConnection::handleImageCommand(const proto::ImageRequest &message) +{ + // extract parameters + int priority = message.priority(); + int duration = message.has_duration() ? message.duration() : -1; + int width = message.imagewidth(); + int height = message.imageheight(); + const std::string & imageData = message.imagedata(); + + // make sure the prio is registered before setInput() + if(priority != _priority) + { + _hyperion->clear(_priority); + _hyperion->registerInput(priority, hyperion::COMP_PROTOSERVER, "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.c_str(), imageData.size()); + + _hyperion->setInputImage(_priority, image, duration); + + // send reply + sendSuccessReply(); +} + + +void ProtoClientConnection::handleClearCommand(const proto::ClearRequest &message) +{ + // extract parameters + int priority = message.priority(); + + // clear priority + _hyperion->clear(priority); + // send reply + sendSuccessReply(); +} + +void ProtoClientConnection::handleClearallCommand() +{ + // clear priority + _hyperion->clearall(); + + // send reply + sendSuccessReply(); +} + + +void ProtoClientConnection::handleNotImplemented() +{ + sendErrorReply("Command not implemented"); +} + +void ProtoClientConnection::sendMessage(const google::protobuf::Message &message) +{ + std::string serializedReply = message.SerializeAsString(); + uint32_t size = serializedReply.size(); + uint8_t sizeData[] = {uint8_t(size >> 24), uint8_t(size >> 16), uint8_t(size >> 8), uint8_t(size)}; + _socket->write((const char *) sizeData, sizeof(sizeData)); + _socket->write(serializedReply.data(), serializedReply.length()); + _socket->flush(); +} + +void ProtoClientConnection::sendSuccessReply() +{ + // create reply + proto::HyperionReply reply; + reply.set_type(proto::HyperionReply::REPLY); + reply.set_success(true); + + // send reply + sendMessage(reply); +} + +void ProtoClientConnection::sendErrorReply(const std::string &error) +{ + // create reply + proto::HyperionReply reply; + reply.set_type(proto::HyperionReply::REPLY); + reply.set_success(false); + reply.set_error(error); + + // send reply + sendMessage(reply); +} diff --git a/libsrc/protoserver/ProtoClientConnection.h b/libsrc/protoserver/ProtoClientConnection.h new file mode 100644 index 00000000..d57c2969 --- /dev/null +++ b/libsrc/protoserver/ProtoClientConnection.h @@ -0,0 +1,135 @@ +#pragma once + +// util +#include +#include +#include +#include + +// protobuffer PROTO +#include "message.pb.h" + +class QTcpSocket; +class QTimer; +class Hyperion; + +namespace proto { +class HyperionRequest; +} + +/// +/// The Connection object created by a ProtoServer when a new connection is established +/// +class ProtoClientConnection : public QObject +{ + Q_OBJECT + +public: + /// + /// @brief Construct the client + /// @param socket The socket + /// @param timeout The timeout when a client is automatically disconnected and the priority unregistered + /// @param parent The parent + /// + explicit ProtoClientConnection(QTcpSocket* socket, const int &timeout, QObject *parent); + +signals: + /// + /// @brief Emits whenever the client disconnected + /// + void clientDisconnected(); + +public slots: + /// + /// @brief close the socket and call disconnected() + /// + void forceClose(); + +private slots: + /// + /// @brief Is called whenever the socket got new data to read + /// + void readyRead(); + + /// + /// @brief Is called when the socket closed the connection, also requests thread exit + /// + void disconnected(); + +private: + /// + /// Handle an incoming Proto message + /// + /// @param message the incoming message as string + /// + void handleMessage(const proto::HyperionRequest &message); + + /// + /// Handle an incoming Proto Color message + /// + /// @param message the incoming message + /// + void handleColorCommand(const proto::ColorRequest & message); + + /// + /// Handle an incoming Proto Image message + /// + /// @param message the incoming message + /// + void handleImageCommand(const proto::ImageRequest & message); + + /// + /// Handle an incoming Proto Clear message + /// + /// @param message the incoming message + /// + void handleClearCommand(const proto::ClearRequest & message); + + /// + /// Handle an incoming Proto Clearall message + /// + void handleClearallCommand(); + + /// + /// Handle an incoming Proto message of unknown type + /// + void handleNotImplemented(); + + /// + /// Send a message to the connected client + /// + /// @param message The Proto message to send + /// + void sendMessage(const google::protobuf::Message &message); + + /// + /// Send a standard reply indicating success + /// + void sendSuccessReply(); + + /// + /// Send an error message back to the client + /// + /// @param error String describing the error + /// + void sendErrorReply(const std::string & error); + +private: + Logger*_log; + + /// The TCP-Socket that is connected tot the Proto-client + QTcpSocket* _socket; + + /// address of client + const QString _clientAddress; + + QTimer*_timeoutTimer; + int _timeout; + int _priority; + + /// Link to Hyperion for writing led-values to a priority channel + Hyperion* _hyperion; + + /// The buffer used for reading data from the socket + QByteArray _receiveBuffer; +}; diff --git a/libsrc/protoserver/ProtoServer.cpp b/libsrc/protoserver/ProtoServer.cpp new file mode 100644 index 00000000..6c2d8152 --- /dev/null +++ b/libsrc/protoserver/ProtoServer.cpp @@ -0,0 +1,104 @@ +#include +#include "ProtoClientConnection.h" + +// qt +#include +#include +#include + +ProtoServer::ProtoServer(const QJsonDocument& config, QObject* parent) + : QObject(parent) + , _server(new QTcpServer(this)) + , _log(Logger::getInstance("PROTOSERVER")) + , _timeout(5000) + , _config(config) +{ + +} + +ProtoServer::~ProtoServer() +{ + stopServer(); + delete _server; +} + +void ProtoServer::initServer() +{ + connect(_server, &QTcpServer::newConnection, this, &ProtoServer::newConnection); + + // apply config + handleSettingsUpdate(settings::PROTOSERVER, _config); +} + +void ProtoServer::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config) +{ + if(type == settings::PROTOSERVER) + { + const QJsonObject& obj = config.object(); + + quint16 port = obj["port"].toInt(19445); + + // port check + if(_server->serverPort() != port) + { + stopServer(); + _port = port; + } + + // new timeout just for new connections + _timeout = obj["timeout"].toInt(5000); + // enable check + obj["enable"].toBool(true) ? startServer() : stopServer(); + } +} + +void ProtoServer::newConnection() +{ + while(_server->hasPendingConnections()) + { + if(QTcpSocket * socket = _server->nextPendingConnection()) + { + Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); + ProtoClientConnection * client = new ProtoClientConnection(socket, _timeout, this); + // internal + connect(client, &ProtoClientConnection::clientDisconnected, this, &ProtoServer::clientDisconnected); + _openConnections.append(client); + } + } +} + +void ProtoServer::clientDisconnected() +{ + ProtoClientConnection* client = qobject_cast(sender()); + client->deleteLater(); + _openConnections.removeAll(client); +} + +void ProtoServer::startServer() +{ + if(!_server->isListening()) + { + if(!_server->listen(QHostAddress::Any, _port)) + { + Error(_log,"Failed to bind port %d", _port); + } + else + { + Info(_log,"Started on port %d", _port); + } + } +} + +void ProtoServer::stopServer() +{ + if(_server->isListening()) + { + // close client connections + for(const auto& client : _openConnections) + { + client->forceClose(); + } + _server->close(); + Info(_log, "Stopped"); + } +} diff --git a/libsrc/protoserver/message.proto b/libsrc/protoserver/message.proto new file mode 100644 index 00000000..9652453e --- /dev/null +++ b/libsrc/protoserver/message.proto @@ -0,0 +1,80 @@ +package proto; + +message HyperionRequest { + enum Command { + COLOR = 1; + IMAGE = 2; + CLEAR = 3; + CLEARALL = 4; + } + + // command specification + required Command command = 1; + + // extensions to define all specific requests + extensions 10 to 100; +} + +message ColorRequest { + extend HyperionRequest { + optional ColorRequest colorRequest = 10; + } + + // priority to use when setting the color + required int32 priority = 1; + + // integer value containing the rgb color (0x00RRGGBB) + required int32 RgbColor = 2; + + // duration of the request (negative results in infinite) + optional int32 duration = 3; +} + +message ImageRequest { + extend HyperionRequest { + optional ImageRequest imageRequest = 11; + } + + // priority to use when setting the image + required int32 priority = 1; + + // width of the image + required int32 imagewidth = 2; + + // height of the image + required int32 imageheight = 3; + + // image data + required bytes imagedata = 4; + + // duration of the request (negative results in infinite) + optional int32 duration = 5; +} + +message ClearRequest { + extend HyperionRequest { + optional ClearRequest clearRequest = 12; + } + + // priority which need to be cleared + required int32 priority = 1; +} + +message HyperionReply { + enum Type { + REPLY = 1; + VIDEO = 2; + } + + // Identifies which field is filled in. + required Type type = 1; + + // flag indication success or failure + optional bool success = 2; + + // string indicating the reason for failure (if applicable) + optional string error = 3; + + // Proto Messages for video mode + optional int32 video = 4; +} diff --git a/libsrc/udplistener/UDPListener.cpp b/libsrc/udplistener/UDPListener.cpp index 60c4e3cb..678735f4 100644 --- a/libsrc/udplistener/UDPListener.cpp +++ b/libsrc/udplistener/UDPListener.cpp @@ -5,6 +5,7 @@ #include // hyperion includes +#include #include "HyperionConfig.h" // qt includes @@ -22,6 +23,9 @@ UDPListener::UDPListener(const QJsonDocument& config) : _isActive(false), _listenPort(0) { + // listen for component change + connect(Hyperion::getInstance(), &Hyperion::componentStateChanged, this, &UDPListener::componentStateChanged); + // init handleSettingsUpdate(settings::UDPLISTENER, config); } @@ -70,6 +74,8 @@ void UDPListener::start() _serviceRegister->registerService("_hyperiond-udp._udp", _listenPort); } } + + Hyperion::getInstance()->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive); } void UDPListener::stop() @@ -80,7 +86,7 @@ void UDPListener::stop() _server->close(); _isActive = false; Info(_log, "Stopped"); -// emit clearGlobalPriority(_priority, hyperion::COMP_UDPLISTENER); + Hyperion::getInstance()->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive); } void UDPListener::componentStateChanged(const hyperion::Components component, bool enable) diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index bcce3312..ebaf09b0 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -17,6 +17,7 @@ target_link_libraries(hyperiond jsonserver udplistener flatbufserver + protoserver webserver bonjour ssdp diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 7cbf64ab..769d17d5 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -26,9 +26,12 @@ #include // Required to determine the cmake options #include "hyperiond.h" -// FlatBufferServer +// Flatbuffer Server #include +// Protobuffer Server +#include + // bonjour browser #include @@ -145,6 +148,8 @@ void HyperionDaemon::freeObjects() delete _jsonServer; _flatBufferServer->thread()->quit(); _flatBufferServer->thread()->wait(1000); + _protoServer->thread()->quit(); + _protoServer->thread()->wait(1000); //ssdp before webserver _ssdp->thread()->quit(); _ssdp->thread()->wait(1000); @@ -166,17 +171,18 @@ void HyperionDaemon::freeObjects() _v4l2Grabbers.clear(); _bonjourBrowserWrapper = nullptr; - _amlGrabber = nullptr; - _dispmanx = nullptr; - _fbGrabber = nullptr; - _osxGrabber = nullptr; - _qtGrabber = nullptr; - _flatBufferServer = nullptr; - _ssdp = nullptr; - _webserver = nullptr; - _jsonServer = nullptr; - _udpListener = nullptr; - _stats = nullptr; + _amlGrabber = nullptr; + _dispmanx = nullptr; + _fbGrabber = nullptr; + _osxGrabber = nullptr; + _qtGrabber = nullptr; + _flatBufferServer = nullptr; + _protoServer = nullptr; + _ssdp = nullptr; + _webserver = nullptr; + _jsonServer = nullptr; + _udpListener = nullptr; + _stats = nullptr; } void HyperionDaemon::startNetworkServices() @@ -198,6 +204,16 @@ void HyperionDaemon::startNetworkServices() connect(this, &HyperionDaemon::settingsChanged, _flatBufferServer, &FlatBufferServer::handleSettingsUpdate); fbThread->start(); + // Create Proto server in thread + _protoServer = new ProtoServer(getSetting(settings::PROTOSERVER)); + QThread* pThread = new QThread(this); + _protoServer->moveToThread(pThread); + connect( pThread, &QThread::started, _protoServer, &ProtoServer::initServer ); + connect( pThread, &QThread::finished, _protoServer, &QObject::deleteLater ); + connect( pThread, &QThread::finished, pThread, &QObject::deleteLater ); + connect(this, &HyperionDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate); + pThread->start(); + // Create UDP listener _udpListener = new UDPListener(getSetting(settings::UDPLISTENER)); connect(this, &HyperionDaemon::settingsChanged, _udpListener, &UDPListener::handleSettingsUpdate); diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index 3fb18916..1aaee749 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -63,6 +63,7 @@ class SettingsManager; class PythonInit; class SSDPHandler; class FlatBufferServer; +class ProtoServer; class HyperionDaemon : public QObject { @@ -130,33 +131,34 @@ private: void createGrabberX11(const QJsonObject & grabberConfig); void createGrabberQt(const QJsonObject & grabberConfig); - Logger* _log; - BonjourBrowserWrapper* _bonjourBrowserWrapper; - PythonInit* _pyInit; - WebServer* _webserver; - JsonServer* _jsonServer; - UDPListener* _udpListener; + Logger* _log; + BonjourBrowserWrapper* _bonjourBrowserWrapper; + PythonInit* _pyInit; + WebServer* _webserver; + JsonServer* _jsonServer; + UDPListener* _udpListener; std::vector _v4l2Grabbers; - DispmanxWrapper* _dispmanx; - X11Wrapper* _x11Grabber; - AmlogicWrapper* _amlGrabber; - FramebufferWrapper* _fbGrabber; - OsxWrapper* _osxGrabber; - QtWrapper* _qtGrabber; - Hyperion* _hyperion; - Stats* _stats; - SSDPHandler* _ssdp; - FlatBufferServer* _flatBufferServer; + DispmanxWrapper* _dispmanx; + X11Wrapper* _x11Grabber; + AmlogicWrapper* _amlGrabber; + FramebufferWrapper* _fbGrabber; + OsxWrapper* _osxGrabber; + QtWrapper* _qtGrabber; + Hyperion* _hyperion; + Stats* _stats; + SSDPHandler* _ssdp; + FlatBufferServer* _flatBufferServer; + ProtoServer* _protoServer; - unsigned _grabber_width; - unsigned _grabber_height; - unsigned _grabber_frequency; - unsigned _grabber_cropLeft; - unsigned _grabber_cropRight; - unsigned _grabber_cropTop; - unsigned _grabber_cropBottom; - int _grabber_ge2d_mode; - QString _grabber_device; + unsigned _grabber_width; + unsigned _grabber_height; + unsigned _grabber_frequency; + unsigned _grabber_cropLeft; + unsigned _grabber_cropRight; + unsigned _grabber_cropTop; + unsigned _grabber_cropBottom; + int _grabber_ge2d_mode; + QString _grabber_device; QString _prevType; From 1d0171a5f75202d3d09fe9ac5aa73e7e90895b95 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Sun, 17 Feb 2019 18:56:04 +0100 Subject: [PATCH 04/74] travis and docker improvements --- .travis.yml | 63 +++++++++++++++++++++++++---------- .travis/travis_build.sh | 11 +++--- CMakeLists.txt | 1 + CompileHowto.md | 16 ++++++--- bin/scripts/docker-compile.sh | 12 +++---- 5 files changed, 71 insertions(+), 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index e85acbb1..06d1a25d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,11 @@ +linux: &linux + os: linux + dist: trusty + services: + - docker +osx: &osx + os: osx + cache: - ccache - directories: @@ -5,27 +13,46 @@ cache: notifications: email: false language: cpp -services: - - docker -matrix: - include: - - os: linux - dist: trusty - env: - - DOCKER_TAG=ubuntu1604 - - DOCKER_NAME="Ubuntu 16.04" - - os: linux - dist: trusty - env: - - DOCKER_TAG=cross-qemu-rpistretch - - DOCKER_NAME="Raspberry Pi" - - os: osx - osx_image: xcode8.3 - env: - - HOMEBREW_CACHE=$HOME/brew-cache + before_install: - ./.travis/travis_install.sh + +jobs: + include: + - <<: *linux + name: "AMD64 (x64)" + env: + - DOCKER_TAG=amd64 + - DOCKER_NAME="Debian Stretch (AMD64)" + - <<: *linux + name: "i386 (x86)" + env: + - DOCKER_TAG=i386 + - DOCKER_NAME="Debian Stretch (i386)" + - <<: *linux + name: "ARMv6hf (Raspberry Pi v1 & ZERO)" + env: + - DOCKER_TAG=armv6hf + - DOCKER_NAME=""Debian Stretch (Raspberry Pi v1 & ZERO)" + - <<: *linux + name: "ARMv7hf (Raspberry Pi 2 & 3)" + env: + - DOCKER_TAG=armv7hf + - DOCKER_NAME="Debian Stretch (Raspberry Pi 2 & 3)" + - <<: *linux + name: "ARMv8 (Generic AARCH64)" + env: + - DOCKER_TAG=aarch64 + - DOCKER_NAME="ARMv8 (Generic AARCH64)" + - PLATFORM="amlogic" + - <<: *osx + osx_image: xcode8.3 + name: "macOS 10.12 (Xcode 8.3.3)" + env: + - HOMEBREW_CACHE=$HOME/brew-cache + script: - ./.travis/travis_build.sh after_success: - ./.travis/travis_deploy.sh + diff --git a/.travis/travis_build.sh b/.travis/travis_build.sh index ddeb569c..7b39b037 100755 --- a/.travis/travis_build.sh +++ b/.travis/travis_build.sh @@ -3,7 +3,9 @@ # for executing in non travis environment [ -z "$TRAVIS_OS_NAME" ] && TRAVIS_OS_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" -PLATFORM=x86 +if [ -z "${PLATFORM}" ]; then + PLATFORM=x86 +fi BUILD_TYPE=Debug PACKAGES="" @@ -23,10 +25,11 @@ echo "compile jobs: ${JOBS:=4}" [ -n "${TRAVIS_TAG:-}" ] && BUILD_TYPE=Release # Determine package creation; True for cron and tag builds +# Commented because tests are currently broken [ "${TRAVIS_EVENT_TYPE:-}" == 'cron' ] || [ -n "${TRAVIS_TAG:-}" ] && PACKAGES=package # Determie -dev appends to platform; -[ "${TRAVIS_EVENT_TYPE:-}" != 'cron' -a -z "${TRAVIS_TAG:-}" ] && PLATFORM=${PLATFORM}-dev +# [ "${TRAVIS_EVENT_TYPE:-}" != 'cron' -a -z "${TRAVIS_TAG:-}" ] && PLATFORM=${PLATFORM}-dev # Build the package on osx if [[ "$TRAVIS_OS_NAME" == 'osx' || "$TRAVIS_OS_NAME" == 'darwin' ]] @@ -48,10 +51,10 @@ then docker run --rm \ -v "${TRAVIS_BUILD_DIR}/deploy:/deploy" \ -v "${TRAVIS_BUILD_DIR}:/source:ro" \ - hyperionorg/hyperion-ci:$DOCKER_TAG \ + hyperionproject/hyperion-ci:$DOCKER_TAG \ /bin/bash -c "mkdir build && cp -r /source/. /build && cd /build && mkdir build && cd build && - cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. || exit 2 && + cmake -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. || exit 2 && make -j $(nproc) ${PACKAGES} || exit 3 && echo '---> Copy binaries and packages to host folder: ${TRAVIS_BUILD_DIR}/deploy' && cp -v /build/build/bin/h* /deploy/ 2>/dev/null || : && diff --git a/CMakeLists.txt b/CMakeLists.txt index a8b24eca..18d7cb66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,6 +237,7 @@ CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") if (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-psabi") endif() if(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") diff --git a/CompileHowto.md b/CompileHowto.md index 205f9a47..2da17bce 100644 --- a/CompileHowto.md +++ b/CompileHowto.md @@ -1,13 +1,21 @@ # With Docker -If you are using [Docker](https://www.docker.com/), you can compile Hyperion inside a docker container. This keeps your system clean and with a simple script it's easy to use. Supported is also cross compilation for Raspberry Pi (Raspbian stretch) +If you are using [Docker](https://www.docker.com/), you can compile Hyperion inside a docker container. This keeps your system clean and with a simple script it's easy to use. Supported is also cross compilation for Raspberry Pi (Debian Stretch) -To compile Hyperion for Ubuntu 16.04 (x64) or higher just execute the following command +To compile Hyperion for Debain Stretch (x64 architecture) or higher just execute the following command ``` wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh ``` -To compile Hyperion for Raspberry Pi +To compile Hyperion for i386 architecture ``` -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -t cross-qemu-rpistretch +wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -t i386 +``` +To compile Hyperion for Raspberry Pi v1 & ZERO +``` +wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -t armv6hf +``` +To compile Hyperion for Raspberry Pi 2 & 3 +``` +wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -t armv7hf ``` The compiled binaries and packages will be available at the deploy folder next to the script Note: call the script with `./docker-compile.sh -h` for more options diff --git a/bin/scripts/docker-compile.sh b/bin/scripts/docker-compile.sh index 6d4215b8..c0e81d60 100644 --- a/bin/scripts/docker-compile.sh +++ b/bin/scripts/docker-compile.sh @@ -5,8 +5,8 @@ DOCKER="docker" GIT_REPO_URL="https://github.com/hyperion-project/hyperion.ng.git" # cmake build type BUILD_TYPE="Release" -# the image tag at hyperionorg/hyperion-ci -BUILD_TARGET="ubuntu1604" +# the image tag at hyperionproject/hyperion-ci +BUILD_TARGET="amd64" # build packages (.deb .zip ...) BUILD_PACKAGES=true # packages string inserted to cmake cmd @@ -42,8 +42,8 @@ function printHelp { echo "######################################################## ## A script to compile Hyperion inside a docker container ## Requires installed Docker: https://www.docker.com/ -## Without arguments it will compile Hyperion for Ubuntu 16.04 (x64) or higher. -## Supports Raspberry Pi (armv6) cross compilation (Raspbian Stretch) +## Without arguments it will compile Hyperion for Debain Stretch (x64) or higher. +## Supports Raspberry Pi (armv6hf, armv7hf) cross compilation (Debian Stretch) ## ## Homepage: https://www.hyperion-project.org ## Forum: https://forum.hyperion-project.org @@ -51,10 +51,10 @@ echo "######################################################## # These are possible arguments to modify the script behaviour with their default values # # docker-compile.sh -h # Show this help message -# docker-compile.sh -t ubuntu1604 # The docker tag, one of ubuntu1604 | cross-qemu-rpistretch +# docker-compile.sh -t amd64 # The docker tag, one of amd64 | i386 | armv6hf | armv7hf # docker-compile.sh -b Release # cmake Release or Debug build # docker-compile.sh -p true # If true build packages with CPack -# More informations to docker tags at: https://hub.docker.com/r/hyperionorg/hyperion-ci/" +# More informations to docker tags at: https://hub.docker.com/r/hyperionproject/hyperion-ci/" } while getopts t:b:p:h option From c22a2df1b625aaab5e5c0549b15e41587e3d42e5 Mon Sep 17 00:00:00 2001 From: Paulchen Panther Date: Sun, 17 Feb 2019 19:47:30 +0100 Subject: [PATCH 05/74] Platforms explicitly specified --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 06d1a25d..0d1a1f88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,11 +34,13 @@ jobs: env: - DOCKER_TAG=armv6hf - DOCKER_NAME=""Debian Stretch (Raspberry Pi v1 & ZERO)" + - PLATFORM="rpi" - <<: *linux name: "ARMv7hf (Raspberry Pi 2 & 3)" env: - DOCKER_TAG=armv7hf - DOCKER_NAME="Debian Stretch (Raspberry Pi 2 & 3)" + - PLATFORM="rpi" - <<: *linux name: "ARMv8 (Generic AARCH64)" env: From 090a5f9f3d3f6b6423827c543dc3c816b6871b52 Mon Sep 17 00:00:00 2001 From: Paulchen Panther Date: Sun, 17 Feb 2019 19:52:01 +0100 Subject: [PATCH 06/74] Fixed a typo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0d1a1f88..a73d1175 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ jobs: name: "ARMv6hf (Raspberry Pi v1 & ZERO)" env: - DOCKER_TAG=armv6hf - - DOCKER_NAME=""Debian Stretch (Raspberry Pi v1 & ZERO)" + - DOCKER_NAME="Debian Stretch (Raspberry Pi v1 & ZERO)" - PLATFORM="rpi" - <<: *linux name: "ARMv7hf (Raspberry Pi 2 & 3)" From 2ae692de2644f908dff17a8798242777c4631e4a Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Mon, 18 Feb 2019 21:16:30 +0100 Subject: [PATCH 07/74] Config correction enabled Change of protobuf dependency for Amlogic --- dependencies/CMakeLists.txt | 4 ++++ libsrc/hyperion/SettingsManager.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 66fcde99..67e30835 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -76,6 +76,10 @@ set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use proto if (USE_SYSTEM_PROTO_LIBS) find_package(Protobuf REQUIRED) + if (ENABLE_AMLOGIC) + set(PROTOBUF_INCLUDE_DIRS "${Protobuf_INCLUDE_DIRS}" PARENT_SCOPE) + set(PROTOBUF_PROTOC_EXECUTABLE "${Protobuf_PROTOC_EXECUTABLE}" PARENT_SCOPE) + endif() include_directories(${PROTOBUF_INCLUDE_DIRS}) else () set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared protobuf library") diff --git a/libsrc/hyperion/SettingsManager.cpp b/libsrc/hyperion/SettingsManager.cpp index e024aa1b..08f20914 100644 --- a/libsrc/hyperion/SettingsManager.cpp +++ b/libsrc/hyperion/SettingsManager.cpp @@ -41,6 +41,7 @@ SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, con Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile)); QJsonSchemaChecker schemaCheckerT; + schemaCheckerT.setSchema(schemaJson); if(!JsonUtils::readFile(configFile, _qconfig, _log)) throw std::runtime_error("Failed to load config!"); @@ -96,6 +97,7 @@ SettingsManager::SettingsManager(const quint8& instance, const QString& configFi Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile)); QJsonSchemaChecker schemaCheckerT; + schemaCheckerT.setSchema(schemaJson); if(!JsonUtils::readFile(configFile, _qconfig, _log)) throw std::runtime_error("Failed to load config!"); From 0ebff511b280624e10ee5c05c01af9bc314edf2d Mon Sep 17 00:00:00 2001 From: Paulchen Panther Date: Mon, 25 Feb 2019 21:10:44 +0100 Subject: [PATCH 08/74] Calling setDeviceVideoStandard in the constructor --- libsrc/grabber/v4l2/V4L2Grabber.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 8d3a3785..2f17f238 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -54,6 +54,9 @@ V4L2Grabber::V4L2Grabber(const QString & device { setPixelDecimation(pixelDecimation); getV4Ldevices(); + + // init + setDeviceVideoStandard(device, videoStandard); } V4L2Grabber::~V4L2Grabber() From cffe33eda8aa7d565fa292998f00ace0968f9e8c Mon Sep 17 00:00:00 2001 From: Paulchen Panther Date: Mon, 25 Feb 2019 21:15:04 +0100 Subject: [PATCH 09/74] setDeviceVideoStandard has been removed because this is called in the constructor --- src/hyperiond/hyperiond.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 769d17d5..d2c05e3c 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -427,13 +427,8 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& type, const QJso connect(this, &HyperionDaemon::settingsChanged, grabber, &V4L2Wrapper::handleSettingsUpdate); if (enableV4l) - { v4lEnableCount++; - // init - grabber->setDeviceVideoStandard(grabberConfig["device"].toString("auto"), parseVideoStandard(grabberConfig["standard"].toString("no-change"))); - } - _v4l2Grabbers.push_back(grabber); #endif } From e5335c539522c9245ed7e29f8fbcaca2d9105ff7 Mon Sep 17 00:00:00 2001 From: Paulchen Panther Date: Fri, 8 Mar 2019 23:51:50 +0100 Subject: [PATCH 10/74] Fixed Amlogic framebuffer pixel format Issue: https://hyperion-project.org/threads/coreelec-hyperion-ng.3286/#post-13727 --- libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp b/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp index 9cf2675b..a14cf805 100755 --- a/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp +++ b/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp @@ -46,7 +46,11 @@ int FramebufferFrameGrabber::grabFrame(Image & image) { case 16: pixelFormat = PIXELFORMAT_BGR16; break; case 24: pixelFormat = PIXELFORMAT_BGR24; break; +#ifdef ENABLE_AMLOGIC + case 32: pixelFormat = PIXELFORMAT_RGB32; break; +#else case 32: pixelFormat = PIXELFORMAT_BGR32; break; +#endif default: Error(_log, "Unknown pixel format: %d bits per pixel", vinfo.bits_per_pixel); close(_fbfd); From 46cad16305d13d20b43e06729717d4bb4bf4a075 Mon Sep 17 00:00:00 2001 From: Portisch Date: Thu, 14 Mar 2019 11:55:17 +0100 Subject: [PATCH 11/74] Amlogic grabber: fixup closing video device on each frame --- libsrc/grabber/amlogic/AmlogicGrabber.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libsrc/grabber/amlogic/AmlogicGrabber.cpp b/libsrc/grabber/amlogic/AmlogicGrabber.cpp index f0aeebaa..23d79082 100755 --- a/libsrc/grabber/amlogic/AmlogicGrabber.cpp +++ b/libsrc/grabber/amlogic/AmlogicGrabber.cpp @@ -117,7 +117,11 @@ int AmlogicGrabber::grabFrame(Image & image) } else if (_device == "amvideocap0") { - grabFrame_amvideocap(image); + if (grabFrame_amvideocap(image) < 0) + { + closeDev(_videoDev); + closeDev(_captureDev); + } } } else @@ -131,8 +135,6 @@ int AmlogicGrabber::grabFrame(Image & image) _fbGrabber.grabFrame(image); } - closeDev(_videoDev); - return 0; } @@ -176,6 +178,7 @@ int AmlogicGrabber::grabFrame_amvideocap(Image & image) // Read of snapshot failed ErrorIf(_lastError != 4, _log,"Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", _bytesToRead, bytesRead); closeDev(_captureDev); + _lastError = 4; return -1; } From 945ab92ef02034f0fb8eb5aae25aa499905f2260 Mon Sep 17 00:00:00 2001 From: bajiomeke <44488093+bajiomeke@users.noreply.github.com> Date: Thu, 28 Mar 2019 13:45:48 -0300 Subject: [PATCH 12/74] New command I have problem when execute "make -j $(nproc)", so the user Akriss from Hyperion Forum oriented me to execute the command "sudo apt-get install rbp-userland-dev-osmc" instead of "sudo apt-get install libraspberrypi-dev". Then I could execute "make -j $(nproc)". So I added a sub-section called "if you have a problem when execute.." with the new command. Not sure if the title is good, feel free to change. Thanks a lot --- CompileHowto.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CompileHowto.md b/CompileHowto.md index 205f9a47..c9e3761c 100644 --- a/CompileHowto.md +++ b/CompileHowto.md @@ -27,6 +27,10 @@ sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev sudo apt-get install libraspberrypi-dev ``` +**if you have a problem when execute make -j $(nproc), execute this command** +``` +sudo apt-get install rbp-userland-dev-osmc +``` **ATTENTION Win10LinuxSubsystem** we do not (/we can't) support using hyperion in linux subsystem of MS Windows 10, albeit some users tested it with success. Keep in mind to disable all linux specific led and grabber hardware via cmake. Because we use QT as framework in hyperion, serialport leds and network driven devices could work. From 3c898762fd8ed65bbd8b8b84f8ffbe4b4f7e84ef Mon Sep 17 00:00:00 2001 From: bajiomeke <44488093+bajiomeke@users.noreply.github.com> Date: Mon, 1 Apr 2019 20:40:35 -0300 Subject: [PATCH 13/74] Change title Change title of the new command to: OSMC on Raspberry Pi --- CompileHowto.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CompileHowto.md b/CompileHowto.md index c9e3761c..526f1531 100644 --- a/CompileHowto.md +++ b/CompileHowto.md @@ -27,7 +27,7 @@ sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev sudo apt-get install libraspberrypi-dev ``` -**if you have a problem when execute make -j $(nproc), execute this command** +**OSMC on Raspberry Pi** ``` sudo apt-get install rbp-userland-dev-osmc ``` From 69ceee8ba5f78dace2cd850dfc25faa79420bacc Mon Sep 17 00:00:00 2001 From: Paulchen Panther Date: Tue, 2 Apr 2019 17:54:15 +0200 Subject: [PATCH 14/74] Added License Icon --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff1cfd7f..3423c965 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -HYPERION -======== +# HYPERION [![Build Status](https://travis-ci.org/hyperion-project/hyperion.ng.svg?branch=master)](https://travis-ci.org/hyperion-project/hyperion.ng) +[![GitHub license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/hyperion-project/hyperion.ng/master/LICENSE) This is a pre alpha development repository for the next major version of hyperion @@ -16,6 +16,7 @@ If you want to use hyperion as 'normal user', please use [current stable version Besides of that .... Feel free to join us! We are looking always for people who wants to participate. -------- +## About Hyperion is an opensource 'AmbiLight' implementation with support for many LED devices and video grabbers. From 3661172d6d1144b58478aa73c0f551d89ae7a5a7 Mon Sep 17 00:00:00 2001 From: LordGrey Date: Mon, 8 Apr 2019 23:13:11 +0200 Subject: [PATCH 15/74] Change Aurora device support to cover additional Nanoleaf devices * Support Nanoleaf LightPanels (aka Aurora) and Canvas * Add Nanoleaf Device discovery * Update SSDPDiscover to be generic for given services --- assets/webconfig/i18n/de.json | 2 +- assets/webconfig/i18n/en.json | 2 +- assets/webconfig/js/content_leds.js | 2 +- libsrc/leddevice/CMakeLists.txt | 1 + libsrc/leddevice/LedDeviceSchemas.qrc | 2 +- libsrc/leddevice/dev_net/LedDeviceAurora.cpp | 196 ------- libsrc/leddevice/dev_net/LedDeviceAurora.h | 63 --- .../leddevice/dev_net/LedDeviceNanoleaf.cpp | 501 ++++++++++++++++++ libsrc/leddevice/dev_net/LedDeviceNanoleaf.h | 148 ++++++ ...chema-aurora.json => schema-nanoleaf.json} | 4 +- libsrc/ssdp/SSDPDiscover.cpp | 15 +- 11 files changed, 665 insertions(+), 271 deletions(-) delete mode 100644 libsrc/leddevice/dev_net/LedDeviceAurora.cpp delete mode 100644 libsrc/leddevice/dev_net/LedDeviceAurora.h create mode 100644 libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp create mode 100644 libsrc/leddevice/dev_net/LedDeviceNanoleaf.h rename libsrc/leddevice/schemas/{schema-aurora.json => schema-nanoleaf.json} (80%) diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json index bfced0ff..83a2d397 100644 --- a/assets/webconfig/i18n/de.json +++ b/assets/webconfig/i18n/de.json @@ -348,7 +348,7 @@ "wiz_cc_morethanone" : "Du hast mehr als 1 Profil, bitte wähle das zu kalibrierende Profil", "wiz_cc_btn_stop" : "Stoppe Video", "wiz_cc_summary" : "Im folgenden eine Zusammenfassung deiner Einstellungen. Während du ein Video abspielst, kannst du hier weiter ausprobieren. Wenn du fertig bist, klicke auf speichern.", - "edt_dev_auth_key_title" : "Aurora API Schlüssel", + "edt_dev_auth_key_title" : "Authentisierungstoken", "edt_dev_enum_subtract_minimum" : "Subtrahiere minimum", "edt_dev_enum_sub_min_cool_adjust" : "Minimale Anpassung: cool", "edt_dev_enum_sub_min_warm_adjust" : "Minimale Anpassung: warm", diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 496c7e76..226e8a40 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -348,7 +348,7 @@ "wiz_cc_morethanone" : "You have more than one profile, please choose the profile you want to calibrate.", "wiz_cc_btn_stop" : "Stop video", "wiz_cc_summary" : "A conclusion of your settings. During video playback, you could change or test values again. If you are done, click on save.", - "edt_dev_auth_key_title" : "Aurora API Key", + "edt_dev_auth_key_title" : "Authentication Token", "edt_dev_enum_subtract_minimum" : "Substract minimum", "edt_dev_enum_sub_min_cool_adjust" : "Subtract cool white", "edt_dev_enum_sub_min_warm_adjust" : "Subtract warm white", diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 81d02e64..fba5384b 100644 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -473,7 +473,7 @@ $(document).ready(function() { devRPiSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'ws2812spi']; devRPiPWM = ['ws281x']; devRPiGPIO = ['piblaster']; - devNET = ['atmoorb', 'fadecandy', 'philipshue', 'aurora', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw']; + devNET = ['atmoorb', 'fadecandy', 'philipshue', 'nanoleaf', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw']; devUSB = ['adalight', 'dmx', 'atmo', 'hyperionusbasp', 'lightpack', 'multilightpack', 'paintpack', 'rawhid', 'sedu', 'tpm2', 'karate']; var optArr = [[]]; diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 885db01f..107f9c58 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -87,6 +87,7 @@ target_link_libraries(leddevice ${CMAKE_THREAD_LIBS_INIT} Qt5::Network Qt5::SerialPort + ssdp ) if(ENABLE_TINKERFORGE) diff --git a/libsrc/leddevice/LedDeviceSchemas.qrc b/libsrc/leddevice/LedDeviceSchemas.qrc index db9100aa..f59d19f5 100644 --- a/libsrc/leddevice/LedDeviceSchemas.qrc +++ b/libsrc/leddevice/LedDeviceSchemas.qrc @@ -32,6 +32,6 @@ schemas/schema-apa104.json schemas/schema-ws281x.json schemas/schema-karate.json - schemas/schema-aurora.json + schemas/schema-nanoleaf.json diff --git a/libsrc/leddevice/dev_net/LedDeviceAurora.cpp b/libsrc/leddevice/dev_net/LedDeviceAurora.cpp deleted file mode 100644 index 9ebb91b2..00000000 --- a/libsrc/leddevice/dev_net/LedDeviceAurora.cpp +++ /dev/null @@ -1,196 +0,0 @@ - -// Local-Hyperion includes -#include "LedDeviceAurora.h" -#include -#include -// qt includes -#include -#include -#include - -#define ll ss - -struct addrinfo vints, *serverinfo, *pt; -//char udpbuffer[1024]; -int sockfp; -int update_num; -LedDevice* LedDeviceAurora::construct(const QJsonObject &deviceConfig) -{ - return new LedDeviceAurora(deviceConfig); -} - -LedDeviceAurora::LedDeviceAurora(const QJsonObject &deviceConfig) { - init(deviceConfig); -} - -bool LedDeviceAurora::init(const QJsonObject &deviceConfig) { - const QString hostname = deviceConfig["output"].toString(); - const QString key = deviceConfig["key"].toString(); - - manager = new QNetworkAccessManager(); - QString port; - // Read Panel count and panel Ids - QByteArray response = get(hostname, key, "panelLayout/layout"); - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(response, &error); - if (error.error != QJsonParseError::NoError) - { - throw std::runtime_error("No Layout found. Check hostname and auth key"); - } - //Debug - QString strJson(doc.toJson(QJsonDocument::Compact)); - std::cout << strJson.toUtf8().constData() << std::endl; - - QJsonObject json = doc.object(); - - panelCount = json["numPanels"].toInt(); - std::cout << panelCount << std::endl; - QJsonObject positionDataJson = doc.object()["positionData"].toObject(); - QJsonArray positionData = json["positionData"].toArray(); - // Loop over all children. - foreach (const QJsonValue & value, positionData) { - QJsonObject panelObj = value.toObject(); - int panelId = panelObj["panelId"].toInt(); - panelIds.push_back(panelId); - } - - // Check if we found enough lights. - if (panelIds.size() != panelCount) { - throw std::runtime_error("Not enough lights found"); - }else { - std::cout << "All panel Ids found: "<< panelIds.size() << std::endl; - } - - // Set Aurora to UDP Mode - QByteArray modeResponse = changeMode(hostname, key, "effects"); - QJsonDocument configDoc = QJsonDocument::fromJson(modeResponse, &error); - - //Debug - //QString strConf(configDoc.toJson(QJsonDocument::Compact)); - //std::cout << strConf.toUtf8().constData() << std::endl; - - if (error.error != QJsonParseError::NoError) - { - throw std::runtime_error("Could not change mode"); - } - - // Get UDP port - port = QString::number(configDoc.object()["streamControlPort"].toInt()); - - std::cout << "hostname " << hostname.toStdString() << " port " << port.toStdString() << std::endl; - - int rv; - - memset(&vints, 0, sizeof vints); - vints.ai_family = AF_UNSPEC; - vints.ai_socktype = SOCK_DGRAM; - - if ((rv = getaddrinfo(hostname.toUtf8().constData() , port.toUtf8().constData(), &vints, &serverinfo)) != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); - assert(rv==0); - } - - // loop through all the results and make a socket - for(pt = serverinfo; pt != NULL; pt = pt->ai_next) { - if ((sockfp = socket(pt->ai_family, pt->ai_socktype, - pt->ai_protocol)) == -1) { - perror("talker: socket"); - continue; - } - - break; - } - - if (pt == NULL) { - fprintf(stderr, "talker: failed to create socket\n"); - assert(pt!=NULL); - } - std::cout << "Started successfully "; - return true; -} - -QString LedDeviceAurora::getUrl(QString host, QString token, QString route) { - return QString("http://%1:16021/api/v1/%2/%3").arg(host).arg(token).arg(route); -} - -QByteArray LedDeviceAurora::get(QString host, QString token, QString route) { - QString url = getUrl(host, token, route); - // Perfrom request - QNetworkRequest request(url); - QNetworkReply* reply = manager->get(request); - // Connect requestFinished signal to quit slot of the loop. - QEventLoop loop; - loop.connect(reply, SIGNAL(finished()), SLOT(quit())); - // Go into the loop until the request is finished. - loop.exec(); - // Read all data of the response. - QByteArray response = reply->readAll(); - // Free space. - reply->deleteLater(); - // Return response - return response; -} - -QByteArray LedDeviceAurora::putJson(QString url, QString json) { - // Perfrom request - QNetworkRequest request(url); - QNetworkReply* reply = manager->put(request, json.toUtf8()); - // Connect requestFinished signal to quit slot of the loop. - QEventLoop loop; - loop.connect(reply, SIGNAL(finished()), SLOT(quit())); - // Go into the loop until the request is finished. - loop.exec(); - // Read all data of the response. - QByteArray response = reply->readAll(); - // Free space. - reply->deleteLater(); - // Return response - return response; -} - -QByteArray LedDeviceAurora::changeMode(QString host, QString token, QString route) { - QString url = getUrl(host, token, route); - QString jsondata( "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}"); //Enable UDP Mode - return putJson(url, jsondata); -} - -LedDeviceAurora::~LedDeviceAurora() -{ - delete manager; -} - -int LedDeviceAurora::write(const std::vector & ledValues) -{ - uint udpBufferSize = panelCount * 7 + 1; - char udpbuffer[udpBufferSize]; - update_num++; - update_num &= 0xf; - - int i=0; - int panelCounter = 0; - udpbuffer[i++] = panelCount; - for (const ColorRgb& color : ledValues) - { - if ((unsigned)i < udpBufferSize) { - udpbuffer[i++] = panelIds[panelCounter++ % panelCount]; - udpbuffer[i++] = 1; // No of Frames - udpbuffer[i++] = color.red; - udpbuffer[i++] = color.green; - udpbuffer[i++] = color.blue; - udpbuffer[i++] = 0; // W not set manually - udpbuffer[i++] = 1; // currently fixed at value 1 which corresponds to 100ms - } - if((unsigned)panelCounter > panelCount) { - break; - } - //printf ("c.red %d sz c.red %d\n", color.red, sizeof(color.red)); - } - sendto(sockfp, udpbuffer, i, 0, pt->ai_addr, pt->ai_addrlen); - - return 0; -} - -int LedDeviceAurora::switchOff() -{ - return 0; -} diff --git a/libsrc/leddevice/dev_net/LedDeviceAurora.h b/libsrc/leddevice/dev_net/LedDeviceAurora.h deleted file mode 100644 index dad09a64..00000000 --- a/libsrc/leddevice/dev_net/LedDeviceAurora.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -// Leddevice includes -#include -// Qt includes -#include -#include -#include -#include -/// -/// Implementation of the LedDevice that write the led-colors to an -/// ASCII-textfile('/home/pi/LedDevice.out') -/// -class LedDeviceAurora : public LedDevice -{ -public: - /// - /// Constructs the test-device, which opens an output stream to the file - /// - LedDeviceAurora(const QJsonObject &deviceConfig); - - /// - /// Destructor of this test-device - /// - virtual ~LedDeviceAurora(); - - /// Switch the leds off - virtual int switchOff(); - /// constructs leddevice - static LedDevice* construct(const QJsonObject &deviceConfig); -protected: - /// - /// Writes the RGB-Color values to the leds. - /// - /// @param[in] ledValues The RGB-color per led - /// - /// @return Zero on success else negative - /// - virtual int write(const std::vector & ledValues); - - bool init(const QJsonObject &deviceConfig); - -private: - /// The outputstream - // std::ofstream _ofs; - // QNetworkAccessManager object for sending requests. - QNetworkAccessManager* manager; - - // the number of leds (needed when switching off) - - size_t panelCount; - /// Array of the pannel ids. - std::vector panelIds; - QByteArray get(QString host, QString token, QString route); - QByteArray putJson(QString url, QString json); - QByteArray changeMode(QString host, QString token, QString route); - /// - /// @param route - /// - /// @return the full URL of the request. - /// - QString getUrl(QString host, QString token, QString route); -}; diff --git a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp new file mode 100644 index 00000000..288776ee --- /dev/null +++ b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp @@ -0,0 +1,501 @@ +// Local-Hyperion includes +#include "LedDeviceNanoleaf.h" + +// ssdp discover +#include + +// Qt includes +#include +#include + +// Controller configuration settings +const QString CONFIG_ADDRESS = "output"; +const QString CONFIG_PORT = "port"; +const QString CONFIG_AUTH_TOKEN ="token"; + +// Panel configuration settings +const QString PANEL_LAYOUT = "layout"; +const QString PANEL_NUM = "numPanels"; +const QString PANEL_ID = "panelId"; +const QString PANEL_POSITIONDATA = "positionData"; +const QString PANEL_SHAPE_TYPE = "shapeType"; +const QString PANEL_ORIENTATION = "0"; +const QString PANEL_POS_X = "x"; +const QString PANEL_POS_Y = "y"; + +// List of State Information +const QString STATE_ON = "on"; +const QString STATE_ONOFF_VALUE = "value"; +const QString STATE_VALUE_TRUE = "true"; +const QString STATE_VALUE_FALSE = "false"; + +//Device Data elements +const QString DEV_DATA_NAME = "name"; +const QString DEV_DATA_MODEL = "model"; +const QString DEV_DATA_MANUFACTURER = "manufacturer"; +const QString DEV_DATA_FIRMWAREVERSION = "firmwareVersion"; + +//Nanoleaf Stream Control elements +const QString STREAM_CONTROL_IP = "streamControlIpAddr"; +const QString STREAM_CONTROL_PORT = "streamControlPort"; +const QString STREAM_CONTROL_PROTOCOL = "streamControlProtocol"; +const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222; //Fixed port for Canvas; + +// Nanoleaf OpenAPI URLs +const QString API_DEFAULT_PORT = "16021"; +const QString API_URL_FORMAT = "http://%1:%2/api/v1/%3/%4"; +const QString API_ROOT = ""; +const QString API_EXT_MODE_STRING_V1 = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\"}}"; +const QString API_EXT_MODE_STRING_V2 = "{\"write\" : {\"command\" : \"display\", \"animType\" : \"extControl\", \"extControlVersion\" : \"v2\"}}"; +const QString API_STATE ="state"; +const QString API_PANELLAYOUT = "panelLayout"; +const QString API_EFFECT = "effects"; + +//Nanoleaf ssdp services +const QString SSDP_CANVAS = "nanoleaf:nl29"; +const QString SSDP_LIGHTPANELS = "nanoleaf_aurora:light"; +const int SSDP_TIMEOUT = 5000; // timout in ms + +// Nanoleaf Panel Shapetypes +enum SHAPETYPES { + TRIANGLE, + RHYTM, + SQUARE, + CONTROL_SQUARE_PRIMARY, + CONTROL_SQUARE_PASSIVE, + POWER_SUPPLY, +}; + +// Nanoleaf external control versions +enum EXTCONTROLVERSIONS { + EXTCTRLVER_V1 = 1, + EXTCTRLVER_V2 +}; + +LedDevice* LedDeviceNanoleaf::construct(const QJsonObject &deviceConfig) +{ + return new LedDeviceNanoleaf(deviceConfig); +} + +LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject &deviceConfig) + : ProviderUdp() +{ + init(deviceConfig); +} + +bool LedDeviceNanoleaf::init(const QJsonObject &deviceConfig) { + + LedDevice::init(deviceConfig); + + int configuredLedCount = this->getLedCount(); + Debug(_log, "ActiveDevice : %s", QSTRING_CSTR( this->getActiveDevice() )); + Debug(_log, "LedCount : %d", configuredLedCount); + Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() )); + Debug(_log, "LatchTime : %d", this->getLatchTime()); + + //Set hostname as per configuration and default port + _hostname = deviceConfig[ CONFIG_ADDRESS ].toString(); + _api_port = API_DEFAULT_PORT; + _auth_token = deviceConfig[ CONFIG_AUTH_TOKEN ].toString(); + + //If host not configured then discover device + if ( _hostname.isEmpty() ) + //Discover Nanoleaf device + if ( !discoverNanoleafDevice() ) { + Error(_log, "No target IP defined nor Nanoleaf device discovered"); + return false; + } + + //Get Nanoleaf device details and configuration + _networkmanager = new QNetworkAccessManager(); + + // Read Panel count and panel Ids + QString url = getUrl(_hostname, _api_port, _auth_token, API_ROOT ); + QJsonDocument doc = getJson( url ); + + QJsonObject jsonAllPanelInfo = doc.object(); + + QString deviceName = jsonAllPanelInfo[DEV_DATA_NAME].toString(); + _deviceModel = jsonAllPanelInfo[DEV_DATA_MODEL].toString(); + QString deviceManufacturer = jsonAllPanelInfo[DEV_DATA_MANUFACTURER].toString(); + _deviceFirmwareVersion = jsonAllPanelInfo[DEV_DATA_FIRMWAREVERSION].toString(); + + Debug(_log, "Name : %s", QSTRING_CSTR( deviceName )); + Debug(_log, "Model : %s", QSTRING_CSTR( _deviceModel )); + Debug(_log, "Manufacturer : %s", QSTRING_CSTR( deviceManufacturer )); + Debug(_log, "FirmwareVersion: %s", QSTRING_CSTR( _deviceFirmwareVersion)); + + // Get panel details from /panelLayout/layout + QJsonObject jsonPanelLayout = jsonAllPanelInfo[API_PANELLAYOUT].toObject(); + QJsonObject jsonLayout = jsonPanelLayout[PANEL_LAYOUT].toObject(); + + int panelNum = jsonLayout[PANEL_NUM].toInt(); + QJsonArray positionData = jsonLayout[PANEL_POSITIONDATA].toArray(); + + std::map> panelMap; + + // Loop over all children. + foreach (const QJsonValue & value, positionData) { + QJsonObject panelObj = value.toObject(); + + int panelId = panelObj[PANEL_ID].toInt(); + int panelX = panelObj[PANEL_POS_X].toInt(); + int panelY = panelObj[PANEL_POS_Y].toInt(); + int panelshapeType = panelObj[PANEL_SHAPE_TYPE].toInt(); + //int panelOrientation = panelObj[PANEL_ORIENTATION].toInt(); + //std::cout << "Panel [" << panelId << "]" << " (" << panelX << "," << panelY << ") - Type: [" << panelshapeType << "]" << std::endl; + + // Skip Rhythm panels + if ( panelshapeType != RHYTM ) { + panelMap[panelY][panelX] = panelId; + } else { + Info(_log, "Rhythm panel skipped."); + } + } + + // Sort panels top down, left right + for(auto posY = panelMap.crbegin(); posY != panelMap.crend(); ++posY) { + // posY.first is the first key + for(auto const &posX : posY->second) { + // posX.first is the second key, posX.second is the data + //std::cout << "panelMap[" << posY->first << "][" << posX.first << "]=" << posX.second << std::endl; + _panelIds.push_back(posX.second); + } + } + this->_panelLedCount = _panelIds.size(); + + + Debug(_log, "PanelsNum : %d", panelNum); + Debug(_log, "PanelLedCount : %d", _panelLedCount); + + // Check. if enough panelds were found. + if (_panelLedCount < configuredLedCount) { + + throw std::runtime_error ( (QString ("Not enough panels [%1] for configured LEDs [%2] found!").arg(_panelLedCount).arg(configuredLedCount)).toStdString() ); + } else { + if ( _panelLedCount > this->getLedCount() ) { + Warning(_log, "Nanoleaf: More panels [%d] than configured LEDs [%d].", _panelLedCount, configuredLedCount ); + } + } + + switchOn(); + + // Set Nanoleaf to External Control (UDP) mode + Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode"); + QJsonDocument responseDoc = changeToExternalControlMode(); + + // Set UDP streaming port + _port = STREAM_CONTROL_DEFAULT_PORT; + + // Resolve port for Ligh Panels + QJsonObject jsonStreamControllInfo = responseDoc.object(); + if ( ! jsonStreamControllInfo.isEmpty() ) { + _port = jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt(); + } + + _defaultHost = _hostname; + ProviderUdp::init(deviceConfig); + + Debug(_log, "Started successfully" ); + return true; +} + +bool LedDeviceNanoleaf::discoverNanoleafDevice() { + + bool isDeviceFound (false); + // device searching by ssdp + QString address; + SSDPDiscover discover; + + // Discover Canvas device + address = discover.getFirstService(STY_WEBSERVER, SSDP_CANVAS, SSDP_TIMEOUT); + + //No Canvas device not found + if ( address.isEmpty() ) { + // Discover Light Panels (Aurora) device + address = discover.getFirstService(STY_WEBSERVER, SSDP_LIGHTPANELS, SSDP_TIMEOUT); + + if ( address.isEmpty() ) { + Warning(_log, "No Nanoleaf device discovered"); + } + } + + // Canvas or Light Panels found + if ( ! address.isEmpty() ) { + Info(_log, "Nanoleaf device discovered at [%s]", QSTRING_CSTR( address )); + isDeviceFound = true; + QStringList addressparts = address.split(":", QString::SkipEmptyParts); + _hostname = addressparts[0]; + _api_port = addressparts[1]; + } + return isDeviceFound; +} + +QJsonDocument LedDeviceNanoleaf::changeToExternalControlMode() { + + QString url = getUrl(_hostname, _api_port, _auth_token, API_EFFECT ); + QJsonDocument jsonDoc; + // If device model is Light Panels (Aurora) + if ( _deviceModel == "NL22") { + _extControlVersion = EXTCTRLVER_V1; + //Enable UDP Mode v1 + jsonDoc = putJson(url, API_EXT_MODE_STRING_V1); + } + else { + _extControlVersion = EXTCTRLVER_V2; + //Enable UDP Mode v2 + jsonDoc= putJson(url, API_EXT_MODE_STRING_V2); + } + return jsonDoc; +} + +QString LedDeviceNanoleaf::getUrl(QString host, QString port, QString auth_token, QString endpoint) const { + return API_URL_FORMAT.arg(host).arg(port).arg(auth_token).arg(endpoint); +} + +QJsonDocument LedDeviceNanoleaf::getJson(QString url) const { + + Debug(_log, "GET: [%s]", QSTRING_CSTR( url )); + + // Perfrom request + QNetworkRequest request(url); + QNetworkReply* reply = _networkmanager->get(request); + // Connect requestFinished signal to quit slot of the loop. + QEventLoop loop; + loop.connect(reply, SIGNAL(finished()), SLOT(quit())); + // Go into the loop until the request is finished. + loop.exec(); + + QJsonDocument jsonDoc; + if(reply->operation() == QNetworkAccessManager::GetOperation) + { + jsonDoc = handleReply( reply ); + } + // Free space. + reply->deleteLater(); + // Return response + return jsonDoc; +} + +QJsonDocument LedDeviceNanoleaf::putJson(QString url, QString json) const { + + Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url ), QSTRING_CSTR( json ) ); + // Perfrom request + QNetworkRequest request(url); + QNetworkReply* reply = _networkmanager->put(request, json.toUtf8()); + // Connect requestFinished signal to quit slot of the loop. + QEventLoop loop; + loop.connect(reply, SIGNAL(finished()), SLOT(quit())); + // Go into the loop until the request is finished. + loop.exec(); + + QJsonDocument jsonDoc; + if(reply->operation() == QNetworkAccessManager::PutOperation) + { + jsonDoc = handleReply( reply ); + } + // Free space. + reply->deleteLater(); + + // Return response + return jsonDoc; +} + +QJsonDocument LedDeviceNanoleaf::handleReply(QNetworkReply* const &reply ) const { + + QJsonDocument jsonDoc; + + int httpStatusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); + if(reply->error() == + QNetworkReply::NoError) + { + + if ( httpStatusCode != 204 ){ + QByteArray response = reply->readAll(); + QJsonParseError error; + jsonDoc = QJsonDocument::fromJson(response, &error); + if (error.error != QJsonParseError::NoError) + { + Error (_log, "Got invalid response"); + throw std::runtime_error(""); + } + else { + //Debug + // QString strJson(jsonDoc.toJson(QJsonDocument::Compact)); + // std::cout << strJson.toUtf8().constData() << std::endl; + } + } + } + else + { + QString errorReason; + if ( httpStatusCode > 0 ) { + QString httpReason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString(); + QString advise; + switch ( httpStatusCode ) { + case 400: + advise = "Check Request Body"; + break; + case 401: + advise = "Check Authentication Token (API Key)"; + break; + case 404: + advise = "Check Resource given"; + break; + default: + break; + } + errorReason = QString ("%1:%2 [%3 %4] - %5").arg(_hostname).arg(_api_port).arg(httpStatusCode).arg(httpReason).arg(advise); + } + else { + errorReason = QString ("%1:%2 - %3").arg(_hostname).arg(_api_port).arg(reply->errorString()); + } + Error (_log, "%s", QSTRING_CSTR( errorReason )); + throw std::runtime_error("Network Error"); + } + // Return response + return jsonDoc; +} + + +LedDeviceNanoleaf::~LedDeviceNanoleaf() +{ + delete _networkmanager; +} + +int LedDeviceNanoleaf::write(const std::vector & ledValues) +{ + + int retVal = 0; + uint udpBufferSize; + + //Light Panels + // nPanels 1B + // nFrames 1B + // panelID 1B + // 3B + // 1B + // tranitionTime 1B + // + //Canvas + //In order to support the much larger number of panels on Canvas, the size of the nPanels, + //panelId and tranitionTime fields have been been increased from 1B to 2B. + //The nFrames field has been dropped as it was set to 1 in v1 anyway + // + // nPanels 2B + // panelID 2B + // 3B + // 1B + // tranitionTime 2B + + + //udpBufferSize = _panelLedCount * 7 + 1; // Buffersize for LightPanels + + udpBufferSize = _panelLedCount * 8 + 2; + uint8_t udpbuffer[udpBufferSize]; + + uchar lowByte; // lower byte + uchar highByte; // upper byte + + uint i=0; + + // Set number of panels + highByte = (uchar) (_panelLedCount >>8 ); + lowByte = (uchar) (_panelLedCount & 0xFF); + + if ( _extControlVersion == EXTCTRLVER_V2 ) { + udpbuffer[i++] = highByte; + } + udpbuffer[i++] = lowByte; + + ColorRgb color; + for ( int panelCounter=0; panelCounter < _panelLedCount; panelCounter++ ) + { + uint panelID = _panelIds[panelCounter]; + + highByte = (uchar) (panelID >>8 ); + lowByte = (uchar) (panelID & 0xFF); + + // Set panels configured + if( panelCounter < this->getLedCount() ) { + color = (ColorRgb) ledValues.at(panelCounter); + } + else + { + // Set panels not configed to black; + color = ColorRgb::BLACK; + //printf ("panelCounter [%d] >= panelLedCount [%d]\n", panelCounter, _panelLedCount ); + } + + // Set panelID + if ( _extControlVersion == EXTCTRLVER_V2 ) { + udpbuffer[i++] = highByte; + } + udpbuffer[i++] = lowByte; + + // Set number of frames - V1 only + if ( _extControlVersion == EXTCTRLVER_V1 ) { + udpbuffer[i++] = 1; // No of Frames + } + + // Set panel's color LEDs + udpbuffer[i++] = color.red; + udpbuffer[i++] = color.green; + udpbuffer[i++] = color.blue; + + // Set white LED + udpbuffer[i++] = 0; // W not set manually + + // Set transition time + unsigned char tranitionTime = 1; // currently fixed at value 1 which corresponds to 100ms + + highByte = (uchar) (tranitionTime >>8 ); + lowByte = (uchar) (tranitionTime & 0xFF); + + if ( _extControlVersion == EXTCTRLVER_V2 ) { + udpbuffer[i++] = highByte; + } + udpbuffer[i++] = lowByte; + + //std::cout << "[" << panelCounter << "]" << " Color: " << color << std::endl; + } + + // printf ("udpBufferSize[%d], Bytes to send [%d]\n", udpBufferSize, i); + // for ( uint c= 0; c < udpBufferSize;c++ ) + // { + // printf ("%x ", (uchar) udpbuffer[c]); + // } + // printf("\n"); + + retVal &= writeBytes( i , udpbuffer); + return retVal; +} + +QString LedDeviceNanoleaf::getOnOffRequest (bool isOn ) const { + QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE; + return QString( "{\"%1\":{\"%2\":%3}}" ).arg(STATE_ON).arg(STATE_ONOFF_VALUE).arg(state); +} + +int LedDeviceNanoleaf::switchOn() { + Debug(_log, "switchOn()"); + //Switch on Nanoleaf device + QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE ); + putJson(url, this->getOnOffRequest(true) ); + + return 0; +} + +int LedDeviceNanoleaf::switchOff() { + Debug(_log, "switchOff()"); + + //Set all LEDs to Black + LedDevice::switchOff(); + + //Switch off Nanoleaf device physically + QString url = getUrl(_hostname, _api_port, _auth_token, API_STATE ); + putJson(url, getOnOffRequest(false) ); + + return _deviceReady ? write(std::vector(_ledCount, ColorRgb::BLACK )) : -1; + + return 0; +} diff --git a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.h b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.h new file mode 100644 index 00000000..00ad576f --- /dev/null +++ b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.h @@ -0,0 +1,148 @@ +#pragma once + +// Leddevice includes +#include +#include "ProviderUdp.h" + +// ssdp discover +#include + +// Qt includes +#include +#include + +/// +/// Implementation of the LedDevice interface for sending to +/// Nanoleaf devices via network by using the 'external control' protocol. +/// +class LedDeviceNanoleaf : public ProviderUdp +{ +public: + /// + /// Constructs the LedDevice for Nanoleaf LightPanels (aka Aurora) or Canvas + /// + /// following code shows all config options + /// @code + /// "device" : + /// { + /// "type" : "nanoleaf" + /// "output" : "hostname or IP", // Optional. If empty, device is tried to be discovered + /// "token" : "Authentication Token", + /// }, + ///@endcode + /// + /// @param deviceConfig json config for nanoleaf + /// + LedDeviceNanoleaf(const QJsonObject &deviceConfig); + + /// + /// Destructor of the LedDevice; closes the tcp client + /// + virtual ~LedDeviceNanoleaf(); + + /// Constructs leddevice + static LedDevice* construct(const QJsonObject &deviceConfig); + + /// Switch the leds on + virtual int switchOn(); + + /// Switch the leds off + virtual int switchOff(); + +protected: + + /// + /// Writes the led color values to the led-device + /// + /// @param ledValues The color-value per led + /// @return Zero on succes else negative + /// + virtual int write(const std::vector & ledValues); + + /// + /// Identifies a Nanoleaf device's panel configuration, + /// sets device into External Control (UDP) mode + /// + /// @param deviceConfig the json device config + /// @return true if success + /// @exception runtime_error in case device cannot be initialised + /// e.g. more LEDs configured than device has panels or network problems + /// + bool init(const QJsonObject &deviceConfig); + +private: + // QNetworkAccessManager object for sending requests. + QNetworkAccessManager* _networkmanager; + + QString _hostname; + QString _api_port; + QString _auth_token; + + //Nanoleaf device details + QString _deviceModel; + QString _deviceFirmwareVersion; + ushort _extControlVersion; + /// The number of panels with leds + int _panelLedCount; + /// Array of the pannel ids. + std::vector _panelIds; + + /// + /// Discover Nanoleaf device via SSDP identifiers + /// + /// @return True, if Nanoleaf device was found + /// + bool discoverNanoleafDevice(); + + /// + /// Change Nanoleaf device to External Control (UDP) mode + /// + /// @return Response from device + /// + QJsonDocument changeToExternalControlMode(); + + /// + /// Get command to switch Nanoleaf device on or off + /// + /// @param isOn True, if to switch on device + /// @return Command to switch device on/off + /// + QString getOnOffRequest (bool isOn ) const; + + /// + /// Get command as url + /// + /// @param host Hostname or IP + /// @param port IP-Port + /// @param _auth_token Authorization token + /// @param Endpoint command for request + /// @return Url to execute endpoint/command + /// + QString getUrl(QString host, QString port, QString auth_token, QString endpoint) const; + + /// + /// Execute GET request + /// + /// @param url GET request for url + /// @return Response from device + /// + QJsonDocument getJson(QString url) const; + + /// + /// Execute PUT request + /// + /// @param Url for PUT request + /// @param json Command for request + /// @return Response from device + /// + QJsonDocument putJson(QString url, QString json) const; + + /// + /// Handle replys for GET and PUT requests + /// + /// @param reply Network reply + /// @return Response for request, if no error + /// @exception runtime_error for network or request errors + /// + QJsonDocument handleReply(QNetworkReply* const &reply ) const; +}; diff --git a/libsrc/leddevice/schemas/schema-aurora.json b/libsrc/leddevice/schemas/schema-nanoleaf.json similarity index 80% rename from libsrc/leddevice/schemas/schema-aurora.json rename to libsrc/leddevice/schemas/schema-nanoleaf.json index a116149f..9d12c158 100644 --- a/libsrc/leddevice/schemas/schema-aurora.json +++ b/libsrc/leddevice/schemas/schema-nanoleaf.json @@ -4,10 +4,10 @@ "properties":{ "output": { "type": "string", - "title":"edt_dev_spec_targetIp_title", + "title":"edt_dev_spec_targetIpHost_title", "propertyOrder" : 1 }, - "key": { + "token": { "type": "string", "title":"edt_dev_auth_key_title", "propertyOrder" : 2 diff --git a/libsrc/ssdp/SSDPDiscover.cpp b/libsrc/ssdp/SSDPDiscover.cpp index bfb6666f..35405a95 100644 --- a/libsrc/ssdp/SSDPDiscover.cpp +++ b/libsrc/ssdp/SSDPDiscover.cpp @@ -36,7 +36,7 @@ void SSDPDiscover::searchForService(const QString& st) const QString SSDPDiscover::getFirstService(const searchType& type, const QString& st, const int& timeout_ms) { - Info(_log, "Search for Hyperion server..."); + Info(_log, "Search for Service [%s]", QSTRING_CSTR(st)); _searchTarget = st; // search @@ -44,7 +44,7 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin _udpSocket->waitForReadyRead(timeout_ms); - while (_udpSocket->hasPendingDatagrams()) + while (_udpSocket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(_udpSocket->pendingDatagramSize()); @@ -54,6 +54,9 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin _udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); QString data(datagram); + + Debug(_log, "_data: [%s]", QSTRING_CSTR(data)); + QMap headers; QString address; // parse request @@ -88,7 +91,7 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin //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()); + Info(_log, "Found service [%s] at: %s:%d", QSTRING_CSTR(st), QSTRING_CSTR(url.host()), url.port()); return url.host()+":"+QString::number(url.port()); } @@ -101,13 +104,13 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin } else { - Info(_log, "Found Hyperion server at: %s:%s", QSTRING_CSTR(url.host()), QSTRING_CSTR(fbsport)); + Info(_log, "Found service [%s] at: %s:%s", QSTRING_CSTR(st), QSTRING_CSTR(url.host()), QSTRING_CSTR(fbsport)); return url.host()+":"+fbsport; } } } } - Info(_log,"Search timeout, no Hyperion server found"); + Info(_log,"Search timeout, service [%s] not found", QSTRING_CSTR(st) ); return QString(); } @@ -163,7 +166,7 @@ void SSDPDiscover::sendSearch(const QString& st) { const QString msg = UPNP_DISCOVER_MESSAGE.arg(st); - _udpSocket->writeDatagram(msg.toUtf8(), + _udpSocket->writeDatagram(msg.toUtf8(), QHostAddress(SSDP_ADDR), SSDP_PORT); } From c16ecde539a75dc7384b2e48dff766eb5ffcd632 Mon Sep 17 00:00:00 2001 From: LordGrey Date: Tue, 9 Apr 2019 21:14:12 +0200 Subject: [PATCH 16/74] Call switchOn, if LEDs get enabled again * Support devices which allow to be switched on/off physically, rather than have a black output stream. --- libsrc/leddevice/LedDevice.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp index 45331853..410c85cb 100644 --- a/libsrc/leddevice/LedDevice.cpp +++ b/libsrc/leddevice/LedDevice.cpp @@ -52,6 +52,12 @@ void LedDevice::setEnable(bool enable) { switchOff(); } + else { + if ( !_enabled && enable) + { + switchOn(); + } + } _enabled = enable; } From 101855fe4f2b646c6ee2b3778372e433f6ef4546 Mon Sep 17 00:00:00 2001 From: Portisch Date: Wed, 27 Mar 2019 08:07:45 +0100 Subject: [PATCH 17/74] Amlogic: remove ge2d mode --- assets/webconfig/i18n/de.json | 2 - assets/webconfig/i18n/en.json | 2 - config/hyperion.config.json.commented | 6 +- config/hyperion.config.json.default | 4 +- include/bonjour/bonjourrecord.h | 0 include/bonjour/bonjourserviceregister.h | 0 include/grabber/AmlogicGrabber.h | 118 +--------- include/grabber/AmlogicWrapper.h | 4 +- include/grabber/X11Grabber.h | 0 libsrc/bonjour/bonjourserviceregister.cpp | 0 libsrc/grabber/amlogic/AmlogicGrabber.cpp | 202 +----------------- libsrc/grabber/amlogic/AmlogicWrapper.cpp | 4 +- libsrc/grabber/amlogic/Amvideocap.h | 66 ------ .../framebuffer/FramebufferFrameGrabber.cpp | 0 libsrc/grabber/osx/OsxFrameGrabber.cpp | 0 libsrc/grabber/x11/X11Grabber.cpp | 0 libsrc/leddevice/LedDeviceFactory.cpp | 0 .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 0 libsrc/utils/ImageResampler.cpp | 0 libsrc/utils/RgbToRgbw.cpp | 0 src/hyperion-aml/AmlogicWrapper.cpp | 4 +- src/hyperion-aml/AmlogicWrapper.h | 2 +- src/hyperion-aml/hyperion-aml.cpp | 6 +- src/hyperiond/hyperiond.cpp | 2 +- 24 files changed, 16 insertions(+), 406 deletions(-) mode change 100755 => 100644 include/bonjour/bonjourrecord.h mode change 100755 => 100644 include/bonjour/bonjourserviceregister.h mode change 100755 => 100644 include/grabber/AmlogicGrabber.h mode change 100755 => 100644 include/grabber/X11Grabber.h mode change 100755 => 100644 libsrc/bonjour/bonjourserviceregister.cpp mode change 100755 => 100644 libsrc/grabber/amlogic/AmlogicGrabber.cpp mode change 100755 => 100644 libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp mode change 100755 => 100644 libsrc/grabber/osx/OsxFrameGrabber.cpp mode change 100755 => 100644 libsrc/grabber/x11/X11Grabber.cpp mode change 100755 => 100644 libsrc/leddevice/LedDeviceFactory.cpp mode change 100755 => 100644 libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp mode change 100755 => 100644 libsrc/utils/ImageResampler.cpp mode change 100755 => 100644 libsrc/utils/RgbToRgbw.cpp diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json index bfced0ff..5ab2a96e 100644 --- a/assets/webconfig/i18n/de.json +++ b/assets/webconfig/i18n/de.json @@ -537,8 +537,6 @@ "edt_conf_fg_pixelDecimation_expl" : "Bildverkleinerung (Faktor) ausgehend von der original Größe. 1 für unveränderte/originale Größe.", "edt_conf_fg_device_title" : "Device", "edt_conf_fg_display_title" : "Display", - "edt_conf_fg_amlogic_grabber_title" : "Amlogic Grabber Device", - "edt_conf_fg_ge2d_mode_title" : "Amlogic ge2d Grabber Modus", "edt_conf_fg_display_expl" : "Gebe an von welchem Desktop aufgenommen werden soll. (Multi Monitor Setup)", "edt_conf_bb_heading_title" : "Schwarze Balken Erkennung", "edt_conf_bb_threshold_title" : "Schwelle", diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 496c7e76..5c5e4340 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -538,8 +538,6 @@ "edt_conf_fg_pixelDecimation_expl" : "Reduce picture size (factor) based on original size. A factor of 1 means no change", "edt_conf_fg_device_title" : "Device", "edt_conf_fg_display_title" : "Display", - "edt_conf_fg_amlogic_grabber_title" : "Amlogic Grabber Device", - "edt_conf_fg_ge2d_mode_title" : "Amlogic ge2d Grabber Modus", "edt_conf_fg_display_expl" : "Select which desktop should be captured (multi monitor setup)", "edt_conf_bb_heading_title" : "Blackbar detector", "edt_conf_bb_threshold_title" : "Threshold", diff --git a/config/hyperion.config.json.commented b/config/hyperion.config.json.commented index 80b8d873..3c9d4fa2 100644 --- a/config/hyperion.config.json.commented +++ b/config/hyperion.config.json.commented @@ -162,11 +162,7 @@ "display" 0, // valid for framebuffer - "device" : "/dev/fb0", - - // valid for amlogic - "amlogic_grabber" : "amvideocap0", - "ge2d_mode" : 1 + "device" : "/dev/fb0" }, /// The black border configuration, contains the following items: diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index b8b9f394..6e360981 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -88,9 +88,7 @@ "cropRight" : 0, "cropTop" : 0, "cropBottom" : 0, - "device" : "/dev/fb0", - "amlogic_grabber" : "amvideocap0", - "ge2d_mode" : 1 + "device" : "/dev/fb0" }, "blackborderdetector" : diff --git a/include/bonjour/bonjourrecord.h b/include/bonjour/bonjourrecord.h old mode 100755 new mode 100644 diff --git a/include/bonjour/bonjourserviceregister.h b/include/bonjour/bonjourserviceregister.h old mode 100755 new mode 100644 diff --git a/include/grabber/AmlogicGrabber.h b/include/grabber/AmlogicGrabber.h old mode 100755 new mode 100644 index c47f8fe2..a2f4b78b --- a/include/grabber/AmlogicGrabber.h +++ b/include/grabber/AmlogicGrabber.h @@ -6,113 +6,6 @@ #include #include -class IonBuffer; - -struct rectangle_s { - int x; /* X coordinate of its top-left point */ - int y; /* Y coordinate of its top-left point */ - int w; /* width of it */ - int h; /* height of it */ -}; - -struct ge2d_para_s { - unsigned int color; - struct rectangle_s src1_rect; - struct rectangle_s src2_rect; - struct rectangle_s dst_rect; - int op; -}; - -struct config_planes_s { - unsigned long addr; - unsigned int w; - unsigned int h; -}; - -struct src_key_ctrl_s { - int key_enable; - int key_color; - int key_mask; - int key_mode; -}; - -struct config_para_s { - int src_dst_type; - int alu_const_color; - unsigned int src_format; - unsigned int dst_format; /* add for src&dst all in user space. */ - - struct config_planes_s src_planes[4]; - struct config_planes_s dst_planes[4]; - struct src_key_ctrl_s src_key; -}; - -struct src_dst_para_ex_s { - int canvas_index; - int top; - int left; - int width; - int height; - int format; - int mem_type; - int color; - unsigned char x_rev; - unsigned char y_rev; - unsigned char fill_color_en; - unsigned char fill_mode; -}; - -struct config_para_ex_s { - struct src_dst_para_ex_s src_para; - struct src_dst_para_ex_s src2_para; - struct src_dst_para_ex_s dst_para; - - /* key mask */ - struct src_key_ctrl_s src_key; - struct src_key_ctrl_s src2_key; - - int alu_const_color; - unsigned src1_gb_alpha; - unsigned op_mode; - unsigned char bitmask_en; - unsigned char bytemask_only; - unsigned int bitmask; - unsigned char dst_xy_swap; - - /* scaler and phase releated */ - unsigned hf_init_phase; - int hf_rpt_num; - unsigned hsc_start_phase_step; - int hsc_phase_slope; - unsigned vf_init_phase; - int vf_rpt_num; - unsigned vsc_start_phase_step; - int vsc_phase_slope; - unsigned char src1_vsc_phase0_always_en; - unsigned char src1_hsc_phase0_always_en; - /* 1bit, 0: using minus, 1: using repeat data */ - unsigned char src1_hsc_rpt_ctrl; - /* 1bit, 0: using minus 1: using repeat data */ - unsigned char src1_vsc_rpt_ctrl; - - /* canvas info */ - struct config_planes_s src_planes[4]; - struct config_planes_s src2_planes[4]; - struct config_planes_s dst_planes[4]; -}; - -struct amvideo_grabber_data { - int canvas_index; - uint32_t canvas0Addr; - uint32_t ge2dformat; - uint64_t size; -}; - -enum ge2d_mode { - ge2d_single = 0, - ge2d_combined = 1 -}; - /// /// class AmlogicGrabber : public Grabber @@ -123,9 +16,8 @@ public: /// /// @param[in] width The width of the captured screenshot /// @param[in] height The heigth of the captured screenshot - /// @param[in] ge2d_mode The ge2d mode, 0: single icotl calls, 1: combined data ioctl call /// - AmlogicGrabber(const unsigned width, const unsigned height, const unsigned ge2d_mode, const QString device); + AmlogicGrabber(const unsigned width, const unsigned height); ~AmlogicGrabber(); /// @@ -149,12 +41,10 @@ private: bool openDev(int &fd, const char* dev); int grabFrame_amvideocap(Image & image); - int grabFrame_ge2d(Image & image); /** The snapshot/capture device of the amlogic video chip */ int _captureDev; int _videoDev; - int _ge2dDev; Image _image_bgr; void* _image_ptr; @@ -164,10 +54,4 @@ private: bool _videoPlaying; FramebufferFrameGrabber _fbGrabber; int _grabbingModeNotification; - void* _ge2dVideoBufferPtr; - IonBuffer* _ge2dIonBuffer; - struct config_para_ex_s _configex; - ge2d_para_s _blitRect; - int _ge2d_mode; - QString _device; }; diff --git a/include/grabber/AmlogicWrapper.h b/include/grabber/AmlogicWrapper.h index a908d29a..0f241162 100644 --- a/include/grabber/AmlogicWrapper.h +++ b/include/grabber/AmlogicWrapper.h @@ -18,10 +18,8 @@ public: /// @param[in] grabWidth The width of the grabbed image [pixels] /// @param[in] grabHeight The height of the grabbed images [pixels] /// @param[in] updateRate_Hz The image grab rate [Hz] - /// @param[in] ge2d_mode use single or combine ge2d ioctl call - /// @param[in] device amlogic grabber device /// - AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const unsigned ge2d_mode, const QString device); + AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz); /// /// Destructor of this dispmanx frame grabber. Releases any claimed resources. diff --git a/include/grabber/X11Grabber.h b/include/grabber/X11Grabber.h old mode 100755 new mode 100644 diff --git a/libsrc/bonjour/bonjourserviceregister.cpp b/libsrc/bonjour/bonjourserviceregister.cpp old mode 100755 new mode 100644 diff --git a/libsrc/grabber/amlogic/AmlogicGrabber.cpp b/libsrc/grabber/amlogic/AmlogicGrabber.cpp old mode 100755 new mode 100644 index 23d79082..5ef1e3ea --- a/libsrc/grabber/amlogic/AmlogicGrabber.cpp +++ b/libsrc/grabber/amlogic/AmlogicGrabber.cpp @@ -19,25 +19,16 @@ #define VIDEO_DEVICE "/dev/amvideo" #define CAPTURE_DEVICE "/dev/amvideocap0" -#define GE2D_DEVICE "/dev/ge2d" -AmlogicGrabber::AmlogicGrabber(const unsigned width, const unsigned height, const unsigned ge2d_mode, const QString device) +AmlogicGrabber::AmlogicGrabber(const unsigned width, const unsigned height) : Grabber("AMLOGICGRABBER", qMax(160u, width), qMax(160u, height)) // Minimum required width or height is 160 , _captureDev(-1) , _videoDev(-1) - , _ge2dDev(-1) , _lastError(0) , _fbGrabber("/dev/fb0",width,height) , _grabbingModeNotification(0) - , _ge2dVideoBufferPtr(nullptr) - , _ge2dIonBuffer(nullptr) - , _ge2d_mode(ge2d_mode) - , _device(device) { - Debug(_log, "constructed(%d x %d), grabber device: %s",_width,_height,QSTRING_CSTR(_device)); - - if (_device.contains("ge2d")) - Debug(_log, "'ge2d' device use mode %d",ge2d_mode); + Debug(_log, "constructed(%d x %d), grabber device: %s",_width,_height, CAPTURE_DEVICE); _image_bgr.resize(_width, _height); _bytesToRead = _image_bgr.size(); @@ -48,7 +39,6 @@ AmlogicGrabber::~AmlogicGrabber() { closeDev(_captureDev); closeDev(_videoDev); - closeDev(_ge2dDev); } bool AmlogicGrabber::openDev(int &fd, const char* dev) @@ -107,22 +97,8 @@ int AmlogicGrabber::grabFrame(Image & image) _lastError = 0; } - if (_device == "ge2d") - { - if (grabFrame_ge2d(image) < 0) - { - closeDev(_videoDev); - closeDev(_ge2dDev); - } - } - else if (_device == "amvideocap0") - { - if (grabFrame_amvideocap(image) < 0) - { - closeDev(_videoDev); - closeDev(_captureDev); - } - } + if (grabFrame_amvideocap(image) < 0) + closeDev(_captureDev); } else { @@ -188,173 +164,3 @@ int AmlogicGrabber::grabFrame_amvideocap(Image & image) return 0; } - - -int AmlogicGrabber::grabFrame_ge2d(Image & image) -{ - int ret; - static int videoWidth; - static int videoHeight; - - if ( ! openDev(_ge2dDev, GE2D_DEVICE) || ! openDev(_videoDev, VIDEO_DEVICE)) - { - ErrorIf( _lastError != 1 && _ge2dDev < 0, _log,"Failed to open the ge2d device: (%d - %s)", errno, strerror(errno)); - ErrorIf( _lastError != 1 && _videoDev < 0, _log,"Failed to open the AMLOGIC video device: (%d - %s)", errno, strerror(errno)); - _lastError = 1; - return -1; - } - - // Ion - if (_ge2dIonBuffer == nullptr) - { - _ge2dIonBuffer = new IonBuffer(_width * _height * 3); // BGR - _ge2dVideoBufferPtr = _ge2dIonBuffer->Map(); - memset(_ge2dVideoBufferPtr, 0, _ge2dIonBuffer->BufferSize()); - - memset(&_configex, 0, sizeof(_configex)); - _configex.src_para.mem_type = CANVAS_TYPE_INVALID; - _configex.dst_para.mem_type = CANVAS_ALLOC; - _configex.dst_para.format = GE2D_FORMAT_S24_RGB; - _configex.dst_planes[0].addr = (long unsigned int)_ge2dIonBuffer->PhysicalAddress(); - _configex.dst_para.width = _width; - _configex.dst_para.height = _height; - _configex.dst_planes[0].w = _configex.dst_para.width; - _configex.dst_planes[0].h = _configex.dst_para.height; - - memset(&_blitRect, 0, sizeof(_blitRect)); - _blitRect.dst_rect.w = _configex.dst_para.width; - _blitRect.dst_rect.h = _configex.dst_para.height; - } - - switch(_ge2d_mode) - { - case ge2d_single: - { - int canvas_index; - if ((ret = ioctl(_videoDev, AMVIDEO_EXT_GET_CURRENT_VIDEOFRAME, &canvas_index)) < 0) - { - if (ret != -EAGAIN) - { - Error(_log, "AMVIDEO_EXT_GET_CURRENT_VIDEOFRAME failed: (%d - %s)", errno, strerror(errno)); - } - else - { - Warning(_log, "AMVIDEO_EXT_GET_CURRENT_VIDEOFRAME failed, please try again!"); - } - return -1; - } - - uint32_t canvas0addr; - if (ioctl(_videoDev, AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_CANVAS0ADDR, &canvas0addr) < 0) - { - Error(_log, "AMSTREAM_EXT_CURRENT_VIDEOFRAME_GET_CANVAS0ADDR failed: (%d - %s)", errno, strerror(errno)); - return -1; - } - - _configex.src_para.canvas_index = canvas0addr; - - uint32_t ge2dformat; - if (ioctl(_videoDev, AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_GE2D_FORMAT, &ge2dformat) <0) - { - Error(_log, "AMSTREAM_EXT_CURRENT_VIDEOFRAME_GET_GE2D_FORMAT failed: (%d - %s)", errno, strerror(errno)); - return -1; - } - - _configex.src_para.format = ge2dformat; - - uint64_t size; - if (ioctl(_videoDev, AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_SIZE, &size) < 0) - { - Error(_log, "AMSTREAM_EXT_CURRENT_VIDEOFRAME_GET_SIZE failed: (%d - %s)", errno, strerror(errno)); - return -1; - } - - videoWidth = (size >> 32) - _cropLeft - _cropRight; - videoHeight = (size & 0xffffff) - _cropTop - _cropBottom; - } - break; - - case ge2d_combined: - { - static struct amvideo_grabber_data grabber_data; - - if ((ret = ioctl(_videoDev, AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_DATA, &grabber_data)) < 0) - { - if (ret == -EAGAIN) - { - Warning(_log, "AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_DATA failed, please try again!"); - return 0; - } - else - { - Error(_log, "AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_DATA failed."); - return -1; - } - } - - videoWidth = (grabber_data.size >> 32) - _cropLeft - _cropRight; - videoHeight = (grabber_data.size & 0xffffff) - _cropTop - _cropBottom; - - _configex.src_para.canvas_index = grabber_data.canvas0Addr; - _configex.src_para.format = grabber_data.ge2dformat; - } - break; - } - - unsigned cropLeft = _cropLeft; - unsigned cropTop = _cropTop; - - // calculate final image dimensions and adjust top/left cropping in 3D modes - switch (_videoMode) - { - case VIDEO_3DSBS: - videoWidth >>= 1; - cropLeft = _cropLeft >> 1; - break; - case VIDEO_3DTAB: - videoHeight >>= 1; - cropTop = _cropTop >> 1; - break; - case VIDEO_2D: - default: - break; - } - - _configex.src_para.left = cropLeft; - _configex.src_para.top = cropTop; - _configex.src_para.width = videoWidth; - _configex.src_para.height = videoHeight >> 1; - - if (ioctl(_ge2dDev, GE2D_CONFIG_EX, &_configex) < 0) - { - Error(_log, "video GE2D_CONFIG_EX failed."); - return -1; - } - - _blitRect.src1_rect.w = _configex.src_para.width; - _blitRect.src1_rect.h = _configex.src_para.height; - - // Blit to videoBuffer - if (ioctl(_ge2dDev, GE2D_STRETCHBLIT_NOALPHA, &_blitRect) < 0) - { - Error(_log,"GE2D_STRETCHBLIT_NOALPHA failed."); - return -1; - } - - // Return video frame - if (ioctl(_videoDev, AMVIDEO_EXT_PUT_CURRENT_VIDEOFRAME) < 0) - { - Error(_log, "AMSTREAM_EXT_PUT_CURRENT_VIDEOFRAME failed."); - return -1; - } - - _ge2dIonBuffer->Sync(); - - // Read the snapshot into the memory - _useImageResampler = false; - _imageResampler.processImage((const uint8_t*)_ge2dVideoBufferPtr, _width, _height, (_width << 1) + _width, PIXELFORMAT_BGR24, image); - _lastError = 0; - - return 0; -} - diff --git a/libsrc/grabber/amlogic/AmlogicWrapper.cpp b/libsrc/grabber/amlogic/AmlogicWrapper.cpp index d70ceec1..205787e8 100644 --- a/libsrc/grabber/amlogic/AmlogicWrapper.cpp +++ b/libsrc/grabber/amlogic/AmlogicWrapper.cpp @@ -1,8 +1,8 @@ #include -AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const unsigned ge2d_mode, const QString device) +AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz) : GrabberWrapper("AmLogic", &_grabber, grabWidth, grabHeight, updateRate_Hz) - , _grabber(grabWidth, grabHeight, ge2d_mode, device) + , _grabber(grabWidth, grabHeight) {} void AmlogicWrapper::action() diff --git a/libsrc/grabber/amlogic/Amvideocap.h b/libsrc/grabber/amlogic/Amvideocap.h index ef3e12ee..5dc2fb7b 100644 --- a/libsrc/grabber/amlogic/Amvideocap.h +++ b/libsrc/grabber/amlogic/Amvideocap.h @@ -11,75 +11,9 @@ #define CAP_FLAG_AT_TIME_WINDOW 1 #define CAP_FLAG_AT_END 2 -/* -format see linux/ge2d/ge2d.h -like: -GE2D_FORMAT_S24_RGB -*/ -#define GE2D_ENDIAN_SHIFT 24 -#define GE2D_LITTLE_ENDIAN (1 << GE2D_ENDIAN_SHIFT) -#define GE2D_COLOR_MAP_SHIFT 20 -#define GE2D_COLOR_MAP_BGR888 (5 << GE2D_COLOR_MAP_SHIFT) -#define GE2D_COLOR_MAP_RGB888 (0 << GE2D_COLOR_MAP_SHIFT) -#define GE2D_FMT_S24_RGB (GE2D_LITTLE_ENDIAN|0x00200) /* 10_00_0_00_0_00 */ -#define GE2D_FORMAT_S24_BGR (GE2D_FMT_S24_RGB | GE2D_COLOR_MAP_BGR888) -#define GE2D_FORMAT_S24_RGB (GE2D_FMT_S24_RGB | GE2D_COLOR_MAP_RGB888) - // #define AMVIDEOCAP_IOW_SET_WANTFRAME_FORMAT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x01, int) #define AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH _IOW(AMVIDEOCAP_IOC_MAGIC, 0x02, int) #define AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x03, int) #define _A_M 'S' #define AMSTREAM_IOC_GET_VIDEO_DISABLE _IOR((_A_M), 0x48, int) - -#define AMVIDEO_MAGIC 'X' - -#define AMVIDEO_EXT_GET_CURRENT_VIDEOFRAME _IOR((AMVIDEO_MAGIC), 0x01, int) -#define AMVIDEO_EXT_PUT_CURRENT_VIDEOFRAME _IO((AMVIDEO_MAGIC), 0x02) - -#define AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_GE2D_FORMAT _IOR((AMVIDEO_MAGIC), 0x03, uint32_t) -#define AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_SIZE _IOR((AMVIDEO_MAGIC), 0x04, uint64_t) -#define AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_CANVAS0ADDR _IOR((AMVIDEO_MAGIC), 0x05, uint32_t) -#define AMVIDEO_EXT_CURRENT_VIDEOFRAME_GET_DATA _IOR((AMVIDEO_MAGIC), 0x06, struct amvideo_grabber_data) - -// GE2D commands -#define GE2D_IOC_MAGIC 'G' -#define GE2D_STRETCHBLIT_NOALPHA 0x4702 -#define GE2D_CONFIG_EX _IOW(GE2D_IOC_MAGIC, 0x01, struct config_para_ex_s) - - -// data structures -enum ge2d_src_dst_e { - OSD0_OSD0 = 0, - OSD0_OSD1, - OSD1_OSD1, - OSD1_OSD0, - ALLOC_OSD0, - ALLOC_OSD1, - ALLOC_ALLOC, - TYPE_INVALID, -}; - -enum ge2d_src_canvas_type_e { - CANVAS_OSD0 = 0, - CANVAS_OSD1, - CANVAS_ALLOC, - CANVAS_TYPE_INVALID, -}; - -struct src_dst_para_s { - int xres; - int yres; - int canvas_index; - int bpp; - int ge2d_color_index; -}; - -enum ge2d_op_type_e { - GE2D_OP_DEFAULT = 0, - GE2D_OP_FILLRECT, - GE2D_OP_BLIT, - GE2D_OP_STRETCHBLIT, - GE2D_OP_BLEND, - GE2D_OP_MAXNUM -}; diff --git a/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp b/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp old mode 100755 new mode 100644 diff --git a/libsrc/grabber/osx/OsxFrameGrabber.cpp b/libsrc/grabber/osx/OsxFrameGrabber.cpp old mode 100755 new mode 100644 diff --git a/libsrc/grabber/x11/X11Grabber.cpp b/libsrc/grabber/x11/X11Grabber.cpp old mode 100755 new mode 100644 diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp old mode 100755 new mode 100644 diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp old mode 100755 new mode 100644 diff --git a/libsrc/utils/ImageResampler.cpp b/libsrc/utils/ImageResampler.cpp old mode 100755 new mode 100644 diff --git a/libsrc/utils/RgbToRgbw.cpp b/libsrc/utils/RgbToRgbw.cpp old mode 100755 new mode 100644 diff --git a/src/hyperion-aml/AmlogicWrapper.cpp b/src/hyperion-aml/AmlogicWrapper.cpp index ce4edbc9..54228208 100644 --- a/src/hyperion-aml/AmlogicWrapper.cpp +++ b/src/hyperion-aml/AmlogicWrapper.cpp @@ -2,9 +2,9 @@ // Hyperion-AmLogic includes #include "AmlogicWrapper.h" -AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const unsigned ge2d_mode, const QString device) : +AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz) : _timer(this), - _grabber(grabWidth, grabHeight, ge2d_mode, device) + _grabber(grabWidth, grabHeight) { _timer.setSingleShot(false); _timer.setInterval(updateRate_Hz); diff --git a/src/hyperion-aml/AmlogicWrapper.h b/src/hyperion-aml/AmlogicWrapper.h index 6322d5d7..7ddbcda1 100644 --- a/src/hyperion-aml/AmlogicWrapper.h +++ b/src/hyperion-aml/AmlogicWrapper.h @@ -9,7 +9,7 @@ class AmlogicWrapper : public QObject { Q_OBJECT public: - AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const unsigned ge2d_mode, const QString device); + AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz); const Image & getScreenshot(); diff --git a/src/hyperion-aml/hyperion-aml.cpp b/src/hyperion-aml/hyperion-aml.cpp index cea02dd3..5179f781 100644 --- a/src/hyperion-aml/hyperion-aml.cpp +++ b/src/hyperion-aml/hyperion-aml.cpp @@ -40,9 +40,7 @@ int main(int argc, char ** argv) IntOption & argFps = parser.add ('f', "framerate", "Capture frame rate [default: %1]", "10", 1, 25); IntOption & argWidth = parser.add (0x0, "width", "Width of the captured image [default: %1]", "160", 160, 4096); IntOption & argHeight = parser.add (0x0, "height", "Height of the captured image [default: %1]", "160", 160, 4096); - BooleanOption & argScreenshot = parser.add(0x0, "screenshot", "Take a single screenshot, save it to file and quit"); - IntOption & argge2d_mode = parser.add (0x0, "ge2d-mode", "ge2d ioctl mode, 0 single ioctl, 1 combined ioctl [default: %1]", "0"); - Option & argDevice = parser.add