diff --git a/.github/actions/codeql/action.yml b/.github/actions/codeql/action.yml index a6e5801a9..6dfb8f508 100644 --- a/.github/actions/codeql/action.yml +++ b/.github/actions/codeql/action.yml @@ -91,7 +91,7 @@ runs: uses: advanced-security/filter-sarif@v1 with: patterns: | - -dependencies/** + -external/** input: sarif-results/${{ inputs.language }}.sarif output: sarif-results/${{ inputs.language }}.sarif diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index fc841740a..dd3fdb78c 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -6,7 +6,7 @@ on: env: USE_CACHE: "1" RESET_CACHE: "0" - USE_CODEQL: "0" + USE_CODEQL: "1" BUILD_ARCHIVES: ${{ startsWith(github.event.ref, 'refs/tags') && 1 || 0 }} jobs: @@ -36,6 +36,10 @@ jobs: linuxVersion: jammy dockerName: Ubuntu 22.04 LTS (x86_64) platform: linux + - dockerImage: x86_64 + linuxVersion: mantic + dockerName: Ubuntu 23.10 (x86_64) + platform: linux - dockerImage: arm-32bit-armv6l linuxVersion: bullseye dockerName: Debian Bullseye (ARM 32-bit Raspberry Pi OS) @@ -198,7 +202,7 @@ jobs: runs-on: windows-2022 env: VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC' - QT_VERSION: 6.2.4 + QT_VERSION: 6.5.3 steps: - name: Checkout uses: actions/checkout@v3 @@ -254,7 +258,7 @@ jobs: analyze: name: Analyze (CodeQL) runs-on: ubuntu-latest - if: ( false ) + if: ( true ) permissions: actions: read contents: read diff --git a/.gitignore b/.gitignore index 30df9eb1b..50ac7720a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,12 +23,8 @@ compile_commands.json sources/flatbufserver/hyperhdr_reply_generated.h sources/flatbufserver/hyperhdr_request_generated.h -# lib-turbo -dependencies/jpeg-windows -dependencies/windows -# Network discovery library -dependencies/bonjour +external/windows # Kdevelop project files *.kdev* diff --git a/.gitmodules b/.gitmodules index c06de4fdd..bac569368 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,24 +1,21 @@ -[submodule "dependencies/external/rpi_ws281x"] - path = dependencies/external/rpi_ws281x +[submodule "external/rpi_ws281x"] + path = external/rpi_ws281x url = https://github.com/jgarff/rpi_ws281x branch = master -[submodule "dependencies/external/flatbuffers"] - path = dependencies/external/flatbuffers +[submodule "external/flatbuffers"] + path = external/flatbuffers url = https://github.com/google/flatbuffers branch = master -[submodule "dependencies/external/mbedtls"] - path = dependencies/external/mbedtls +[submodule "external/mbedtls"] + path = external/mbedtls url = https://github.com/ARMmbed/mbedtls -[submodule "dependencies/external/libcec"] - path = dependencies/external/libcec - url = https://github.com/awawa-dev/libcec.git -[submodule "dependencies/external/qmqtt"] - path = dependencies/external/qmqtt +[submodule "external/qmqtt"] + path = external/qmqtt url = https://github.com/emqx/qmqtt.git ignore = dirty -[submodule "dependencies/external/xz"] - path = dependencies/external/xz +[submodule "external/xz"] + path = external/xz url = https://github.com/tukaani-project/xz.git -[submodule "dependencies/external/mdns"] - path = dependencies/external/mdns +[submodule "external/mdns"] + path = external/mdns url = https://github.com/mjansson/mdns.git diff --git a/3RD_PARTY_LICENSES b/3RD_PARTY_LICENSES index 991df2a0b..027711403 100644 --- a/3RD_PARTY_LICENSES +++ b/3RD_PARTY_LICENSES @@ -2584,6 +2584,18 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +============================================= +Simulate iOS toggle button with html and less +============================================= + +Copyright (c) 2023 by coldsoul (https://codepen.io/coldsoul/pen/pbdvPa) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ============================== svg-loader ============================== diff --git a/CHANGELOG.md b/CHANGELOG.md index db9697474..8c61007b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +- Refactoring. Migration to C++ smart pointers (v20 beta ๐Ÿ†•) +- Pixel and vertex shaders hardware acceleration for DX11 Windows grabber (v20 beta ๐Ÿ†•) +- Unified LED driver smoothing and clocking for better linear transition (v20 beta ๐Ÿ†•) +- WLED: migrate to Audio-Reactive-Led-Strip protocol. Overcome 490 LEDs limit (v20 beta ๐Ÿ†•) +- Automatic DB backup before upgrading (v20 beta ๐Ÿ†•) +- support for HyperSPI on Pico rp2040 boards (v20 beta ๐Ÿ†•) +- MQTT: auto-resume broken connection (v20 beta ๐Ÿ†•) +- MQTT: support for multiple JSON API commands in one MQTT request (v20 beta ๐Ÿ†•) +- ArtNet driver: toggle to prevent pixel color data split across multiple universes (v20 beta ๐Ÿ†•) +- Add video buffer memory caching to Pipewire grabber (v20 beta ๐Ÿ†•) +- Fix typo in LED strip name #670 Thanks @fluchfux (v20 beta ๐Ÿ†•) +- Pipewire DMA & EGL hardware support (Wayland/x11 grabber) #556 #556 (v20 beta ๐Ÿ†•) +- Upgrade Fedora 38 to 39, Ubuntu 23.04 to 23.10 #667 (v20 beta ๐Ÿ†•) +- Colorized logs #640 (v20 beta ๐Ÿ†•) +- Fix macOS build #638 #671 #672 (v20 beta ๐Ÿ†•) +- Update language file's. Thanks @AstaRom #617 (v20 beta ๐Ÿ†•) +- Make bonjour use logging utilities. Thanks @nurikk #529 (v20 beta ๐Ÿ†•) - New interface: removed ancient Font Awesome 4 (so 2017...) Migrate to SVG: Bootstrap Icons and Google Material Icons/Symbols #605 (v20 beta ๐Ÿ†•) - New device discovery service #605 (v20 beta ๐Ÿ†•) - Stability improvements & bug fixing #605 (v20 beta ๐Ÿ†•) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47c19314e..2ac3be4f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ SET ( DEFAULT_MQTT ON ) SET ( DEFAULT_STATIC_QT_PLUGINS OFF ) SET ( DEFAULT_PRECOMPILED_HEADERS ON ) SET ( DEFAULT_XZ ON ) +SET ( DEFAULT_POWER_MANAGEMENT ON ) # Configure CCache if available find_program(CCACHE_FOUND ccache) @@ -179,16 +180,23 @@ elseif ( "${PLATFORM}" MATCHES "rpi" ) message(STATUS "Arm model info: ${ARM_MODEL}") string(FIND "${ARM_MODEL}" "raspberry" IS_RASPBERRY) - message(STATUS "Check if Raspberry Pi (0-yes): ${IS_RASPBERRY}") + if(${IS_RASPBERRY} EQUAL -1) + set (IS_RASPBERRY_PI OFF) + else() + set (IS_RASPBERRY_PI ON) + endif() + message(STATUS "Check if Raspberry Pi: ${IS_RASPBERRY_PI}") else() message(WARNING "Model info not exists. Assuming that it is Raspberry Pi") - SET (IS_RASPBERRY 0) + SET (IS_RASPBERRY_PI ON) endif() - if(${IS_RASPBERRY} EQUAL -1) - message(STATUS "This is not Raspberry Pi. Default compiler settings.") + if (IS_RASPBERRY_PI) + message(STATUS "Platform variant: Raspberry Pi") + SET ( DEFAULT_POWER_MANAGEMENT ON ) + SET ( DEFAULT_PIPEWIRE ON ) else() - message(STATUS "Default compiler settings for Raspberry Pi.") + message(STATUS "Platform variant: generic ARM") endif() elseif ( "${PLATFORM}" STREQUAL "amlogic" ) @@ -236,13 +244,19 @@ if (DEFAULT_FRAMEBUFFER) endif() endif() -if (DEFAULT_PIPEWIRE) - find_package(Qt${Qt_VERSION} COMPONENTS DBus QUIET ) +find_package(Qt${Qt_VERSION} COMPONENTS DBus QUIET ) + +if (UNIX AND NOT APPLE AND NOT Qt${Qt_VERSION}DBus_FOUND AND DEFAULT_POWER_MANAGEMENT) + message( WARNING "QT dbus library is required for Power Management support" ) + SET ( DEFAULT_POWER_MANAGEMENT OFF ) +endif() + +if (DEFAULT_PIPEWIRE OR ENABLE_PIPEWIRE) if (NOT Qt${Qt_VERSION}DBus_FOUND) message( WARNING "QT dbus library is required for PipeWire/Portal support" ) SET ( DEFAULT_PIPEWIRE OFF ) else() - + FIND_PACKAGE(PkgConfig REQUIRED) pkg_check_modules(PIPEWIRE libpipewire-0.3) if(NOT PIPEWIRE_FOUND OR NOT PIPEWIRE_INCLUDE_DIRS OR NOT PIPEWIRE_LIBRARIES) message( WARNING "Pipewire >= 3.0 not found (did you install libpipewire-0.3-dev?). Disabling support for PipeWire software grabber.") @@ -271,10 +285,11 @@ elseif(DEFAULT_CEC) endif() if(DEFAULT_CEC) - IF ((NOT EXISTS "/opt/vc/lib" OR NOT EXISTS "/opt/vc/include") AND - (NOT EXISTS "/usr/lib/arm-linux-gnueabihf/libvchostif.a" OR NOT EXISTS "/usr/include/vcinclude/vcore.h") AND - (NOT EXISTS "/usr/lib/aarch64-linux-gnu/libbcm_host.so.0" OR NOT EXISTS "/usr/include/interface/vchiq_arm/vchiq_if.h")) - message( WARNING "CEC Rpi mode support could be disabled. Could not find Rpi developers libs: arm32 > /opt/vc/lib,/opt/vc/include,/usr/lib/arm-linux-gnueabihf,/usr/include/vcinclude, aarch64 > /usr/lib/aarch64-linux-gnu/libbcm_host.so.0,/usr/include/interface/vchiq_arm/vchiq_if.h" ) + FIND_PACKAGE(PkgConfig REQUIRED) + pkg_check_modules (CEC libcec>=6.0.0) + if(NOT CEC_FOUND) + message( WARNING "Could not find: libcec>=6. Disabling CEC support." ) + SET ( DEFAULT_CEC OFF ) endif() endif() @@ -383,6 +398,9 @@ colorMe("ENABLE_CEC = " ${ENABLE_CEC}) option(ENABLE_MQTT "Enable MQTT" ${DEFAULT_MQTT}) colorMe("ENABLE_MQTT = " ${ENABLE_MQTT}) +option(ENABLE_POWER_MANAGEMENT "Enable Power Management support" ${DEFAULT_POWER_MANAGEMENT}) +colorMe("ENABLE_POWER_MANAGEMENT = " ${ENABLE_POWER_MANAGEMENT}) + option(ENABLE_PROTOBUF "Enable PROTOBUF" ${DEFAULT_PROTOBUF}) colorMe("ENABLE_PROTOBUF = " ${ENABLE_PROTOBUF}) @@ -439,7 +457,6 @@ file(MAKE_DIRECTORY ${LIBRARY_OUTPUT_PATH}) file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) # Add the project include directory as additional include path -include_directories(${CMAKE_SOURCE_DIR}/dependencies/include) include_directories(${CMAKE_SOURCE_DIR}/include) # enable C++11; MSVC doesn't have c++11 feature switch @@ -460,6 +477,13 @@ if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC") else() message(STATUS "No support for C++11 detected. Compilation will most likely fail on your compiler") endif() +else() + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("/std:c++20" COMPILER_SUPPORTS_CXX20) + if (COMPILER_SUPPORTS_CXX20) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20") + message(STATUS "Enabling MSVC support for c++20") + endif() endif() # Use GNU gold linker if available @@ -523,51 +547,49 @@ endif() add_definitions(${QT_DEFINITIONS}) # libjpegturbo -if ( ENABLE_V4L2 OR ENABLE_MF ) - if(WIN32) - if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows) - file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows) - endif() +if(WIN32) + if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external/windows) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external/windows) + endif() - if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libs4windows.zip OR - NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libjpeg-turbo_x64-windows) - message( STATUS "Downloading libraries for HyperHDR (Windows)") - file(DOWNLOAD https://github.com/awawa-dev/HyperHDR.libs.provider/releases/download/2023.08.07/libs4windows.zip - ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libs4windows.zip - STATUS WIN_LIBS_DOWNLOAD_STATUS - EXPECTED_HASH SHA256=2a0fc14eaad35d8cc36b262c8dcb501d1fac3a72d7bedcca51b71b60395c6c3f) - list(GET WIN_LIBS_DOWNLOAD_STATUS 0 WIN_LIBS_DOWNLOAD_STATUS_CODE) - if(WIN_LIBS_DOWNLOAD_STATUS_CODE AND NOT WIN_LIBS_DOWNLOAD_STATUS_CODE EQUAL 0) - file( REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libs4windows.zip ) - message( FATAL_ERROR "Could not download libraries needed by HyperHDR (Windows)") - endif() + if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libs4windows.zip OR + NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libjpeg-turbo_x64-windows) + message( STATUS "Downloading libraries for HyperHDR (Windows)") + file(DOWNLOAD https://github.com/awawa-dev/HyperHDR.libs.provider/releases/download/2023.08.07/libs4windows.zip + ${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libs4windows.zip + STATUS WIN_LIBS_DOWNLOAD_STATUS + EXPECTED_HASH SHA256=2a0fc14eaad35d8cc36b262c8dcb501d1fac3a72d7bedcca51b71b60395c6c3f) + list(GET WIN_LIBS_DOWNLOAD_STATUS 0 WIN_LIBS_DOWNLOAD_STATUS_CODE) + if(WIN_LIBS_DOWNLOAD_STATUS_CODE AND NOT WIN_LIBS_DOWNLOAD_STATUS_CODE EQUAL 0) + file( REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libs4windows.zip ) + message( FATAL_ERROR "Could not download libraries needed by HyperHDR (Windows)") endif() + endif() - execute_process( - COMMAND ${SEVENZIP_BIN} x ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libs4windows.zip -o${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/ -aoa -y - RESULT_VARIABLE STATUS_EXTRACT - OUTPUT_VARIABLE OUTPUT1 - ) - - if(STATUS_EXTRACT AND NOT STATUS_EXTRACT EQUAL 0) - message( FATAL_ERROR "Could not extract libraries for HyperHDR (Windows)") - else() - message( STATUS "Package of libraries for HyperHDR extracted (Windows)") - endif() + execute_process( + COMMAND ${SEVENZIP_BIN} x ${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libs4windows.zip -o${CMAKE_CURRENT_SOURCE_DIR}/external/windows/ -aoa -y + RESULT_VARIABLE STATUS_EXTRACT + OUTPUT_VARIABLE OUTPUT1 + ) - set (TURBOJPEG_FOUND 1) - set (TURBOJPEG_LIBRARY_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libjpeg-turbo_x64-windows/bin") - set (TURBOJPEG_LINK_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libjpeg-turbo_x64-windows/lib/turbojpeg.lib") - set (TURBOJPEG_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/windows/libjpeg-turbo_x64-windows/include") + if(STATUS_EXTRACT AND NOT STATUS_EXTRACT EQUAL 0) + message( FATAL_ERROR "Could not extract libraries for HyperHDR (Windows)") else() - FIND_PACKAGE(PkgConfig REQUIRED) - pkg_check_modules(TURBOJPEG REQUIRED libturbojpeg>=2.0) - endif () + message( STATUS "Package of libraries for HyperHDR extracted (Windows)") + endif() + + set (TURBOJPEG_FOUND 1) + set (TURBOJPEG_LIBRARY_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libjpeg-turbo_x64-windows/bin") + set (TURBOJPEG_LINK_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libjpeg-turbo_x64-windows/lib/turbojpeg.lib") + set (TURBOJPEG_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/windows/libjpeg-turbo_x64-windows/include") +else() + FIND_PACKAGE(PkgConfig REQUIRED) + pkg_check_modules(TURBOJPEG REQUIRED libturbojpeg>=2.0) +endif () - message( STATUS "TURBOJPEG_LIBRARY_DIRS=${TURBOJPEG_LIBRARY_DIRS}") - message( STATUS "TURBOJPEG_LINK_LIBRARIES=${TURBOJPEG_LINK_LIBRARIES}") - message( STATUS "TURBOJPEG_INCLUDE_DIRS=${TURBOJPEG_INCLUDE_DIRS}") -endif() +message( STATUS "TURBOJPEG_LIBRARY_DIRS=${TURBOJPEG_LIBRARY_DIRS}") +message( STATUS "TURBOJPEG_LINK_LIBRARIES=${TURBOJPEG_LINK_LIBRARIES}") +message( STATUS "TURBOJPEG_INCLUDE_DIRS=${TURBOJPEG_INCLUDE_DIRS}") # Embedded QT plugins if (USE_STATIC_QT_PLUGINS) @@ -589,7 +611,7 @@ if (USE_STATIC_QT_PLUGINS) endif() # Add the source/lib directories -add_subdirectory(dependencies) +add_subdirectory(external) add_subdirectory(sources) # Add resources directory @@ -610,8 +632,8 @@ add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_D include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/packages.cmake) # external targets -if (WIN32 AND TARGET precompiled_hyperhdr_headers AND TARGET apidoc AND TARGET flatbuffers AND TARGET flatc AND TARGET flathash AND TARGET mbedcrypto AND TARGET qmqtt AND TARGET liblzma) - set_target_properties(precompiled_hyperhdr_headers qmqtt apidoc flatbuffers flatc flathash lib mbedcrypto mbedtls mbedx509 resources uninstall liblzma PROPERTIES FOLDER ExternalLibsTargets) +if (WIN32 AND TARGET apidoc AND TARGET flatbuffers AND TARGET flatc AND TARGET mbedcrypto AND TARGET qmqtt AND TARGET liblzma) + set_target_properties(qmqtt apidoc flatbuffers flatc lib mbedcrypto mbedtls mbedx509 resources uninstall liblzma PROPERTIES FOLDER ExternalLibsTargets) else() set_target_properties(resources uninstall PROPERTIES FOLDER ExternalLibsTargets) endif() diff --git a/HyperhdrConfig.h.in b/HyperhdrConfig.h.in index ec57d1dff..d8587a705 100644 --- a/HyperhdrConfig.h.in +++ b/HyperhdrConfig.h.in @@ -72,6 +72,8 @@ // Define to enable system mbedtls #cmakedefine USE_SYSTEM_MBEDTLS_LIBS +#cmakedefine ENABLE_POWER_MANAGEMENT + // the hyperhdr build id string #define HYPERHDR_BUILD_ID "${HYPERHDR_BUILD_ID}" #define HYPERHDR_GIT_REMOTE "${HYPERHDR_GIT_REMOTE}" diff --git a/LICENSE b/LICENSE index 64dfeb969..18c8e0981 100644 --- a/LICENSE +++ b/LICENSE @@ -1,8 +1,6 @@ MIT License -Copyright (c) 2023 awawa-dev - -Project homesite: https://github.com/awawa-dev/HyperHDR +Copyright (c) 2020-2023 awawa-dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0b7e2ca74..0498336fc 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ Open source ambient lighting implementation for television and music sets based * High portability on various ARM embedded platforms * Video post-processing filter to eliminate LED flickering * Modern interface using Bootstrap 5 and SVG icons -* Support for USB grabbers under Linux, Windows 10, macOS (x64/M1) * Provides vital informations about your OS condition: CPU & RAM usage, CPU temperature, undervoltage detection, internal components performance including USB grabber and LED devices +* Support for USB grabbers under Linux, Windows 10, macOS (x64/M1) +* Pipewire/Portal hardware-accelerated screen capturer for Linux/Wayland +* DirectX screen grabber with pixel and vertex shader processing acceleration for Windows 10/11 * Dynamic video cache buffers. Now Rpi can process even 1080p120 NV12 stream without any size decimation * Built-in audio visualization effects using **spectrum analysis** * MQTT support for IoT diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 67b8365b7..5cadda42f 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -263,29 +263,7 @@ macro(DeployUnix TARGET) get_filename_component(file_canonical ${resolved_file} REALPATH) gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) message(STATUS "Added smartPipewire(2): ${file_canonical}") - endif() - - # Copy CEC lib - find_library(LIBSMARTCEC - NAMES "cec" "cec.so" - PATHS "${CMAKE_BINARY_DIR}/lib" - NO_DEFAULT_PATH - ) - if (LIBSMARTCEC) - SET(resolved_file ${LIBSMARTCEC}) - get_filename_component(resolved_file ${resolved_file} ABSOLUTE) - gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) - message(STATUS "Adding CEC: ${resolved_file}") - set(resolved_file "${resolved_file}.6") - if(EXISTS ${resolved_file}) - get_filename_component(resolved_file ${resolved_file} ABSOLUTE) - gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) - message(STATUS "Adding CEC(2): ${resolved_file}") - endif() - get_filename_component(file_canonical ${resolved_file} REALPATH) - gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) - message(STATUS "Added CEC(3): ${file_canonical}") - endif() + endif() #OpenSSL find_package(OpenSSL) @@ -318,23 +296,44 @@ macro(DeployUnix TARGET) message(STATUS "QT plugin path: ${QT_PLUGINS_DIR}") - if ( CEC_SUPPORT ) - SET(resolved_file ${CEC_SUPPORT}) - message(STATUS "Adding CEC support: ${resolved_file}") - get_filename_component(resolved_file ${resolved_file} ABSOLUTE) - gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) - message(STATUS "Added CEC support: ${resolved_file}") - set(resolved_file2 "${resolved_file}.2") - if(EXISTS ${resolved_file2}) - message(STATUS "Adding CEC2: ${resolved_file2}") - get_filename_component(resolved_file2 ${resolved_file2} ABSOLUTE) - gp_append_unique(PREREQUISITE_LIBS ${resolved_file2}) - message(STATUS "Added CEC2: ${resolved_file2}") + # Copy CEC lib + if (CEC_FOUND) + + find_library(XRANDR_LIBRARY NAMES Xrandr libXrandr libXrandr.so.2) + + if (XRANDR_LIBRARY) + SET(resolved_file ${XRANDR_LIBRARY}) + get_filename_component(resolved_file ${resolved_file} ABSOLUTE) + gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) + message(STATUS "Adding xrandr: ${resolved_file}") + get_filename_component(file_canonical ${resolved_file} REALPATH) + gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) + message(STATUS "Added xrandr(2): ${file_canonical}") endif() - get_filename_component(file_canonical ${resolved_file} REALPATH) - gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) - message(STATUS "Added CEC support: ${file_canonical}") - endif() + + foreach(resolved_file_in ${CEC_LIBRARIES}) + message(STATUS "Adding CEC: ${resolved_file_in}") + unset(LIBCEC CACHE) + find_library(LIBCEC NAMES ${resolved_file_in}) + if (LIBCEC) + SET(resolved_file ${LIBCEC}) + get_filename_component(resolved_file ${resolved_file} ABSOLUTE) + gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) + message(STATUS "Adding CEC(1): ${resolved_file}") + get_filename_component(file_canonical ${resolved_file} REALPATH) + gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) + message(STATUS "Added CEC(2): ${file_canonical}") + foreach(indexer RANGE 9) + set(resolved_fileLink "${resolved_file}.${indexer}") + if(EXISTS ${resolved_fileLink}) + get_filename_component(resolved_fileLink ${resolved_fileLink} ABSOLUTE) + gp_append_unique(PREREQUISITE_LIBS ${resolved_fileLink}) + message(STATUS "Adding CEC(3): ${resolved_fileLink}") + endif() + endforeach() + endif() + endforeach() + endif() if ( GLD ) SET(resolved_file ${GLD}) diff --git a/cmake/arch/INSTALL b/cmake/arch/INSTALL index c07af00f6..d816b6658 100644 --- a/cmake/arch/INSTALL +++ b/cmake/arch/INSTALL @@ -1,5 +1,5 @@ pre_install() { - + echo "Preparing to install HyperHDR..." # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" @@ -9,18 +9,18 @@ pre_install() { then if grep -m1 systemd /proc/1/comm > /dev/null then - echo "--> stop init deamon: systemd" + echo "Stopping HyperHDR daemon: systemd" # systemd systemctl stop hyperhdr"@${FOUND_USR}" 2> /dev/null elif [ -e /usr/bin/initctl ] then - echo "--> stop init deamon: upstart" + echo "Stopping HyperHDR daemon: upstart" # upstart initctl stop hyperhdr else - echo "--> stop init deamon: sysV" + echo "Stopping HyperHDR daemon: sysV" # sysV service hyperhdr stop 2>/dev/null fi @@ -46,7 +46,7 @@ post_install() { cp "$src" "${dest}" return 1 else - echo "--> Service file already exists, skip creation" + echo "Service file already exists, skip creation" return 0 fi } @@ -69,11 +69,11 @@ post_install() { # determine if we should use a service ENABLE_XSYS=0 ENABLE_SERVICE=0 - STARTUP_MSG="echo ---> You can start HyperHDR from your menu now" + STARTUP_MSG="Service is NOT enabled by default for GUI or non-systemd OS" if [ $CPU_RPI -eq 1 ]; then ENABLE_SERVICE=1 - STARTUP_MSG="echo ---> HyperHDR has been installed as service, it will start on each system startup" + STARTUP_MSG="HyperHDR is installed as a service and starts automatically" fi start_msg="" @@ -87,11 +87,10 @@ post_install() { then # systemd if [ -z "${DISPLAY}" ]; then - echo "---> Init deamon: systemd (service is enabled by default for console systems)" + echo "HyperHDR is installed as a service and starts automatically" ENABLE_SERVICE=1 else - STARTUP_MSG="echo ---> Service is NOT enabled by default for GUI systems. You can run HyperHDR as an application or service with: 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service'" - echo "---> Init deamon: GUI detected (service is NOT enabled by default, you can run HyperHDR as an application or service with 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service')" + echo "Init daemon: GUI detected (service is NOT enabled by default, you can run HyperHDR as an application or service with 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service')" ENABLE_SERVICE=0 ENABLE_XSYS=1 fi @@ -100,7 +99,7 @@ post_install() { if [ $ENABLE_SERVICE -eq 1 ]; then systemctl enable hyperhdr"@${FOUND_USR}".service - start_msg="--> systemctl start hyperhdr for user ${FOUND_USR}" + start_msg="systemctl start hyperhdr for user ${FOUND_USR}" systemctl start hyperhdr"@${FOUND_USR}" fi @@ -108,23 +107,23 @@ post_install() { then # upstart if [ $ENABLE_SERVICE -eq 1 ]; then - echo "---> Init deamon: upstart (service is enabled by default)" + echo "Init daemon: upstart (service is enabled by default)" install_file /usr/share/hyperhdr/service/hyperhdr.initctl /etc/init/hyperhdr.conf && initctl reload-configuration - start_msg="--> initctl start hyperhdr" + start_msg="initctl start hyperhdr" initctl start hyperhdr else - echo "---> Init deamon: upstart (service is NOT enabled by default)" + echo "Init daemon: upstart (service is NOT enabled by default)" fi else # sysV if [ $ENABLE_SERVICE -eq 1 ]; then - echo "---> Init deamon: sysV (enabled by default)" + echo "Init daemon: sysV (enabled by default)" install_file /usr/share/hyperhdr/service/hyperhdr.init /etc/init.d/hyperhdr && chmod +x /etc/init.d/hyperhdr && update-rc.d hyperhdr defaults 98 02 - start_msg="---> service hyperhdr start" + start_msg="service hyperhdr start" service hyperhdr start else - echo "---> Init deamon: sysV (service is NOT enabled by default)" + echo "Init daemon: sysV (service is NOT enabled by default)" fi fi @@ -139,7 +138,7 @@ post_install() { ln -fs $BINSP/hyperhdr-remote $BINTP/hyperhdr-remote # install desktop icons - echo "---> Install HyperHdr desktop icons" + echo "Install HyperHDR desktop icons" mkdir /usr/share/pixmaps/hyperhdr 2>/dev/null cp /usr/share/hyperhdr/desktop/*.png /usr/share/pixmaps/hyperhdr 2>/dev/null desktop-file-install /usr/share/hyperhdr/desktop/hyperhdr.desktop 2>/dev/null @@ -154,30 +153,39 @@ post_install() { BOOT_DIR=$(sed -ne "s#/dev/mmcblk0p1 \([^ ]*\) vfat rw,.*#\1#p" /etc/mtab) fi if [ -z "$BOOT_DIR" -o ! -f "$BOOT_DIR/config.txt" ]; then - echo '---> Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)" + echo 'Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)" SPIOK=1 # Not sure if OK, but don't ask to reboot else SPIOK=`grep '^\dtparam=spi=on' "$BOOT_DIR/config.txt" | wc -l` if [ $SPIOK -ne 1 ]; then - echo '---> Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt" + echo 'Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt" sed -i '$a dtparam=spi=on' "$BOOT_DIR/config.txt" - REBOOTMESSAGE="echo Please reboot your Raspberry Pi, we inserted dtparam=spi=on to $BOOT_DIR/config.txt" + REBOOTMESSAGE="Restart Raspberry Pi, SPI has been enabled in $BOOT_DIR/config.txt" fi fi fi echo ${start_msg} - echo "-----------------------------------------------------------------------------" - echo "---> HyperHDR has been installed/updated!" - echo "---> Please check log above to verify if the service was automaticly enabled" - $STARTUP_MSG - echo "---> For configuration, visit with your browser: ${NET_IP}:8090" - echo "---> or if already used by another service try: ${NET_IP}:8091" - $REBOOTMESSAGE - echo "-----------------------------------------------------------------------------" - echo "Webpage: https://github.com/awawa-dev/HyperHDR" - echo "-----------------------------------------------------------------------------" +echo " +-----------------------------------------------------------------------+" +echo " | \033[32;1mHyperHDR has been installed/updated!\033[0m |" +echo " +-----------------------------------------------------------------------+" +printf " | For configuration, visit with your browser: \033[37;1m%13s:%s\033[0m |\n" "$NET_IP" "8090" +printf " | If already used by another service try: \033[37;1m%13s:%s\033[0m |\n" "$NET_IP" "8091" +printf " | Start the service: \033[37;1msudo systemctl start hyperhdr@%-12s\033[0m |\n" "$FOUND_USR" +printf " | Stop the service: \033[37;1msudo systemctl stop hyperhdr@%-12s\033[0m |\n" "$FOUND_USR" +echo " | Troubleshooting? Run HyperHDR manually: \033[37;1m/usr/bin/hyperhdr\033[0m |" +echo " +-----------------------------------------------------------------------+" +case "$STARTUP_MSG" in + *"HyperHDR is installed as a service"*) + printf " | \033[32;1m%-67s\033[0m |\n" "$STARTUP_MSG";; + *) printf " | \033[31;1m%-67s\033[0m |\n" "$STARTUP_MSG" +esac +[ -z "$REBOOTMESSAGE" ] || printf " | \033[31;1m%-67s\033[0m |\n" "$REBOOTMESSAGE" +echo " +-----------------------------------------------------------------------+" +echo " | Webpage: \033[36;1mhttps://hyperhdr.eu\033[0m |" +echo " | GitHub: \033[36;1mhttps://github.com/awawa-dev/HyperHDR\033[0m |" +echo " +-----------------------------------------------------------------------+" update-desktop-database -q } @@ -191,7 +199,7 @@ post_upgrade() { } pre_remove() { - + echo "Preparing to uninstall HyperHDR" # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" @@ -202,29 +210,29 @@ pre_remove() { if grep -m1 systemd /proc/1/comm > /dev/null then - echo "---> stop init deamon: systemd" + echo "Stopping HyperHDR daemon: systemd" # systemd $HYPERHDR_RUNNING && systemctl stop hyperhdr"@${FOUND_USR}" 2> /dev/null # disable user specific symlink - echo "---> Disable service and remove entry" + echo "Disabling service and removing entry" systemctl -q disable hyperhdr"@${FOUND_USR}" rm -v /etc/systemd/system/hyperhdr@.service 2>/dev/null elif [ -e /usr/bin/initctl ] then - echo "---> stop init deamon: upstart" + echo "Stopping HyperHDR daemon: upstart" # upstart $HYPERHDR_RUNNING && initctl stop hyperhdr - echo "---> Remove upstart service" + echo "Remove upstart service" rm -v /etc/init/hyperhdr* 2>/dev/null initctl reload-configuration else - echo "---> stop init deamon: sysV" + echo "Stopping HyperHDR daemon: sysV" # sysV $HYPERHDR_RUNNING && service hyperhdr stop 2>/dev/null - echo "---> Remove sysV service" + echo "Remove sysV service" update-rc.d -f hyperhdr remove rm /etc/init.d/hyperhdr* 2>/dev/null fi @@ -234,12 +242,12 @@ pre_remove() { # delete desktop icons; desktop-file-edit is a workaround to hide the entry and delete it afterwards manual. # TODO Better way for deletion and keep the desktop in sync without logout/login or desktop dependend cmds? - echo "---> Delete HyperHdr desktop icons" + echo "Deleting HyperHDR desktop icons" desktop-file-edit --set-key=NoDisplay --set-value=true /usr/share/applications/hyperhdr.desktop 2>/dev/null rm -v /usr/share/applications/hyperhdr* 2>/dev/null rm -rv /usr/share/pixmaps/hyperhdr 2>/dev/null - echo "Removing LUT table..." + echo "Removing LUT table" rm -f -v /usr/share/hyperhdr/lut/lut_lin_tables.3d exit 0 } diff --git a/cmake/debian/postinst b/cmake/debian/postinst index 5baf0404f..29222597d 100644 --- a/cmake/debian/postinst +++ b/cmake/debian/postinst @@ -10,13 +10,13 @@ install_file() cp "$src" "${dest}" return 1 else - echo "--> Service file already exists, skip creation" + echo "Service file already exists, skip creation" return 0 fi } -echo "---HyperHDR ambient light postinstall ---" +echo "Finalizing HyperHDR setup" #check system CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835\|BCM2836\|BCM2837\|BCM2711' /proc/cpuinfo` @@ -34,11 +34,11 @@ FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" # determine if we should use a service ENABLE_XSYS=0 ENABLE_SERVICE=0 -STARTUP_MSG="echo ---> You can start HyperHDR from your menu now" +STARTUP_MSG="Service is NOT enabled by default for GUI or non-systemd OS" if [ $CPU_RPI -eq 1 ]; then ENABLE_SERVICE=1 - STARTUP_MSG="echo ---> HyperHDR has been installed as service, it will start on each system startup" + STARTUP_MSG="HyperHDR is installed as a service and starts automatically" fi start_msg="" @@ -52,11 +52,11 @@ if grep -m1 systemd /proc/1/comm > /dev/null then # systemd if [ -z "${DISPLAY}" ]; then - echo "---> Init deamon: systemd (service is enabled by default for console systems)" + STARTUP_MSG="HyperHDR is installed as a service and starts automatically" + echo "Init daemon: systemd (service is enabled by default for console systems)" ENABLE_SERVICE=1 else - STARTUP_MSG="echo ---> Service is NOT enabled by default for GUI systems. You can run HyperHDR as an application or service with: 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service'" - echo "---> Init deamon: GUI detected (service is NOT enabled by default, you can run HyperHDR as an application or service with 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service')" + echo "Init daemon: GUI detected (service is NOT enabled by default, you can run HyperHDR as an application or service with 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service')" ENABLE_SERVICE=0 ENABLE_XSYS=1 fi @@ -65,7 +65,7 @@ then if [ $ENABLE_SERVICE -eq 1 ]; then systemctl enable hyperhdr"@${FOUND_USR}".service - start_msg="--> systemctl start hyperhdr for user ${FOUND_USR}" + start_msg="systemctl start hyperhdr for user ${FOUND_USR}" systemctl start hyperhdr"@${FOUND_USR}" fi @@ -73,23 +73,23 @@ elif [ -e /sbin/initctl ] then # upstart if [ $ENABLE_SERVICE -eq 1 ]; then - echo "---> Init deamon: upstart (service is enabled by default)" + echo "Init daemon: upstart (service is enabled by default)" install_file /usr/share/hyperhdr/service/hyperhdr.initctl /etc/init/hyperhdr.conf && initctl reload-configuration - start_msg="--> initctl start hyperhdr" + start_msg="initctl start hyperhdr" initctl start hyperhdr else - echo "---> Init deamon: upstart (service is NOT enabled by default)" + echo "Init daemon: upstart (service is NOT enabled by default)" fi else # sysV if [ $ENABLE_SERVICE -eq 1 ]; then - echo "---> Init deamon: sysV (enabled by default)" + echo "Init daemon: sysV (enabled by default)" install_file /usr/share/hyperhdr/service/hyperhdr.init /etc/init.d/hyperhdr && chmod +x /etc/init.d/hyperhdr && update-rc.d hyperhdr defaults 98 02 - start_msg="---> service hyperhdr start" + start_msg="service hyperhdr start" service hyperhdr start else - echo "---> Init deamon: sysV (service is NOT enabled by default)" + echo "Init daemon: sysV (service is NOT enabled by default)" fi fi @@ -104,7 +104,7 @@ ln -fs $BINSP/hyperhdr $BINTP/hyperhdr ln -fs $BINSP/hyperhdr-remote $BINTP/hyperhdr-remote # install desktop icons -echo "---> Install HyperHdr desktop icons" +echo "Install HyperHDR desktop icons" mkdir /usr/share/pixmaps/hyperhdr 2>/dev/null cp /usr/share/hyperhdr/desktop/*.png /usr/share/pixmaps/hyperhdr 2>/dev/null desktop-file-install /usr/share/hyperhdr/desktop/hyperhdr.desktop 2>/dev/null @@ -119,29 +119,38 @@ if [ $CPU_RPI -eq 1 ]; then BOOT_DIR=$(sed -ne "s#/dev/mmcblk0p1 \([^ ]*\) vfat rw,.*#\1#p" /etc/mtab) fi if [ -z "$BOOT_DIR" -o ! -f "$BOOT_DIR/config.txt" ]; then - echo '---> Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)" + echo 'Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)" SPIOK=1 # Not sure if OK, but don't ask to reboot else SPIOK=`grep '^\dtparam=spi=on' "$BOOT_DIR/config.txt" | wc -l` if [ $SPIOK -ne 1 ]; then - echo '---> Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt" + echo 'Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt" sed -i '$a dtparam=spi=on' "$BOOT_DIR/config.txt" - REBOOTMESSAGE="echo Please reboot your Raspberry Pi, we inserted dtparam=spi=on to $BOOT_DIR/config.txt" + REBOOTMESSAGE="Restart Raspberry Pi, SPI has been enabled in $BOOT_DIR/config.txt" fi fi fi echo ${start_msg} -echo "-----------------------------------------------------------------------------" -echo "---> HyperHDR has been installed/updated!" -echo "---> Please check log above to verify if the service was automaticly enabled" -$STARTUP_MSG -echo "---> For configuration, visit with your browser: ${NET_IP}:8090" -echo "---> or if already used by another service try: ${NET_IP}:8091" -$REBOOTMESSAGE -echo "-----------------------------------------------------------------------------" -echo "Webpage: https://github.com/awawa-dev/HyperHDR" -echo "-----------------------------------------------------------------------------" +echo "\n +-----------------------------------------------------------------------+" +echo " | \033[32;1mHyperHDR has been installed/updated!\033[0m |" +echo " +-----------------------------------------------------------------------+" +printf " | For configuration, visit with your browser: \033[37;1m%13s:%s\033[0m |\n" "$NET_IP" "8090" +printf " | If already used by another service try: \033[37;1m%13s:%s\033[0m |\n" "$NET_IP" "8091" +printf " | Start the service: \033[37;1msudo systemctl start hyperhdr@%-12s\033[0m |\n" "$FOUND_USR" +printf " | Stop the service: \033[37;1msudo systemctl stop hyperhdr@%-12s\033[0m |\n" "$FOUND_USR" +echo " | Troubleshooting? Run HyperHDR manually: \033[37;1m/usr/bin/hyperhdr\033[0m |" +echo " +-----------------------------------------------------------------------+" +case "$STARTUP_MSG" in + *"HyperHDR is installed as a service"*) + printf " | \033[32;1m%-67s\033[0m |\n" "$STARTUP_MSG";; + *) printf " | \033[31;1m%-67s\033[0m |\n" "$STARTUP_MSG" +esac +[ -z "$REBOOTMESSAGE" ] || printf " | \033[31;1m%-67s\033[0m |\n" "$REBOOTMESSAGE" +echo " +-----------------------------------------------------------------------+" +echo " | Webpage: \033[36;1mhttps://hyperhdr.eu\033[0m |" +echo " | GitHub: \033[36;1mhttps://github.com/awawa-dev/HyperHDR\033[0m |" +echo " +-----------------------------------------------------------------------+\n" exit 0 diff --git a/cmake/debian/preinst b/cmake/debian/preinst index 2dac31048..32e9140f5 100644 --- a/cmake/debian/preinst +++ b/cmake/debian/preinst @@ -1,6 +1,6 @@ #!/bin/sh -echo "---HyperHdr ambient light preinst ---" +echo "Preparing to install HyperHDR..." # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" @@ -10,18 +10,18 @@ if pgrep hyperhdr > /dev/null 2>&1 then if grep -m1 systemd /proc/1/comm > /dev/null then - echo "--> stop init deamon: systemd" + echo "Stopping HyperHDR daemon: systemd" # systemd systemctl stop hyperhdr"@${FOUND_USR}" 2> /dev/null elif [ -e /sbin/initctl ] then - echo "--> stop init deamon: upstart" + echo "Stopping HyperHDR daemon: upstart" # upstart initctl stop hyperhdr else - echo "--> stop init deamon: sysV" + echo "Stopping HyperHDR daemon: sysV" # sysV service hyperhdr stop 2>/dev/null fi diff --git a/cmake/debian/prerm b/cmake/debian/prerm index d26d22558..80d891567 100644 --- a/cmake/debian/prerm +++ b/cmake/debian/prerm @@ -1,6 +1,6 @@ #!/bin/sh -echo "---HyperHdr ambient light prerm ---" +echo "Preparing to uninstall HyperHDR" # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" @@ -11,29 +11,29 @@ pgrep hyperhdr > /dev/null 2>&1 && HYPERHDR_RUNNING=true if grep -m1 systemd /proc/1/comm > /dev/null then - echo "---> stop init deamon: systemd" + echo "Stopping HyperHDR daemon: systemd" # systemd $HYPERHDR_RUNNING && systemctl stop hyperhdr"@${FOUND_USR}" 2> /dev/null # disable user specific symlink - echo "---> Disable service and remove entry" + echo "Disabling service and removing entry" systemctl -q disable hyperhdr"@${FOUND_USR}" rm -v /etc/systemd/system/hyperhdr@.service 2>/dev/null elif [ -e /sbin/initctl ] then - echo "---> stop init deamon: upstart" + echo "Stopping HyperHDR daemon: upstart" # upstart $HYPERHDR_RUNNING && initctl stop hyperhdr - echo "---> Remove upstart service" + echo "Removing upstart service" rm -v /etc/init/hyperhdr* 2>/dev/null initctl reload-configuration else - echo "---> stop init deamon: sysV" + echo "Stopping HyperHDR daemon: sysV" # sysV $HYPERHDR_RUNNING && service hyperhdr stop 2>/dev/null - echo "---> Remove sysV service" + echo "Removing sysV service" update-rc.d -f hyperhdr remove rm /etc/init.d/hyperhdr* 2>/dev/null fi @@ -43,11 +43,11 @@ killall hyperhdr 2> /dev/null # delete desktop icons; desktop-file-edit is a workaround to hide the entry and delete it afterwards manual. # TODO Better way for deletion and keep the desktop in sync without logout/login or desktop dependend cmds? -echo "---> Delete HyperHdr desktop icons" +echo "Deleting HyperHDR desktop icons" desktop-file-edit --set-key=NoDisplay --set-value=true /usr/share/applications/hyperhdr.desktop 2>/dev/null rm -v /usr/share/applications/hyperhdr* 2>/dev/null rm -rv /usr/share/pixmaps/hyperhdr 2>/dev/null -echo "Removing LUT table..." +echo "Removing LUT table" rm -f -v /usr/share/hyperhdr/lut/lut_lin_tables.3d exit 0 diff --git a/cmake/rpm/postinst b/cmake/rpm/postinst index 5baf0404f..dafbaba10 100644 --- a/cmake/rpm/postinst +++ b/cmake/rpm/postinst @@ -10,13 +10,13 @@ install_file() cp "$src" "${dest}" return 1 else - echo "--> Service file already exists, skip creation" + echo "Service file already exists, skip creation" return 0 fi } -echo "---HyperHDR ambient light postinstall ---" +echo "Finalizing HyperHDR setup" #check system CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835\|BCM2836\|BCM2837\|BCM2711' /proc/cpuinfo` @@ -34,11 +34,11 @@ FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" # determine if we should use a service ENABLE_XSYS=0 ENABLE_SERVICE=0 -STARTUP_MSG="echo ---> You can start HyperHDR from your menu now" +STARTUP_MSG="Service is NOT enabled by default for GUI or non-systemd OS" if [ $CPU_RPI -eq 1 ]; then ENABLE_SERVICE=1 - STARTUP_MSG="echo ---> HyperHDR has been installed as service, it will start on each system startup" + STARTUP_MSG="HyperHDR is installed as a service and starts automatically" fi start_msg="" @@ -52,11 +52,10 @@ if grep -m1 systemd /proc/1/comm > /dev/null then # systemd if [ -z "${DISPLAY}" ]; then - echo "---> Init deamon: systemd (service is enabled by default for console systems)" + echo "HyperHDR is installed as a service and starts automatically" ENABLE_SERVICE=1 else - STARTUP_MSG="echo ---> Service is NOT enabled by default for GUI systems. You can run HyperHDR as an application or service with: 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service'" - echo "---> Init deamon: GUI detected (service is NOT enabled by default, you can run HyperHDR as an application or service with 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service')" + echo "Init daemon: GUI detected (service is NOT enabled by default, you can run HyperHDR as an application or service with 'systemctl enable (and next: start) hyperhdr@${FOUND_USR}.service')" ENABLE_SERVICE=0 ENABLE_XSYS=1 fi @@ -65,7 +64,7 @@ then if [ $ENABLE_SERVICE -eq 1 ]; then systemctl enable hyperhdr"@${FOUND_USR}".service - start_msg="--> systemctl start hyperhdr for user ${FOUND_USR}" + start_msg="systemctl start hyperhdr for user ${FOUND_USR}" systemctl start hyperhdr"@${FOUND_USR}" fi @@ -73,23 +72,23 @@ elif [ -e /sbin/initctl ] then # upstart if [ $ENABLE_SERVICE -eq 1 ]; then - echo "---> Init deamon: upstart (service is enabled by default)" + echo "Init daemon: upstart (service is enabled by default)" install_file /usr/share/hyperhdr/service/hyperhdr.initctl /etc/init/hyperhdr.conf && initctl reload-configuration - start_msg="--> initctl start hyperhdr" + start_msg="initctl start hyperhdr" initctl start hyperhdr else - echo "---> Init deamon: upstart (service is NOT enabled by default)" + echo "Init daemon: upstart (service is NOT enabled by default)" fi else # sysV if [ $ENABLE_SERVICE -eq 1 ]; then - echo "---> Init deamon: sysV (enabled by default)" + echo "Init daemon: sysV (enabled by default)" install_file /usr/share/hyperhdr/service/hyperhdr.init /etc/init.d/hyperhdr && chmod +x /etc/init.d/hyperhdr && update-rc.d hyperhdr defaults 98 02 - start_msg="---> service hyperhdr start" + start_msg="service hyperhdr start" service hyperhdr start else - echo "---> Init deamon: sysV (service is NOT enabled by default)" + echo "Init daemon: sysV (service is NOT enabled by default)" fi fi @@ -104,7 +103,7 @@ ln -fs $BINSP/hyperhdr $BINTP/hyperhdr ln -fs $BINSP/hyperhdr-remote $BINTP/hyperhdr-remote # install desktop icons -echo "---> Install HyperHdr desktop icons" +echo "Install HyperHDR desktop icons" mkdir /usr/share/pixmaps/hyperhdr 2>/dev/null cp /usr/share/hyperhdr/desktop/*.png /usr/share/pixmaps/hyperhdr 2>/dev/null desktop-file-install /usr/share/hyperhdr/desktop/hyperhdr.desktop 2>/dev/null @@ -119,29 +118,38 @@ if [ $CPU_RPI -eq 1 ]; then BOOT_DIR=$(sed -ne "s#/dev/mmcblk0p1 \([^ ]*\) vfat rw,.*#\1#p" /etc/mtab) fi if [ -z "$BOOT_DIR" -o ! -f "$BOOT_DIR/config.txt" ]; then - echo '---> Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)" + echo 'Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)" SPIOK=1 # Not sure if OK, but don't ask to reboot else SPIOK=`grep '^\dtparam=spi=on' "$BOOT_DIR/config.txt" | wc -l` if [ $SPIOK -ne 1 ]; then - echo '---> Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt" + echo 'Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt" sed -i '$a dtparam=spi=on' "$BOOT_DIR/config.txt" - REBOOTMESSAGE="echo Please reboot your Raspberry Pi, we inserted dtparam=spi=on to $BOOT_DIR/config.txt" + REBOOTMESSAGE="Restart Raspberry Pi, SPI has been enabled in $BOOT_DIR/config.txt" fi fi fi echo ${start_msg} -echo "-----------------------------------------------------------------------------" -echo "---> HyperHDR has been installed/updated!" -echo "---> Please check log above to verify if the service was automaticly enabled" -$STARTUP_MSG -echo "---> For configuration, visit with your browser: ${NET_IP}:8090" -echo "---> or if already used by another service try: ${NET_IP}:8091" -$REBOOTMESSAGE -echo "-----------------------------------------------------------------------------" -echo "Webpage: https://github.com/awawa-dev/HyperHDR" -echo "-----------------------------------------------------------------------------" +echo " +-----------------------------------------------------------------------+" +echo " | \033[32;1mHyperHDR has been installed/updated!\033[0m |" +echo " +-----------------------------------------------------------------------+" +printf " | For configuration, visit with your browser: \033[37;1m%13s:%s\033[0m |\n" "$NET_IP" "8090" +printf " | If already used by another service try: \033[37;1m%13s:%s\033[0m |\n" "$NET_IP" "8091" +printf " | Start the service: \033[37;1msudo systemctl start hyperhdr@%-12s\033[0m |\n" "$FOUND_USR" +printf " | Stop the service: \033[37;1msudo systemctl stop hyperhdr@%-12s\033[0m |\n" "$FOUND_USR" +echo " | Troubleshooting? Run HyperHDR manually: \033[37;1m/usr/bin/hyperhdr\033[0m |" +echo " +-----------------------------------------------------------------------+" +case "$STARTUP_MSG" in + *"HyperHDR is installed as a service"*) + printf " | \033[32;1m%-67s\033[0m |\n" "$STARTUP_MSG";; + *) printf " | \033[31;1m%-67s\033[0m |\n" "$STARTUP_MSG" +esac +[ -z "$REBOOTMESSAGE" ] || printf " | \033[31;1m%-67s\033[0m |\n" "$REBOOTMESSAGE" +echo " +-----------------------------------------------------------------------+" +echo " | Webpage: \033[36;1mhttps://hyperhdr.eu\033[0m |" +echo " | GitHub: \033[36;1mhttps://github.com/awawa-dev/HyperHDR\033[0m |" +echo " +-----------------------------------------------------------------------+" exit 0 diff --git a/cmake/rpm/preinst b/cmake/rpm/preinst index 2dac31048..32e9140f5 100644 --- a/cmake/rpm/preinst +++ b/cmake/rpm/preinst @@ -1,6 +1,6 @@ #!/bin/sh -echo "---HyperHdr ambient light preinst ---" +echo "Preparing to install HyperHDR..." # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" @@ -10,18 +10,18 @@ if pgrep hyperhdr > /dev/null 2>&1 then if grep -m1 systemd /proc/1/comm > /dev/null then - echo "--> stop init deamon: systemd" + echo "Stopping HyperHDR daemon: systemd" # systemd systemctl stop hyperhdr"@${FOUND_USR}" 2> /dev/null elif [ -e /sbin/initctl ] then - echo "--> stop init deamon: upstart" + echo "Stopping HyperHDR daemon: upstart" # upstart initctl stop hyperhdr else - echo "--> stop init deamon: sysV" + echo "Stopping HyperHDR daemon: sysV" # sysV service hyperhdr stop 2>/dev/null fi diff --git a/cmake/rpm/prerm b/cmake/rpm/prerm index d26d22558..80d891567 100644 --- a/cmake/rpm/prerm +++ b/cmake/rpm/prerm @@ -1,6 +1,6 @@ #!/bin/sh -echo "---HyperHdr ambient light prerm ---" +echo "Preparing to uninstall HyperHDR" # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" @@ -11,29 +11,29 @@ pgrep hyperhdr > /dev/null 2>&1 && HYPERHDR_RUNNING=true if grep -m1 systemd /proc/1/comm > /dev/null then - echo "---> stop init deamon: systemd" + echo "Stopping HyperHDR daemon: systemd" # systemd $HYPERHDR_RUNNING && systemctl stop hyperhdr"@${FOUND_USR}" 2> /dev/null # disable user specific symlink - echo "---> Disable service and remove entry" + echo "Disabling service and removing entry" systemctl -q disable hyperhdr"@${FOUND_USR}" rm -v /etc/systemd/system/hyperhdr@.service 2>/dev/null elif [ -e /sbin/initctl ] then - echo "---> stop init deamon: upstart" + echo "Stopping HyperHDR daemon: upstart" # upstart $HYPERHDR_RUNNING && initctl stop hyperhdr - echo "---> Remove upstart service" + echo "Removing upstart service" rm -v /etc/init/hyperhdr* 2>/dev/null initctl reload-configuration else - echo "---> stop init deamon: sysV" + echo "Stopping HyperHDR daemon: sysV" # sysV $HYPERHDR_RUNNING && service hyperhdr stop 2>/dev/null - echo "---> Remove sysV service" + echo "Removing sysV service" update-rc.d -f hyperhdr remove rm /etc/init.d/hyperhdr* 2>/dev/null fi @@ -43,11 +43,11 @@ killall hyperhdr 2> /dev/null # delete desktop icons; desktop-file-edit is a workaround to hide the entry and delete it afterwards manual. # TODO Better way for deletion and keep the desktop in sync without logout/login or desktop dependend cmds? -echo "---> Delete HyperHdr desktop icons" +echo "Deleting HyperHDR desktop icons" desktop-file-edit --set-key=NoDisplay --set-value=true /usr/share/applications/hyperhdr.desktop 2>/dev/null rm -v /usr/share/applications/hyperhdr* 2>/dev/null rm -rv /usr/share/pixmaps/hyperhdr 2>/dev/null -echo "Removing LUT table..." +echo "Removing LUT table" rm -f -v /usr/share/hyperhdr/lut/lut_lin_tables.3d exit 0 diff --git a/dependencies/external/libcec b/dependencies/external/libcec deleted file mode 160000 index 1e3866018..000000000 --- a/dependencies/external/libcec +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1e3866018d69a1def03058a5810be54e40b5ce87 diff --git a/dependencies/CMakeLists.txt b/external/CMakeLists.txt similarity index 85% rename from dependencies/CMakeLists.txt rename to external/CMakeLists.txt index f60fd0d33..f0acf6fb1 100644 --- a/dependencies/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,13 +1,9 @@ -if ( ENABLE_CEC ) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/libcec) -ENDIF() - if(ENABLE_WS281XPWM) add_library(ws281x - ${CMAKE_CURRENT_SOURCE_DIR}/external/rpi_ws281x/mailbox.c ${CMAKE_CURRENT_SOURCE_DIR}/external/rpi_ws281x/ws2811.c - ${CMAKE_CURRENT_SOURCE_DIR}/external/rpi_ws281x/pwm.c ${CMAKE_CURRENT_SOURCE_DIR}/external/rpi_ws281x/dma.c - ${CMAKE_CURRENT_SOURCE_DIR}/external/rpi_ws281x/pcm.c - ${CMAKE_CURRENT_SOURCE_DIR}/external/rpi_ws281x/rpihw.c) + ${CMAKE_CURRENT_SOURCE_DIR}/rpi_ws281x/mailbox.c ${CMAKE_CURRENT_SOURCE_DIR}/rpi_ws281x/ws2811.c + ${CMAKE_CURRENT_SOURCE_DIR}/rpi_ws281x/pwm.c ${CMAKE_CURRENT_SOURCE_DIR}/rpi_ws281x/dma.c + ${CMAKE_CURRENT_SOURCE_DIR}/rpi_ws281x/pcm.c + ${CMAKE_CURRENT_SOURCE_DIR}/rpi_ws281x/rpihw.c) endif() #============================================================================= @@ -31,7 +27,12 @@ endif() if (NOT USE_SYSTEM_FLATBUFFERS_LIBS OR CMAKE_CROSSCOMPILING) set(CMAKE_POLICY_DEFAULT_CMP0071 NEW) - set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "") + set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "") + + if (NOT CMAKE_CROSSCOMPILING AND USE_PRECOMPILED_HEADERS) + set(FLATBUFFERS_ENABLE_PCH ON CACHE BOOL "") + set(FLATBUFFERS_SKIP_MONSTER_EXTRA ON CACHE BOOL "") + endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(FLATBUFFERS_CXX_FLAGS "-Wno-error") @@ -47,7 +48,7 @@ if (NOT USE_SYSTEM_FLATBUFFERS_LIBS OR CMAKE_CROSSCOMPILING) file(MAKE_DIRECTORY ${FLATBUFFERS_HOST_FLATBUFFERS_DIR}) EXECUTE_PROCESS ( WORKING_DIRECTORY ${FLATBUFFERS_HOST_FLATBUFFERS_DIR} RESULT_VARIABLE EXEC_RES - COMMAND ${CMAKE_COMMAND} -E env --unset=CC --unset=CXX --unset=CXXFLAGS --unset=CFLAGS ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/dependencies/external/flatbuffers -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF) + COMMAND ${CMAKE_COMMAND} -E env --unset=CC --unset=CXX --unset=CXXFLAGS --unset=CFLAGS ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/external/flatbuffers -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF) if (EXEC_RES AND NOT EXEC_RES EQUAL 0) message( FATAL_ERROR "Could not configure flatc compiler for the host") @@ -61,9 +62,9 @@ if (NOT USE_SYSTEM_FLATBUFFERS_LIBS OR CMAKE_CROSSCOMPILING) endif() endif() - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/flatbuffers) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/flatbuffers) - set(FLATBUFFERS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/flatbuffers/include") + set(FLATBUFFERS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/flatbuffers/include") IF (NOT CMAKE_CROSSCOMPILING) set(FLATBUFFERS_FLATC_EXECUTABLE "$") @@ -121,9 +122,9 @@ if (NOT USE_SYSTEM_MBEDTLS_LIBS) set(USE_SHARED_MBEDTLS_LIBRARY OFF CACHE BOOL "Disable mbedTLS shared libraries") set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "Enable mbedTLS static libraries") - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/mbedtls) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mbedtls) - set(MBEDTLS_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/dependencies/external/mbedtls/include") + set(MBEDTLS_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/external/mbedtls/include") set(MBEDTLS_INCLUDE_DIR ${MBEDTLS_INCLUDE_DIR} PARENT_SCOPE) if (MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") @@ -159,17 +160,17 @@ if ( ENABLE_MQTT ) if (NOT USE_SYSTEM_MQTT_LIBS) # HyperHDR workaround for fixed Qt5 version - file(READ "${CMAKE_CURRENT_SOURCE_DIR}/external/qmqtt/CMakeLists.txt" FILE_CONTENTS) + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/qmqtt/CMakeLists.txt" FILE_CONTENTS) string(REPLACE "Qt5" "Qt${Qt_VERSION}" FILE_CONTENTS "${FILE_CONTENTS}") string(REPLACE "find_package" "#find_package" FILE_CONTENTS "${FILE_CONTENTS}") - file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/external/qmqtt/CMakeLists.txt" "${FILE_CONTENTS}") + file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/qmqtt/CMakeLists.txt" "${FILE_CONTENTS}") if (NOT WIN32) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(qmqtt_SHARED OFF) endif() - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/qmqtt) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/qmqtt) endif() endif() @@ -182,7 +183,7 @@ if ( ENABLE_XZ ) string(REGEX REPLACE "(\/W[011123456789])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") message( STATUS "Disable warnings for xz library: ${CMAKE_CXX_FLAGS}") endif() - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/xz) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/xz) if(MSVC) set(CMAKE_CXX_FLAGS ${BACKUP_OF_CMAKE_CXX_FLAGS}) message( STATUS "Restoring compiling flags after xz: ${CMAKE_CXX_FLAGS}") diff --git a/dependencies/external/flatbuffers b/external/flatbuffers similarity index 100% rename from dependencies/external/flatbuffers rename to external/flatbuffers diff --git a/dependencies/external/mbedtls b/external/mbedtls similarity index 100% rename from dependencies/external/mbedtls rename to external/mbedtls diff --git a/dependencies/external/mdns b/external/mdns similarity index 100% rename from dependencies/external/mdns rename to external/mdns diff --git a/dependencies/external/qmqtt b/external/qmqtt similarity index 100% rename from dependencies/external/qmqtt rename to external/qmqtt diff --git a/dependencies/external/rpi_ws281x b/external/rpi_ws281x similarity index 100% rename from dependencies/external/rpi_ws281x rename to external/rpi_ws281x diff --git a/dependencies/external/xz b/external/xz similarity index 100% rename from dependencies/external/xz rename to external/xz diff --git a/include/api/API.h b/include/api/API.h deleted file mode 100644 index 93ee11de2..000000000 --- a/include/api/API.h +++ /dev/null @@ -1,428 +0,0 @@ -#pragma once - -#include -#include -#include - -class QNetworkReply; -class QTimer; -class JsonCB; - - -const QString NO_AUTH = "No Authorization"; - -const QString DEFAULT_CONFIG_USER = "Hyperhdr"; -const QString DEFAULT_CONFIG_PASSWORD = "hyperhdr"; - -/// -/// @brief API for Hyperhdr to be inherted from a child class with specific protocol implementations -/// Workflow: -/// 1. create the class -/// 2. connect the forceClose signal, as the api might to close the connection for security reasons -/// 3. call Initialize() -/// 4. proceed as usual -/// - -class API : public QObject -{ - Q_OBJECT - -public: - API(Logger* log, bool localConnection, QObject* parent); - - struct ImageCmdData - { - int priority; - QString origin; - int64_t duration; - int width; - int height; - int scale; - QString format; - QString imgName; - QByteArray data; - }; - - struct EffectCmdData - { - int priority; - int duration; - QString pythonScript; - QString origin; - QString effectName; - QString data; - QJsonObject args; - }; - - - struct registerData - { - hyperhdr::Components component; - QString origin; - QString owner; - hyperhdr::Components callerComp; - }; - - typedef std::map MapRegister; - typedef QMap MapAuthDefs; - -protected: - /// - /// @brief Initialize the API - /// This call is REQUIRED! - /// - void init(); - - QString installLut(QNetworkReply* reply, QString fileName, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time); - - /// - /// @brief Set a single color - /// @param[in] priority The priority of the written color - /// @param[in] ledColor The color to write to the leds - /// @param[in] timeout_ms The time the leds are set to the given color [ms] - /// @param[in] origin The setter - /// - void setColor(int priority, const std::vector& ledColors, int timeout_ms = -1, const QString& origin = "API", hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Set a image - /// @param[in] data The command data - /// @param[in] comp The component that should be used - /// @param[out] replyMsg The replyMsg on failure - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// @return True on success - /// - bool setImage(ImageCmdData& data, hyperhdr::Components comp, QString& replyMsg, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Clear a priority in the Muxer, if -1 all priorities are cleared - /// @param priority The priority to clear - /// @param replyMsg the message on failure - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// @return True on success - /// - bool clearPriority(int priority, QString& replyMsg, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Set a new component state - /// @param comp The component name - /// @param compState The new state of the comp - /// @param replyMsg The reply on failure - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// @ return True on success - /// - bool setComponentState(const QString& comp, bool& compState, QString& replyMsg, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Set a ledToImageMapping type - /// @param type mapping type string - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// - void setLedMappingType(int type, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Set the 2D/3D modes type - /// @param mode The VideoMode - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// - void setVideoModeHdr(int hdr, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Set user LUT filename for flatbuffers tone mapping - /// @param userLUTfile user LUT filename - /// - void setFlatbufferUserLUT(QString userLUTfile); - - /// - /// @brief Set an effect - /// @param dat The effect data - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// REQUIRED dat fields: effectName, priority, duration, origin - /// @return True on success else false - /// - bool setEffect(const EffectCmdData& dat, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Set source auto select enabled or disabled - /// @param sate The new state - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// - void setSourceAutoSelect(bool state, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Set the visible priority to given priority - /// @param priority The priority to set - /// @param callerComp The HYPERHDR COMPONENT that calls this function! e.g. PROT/FLATBUF - /// - void setVisiblePriority(int priority, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); - - /// - /// @brief Register a input or update the meta data of a previous register call - /// ATTENTION: Check unregisterInput() method description !!! - /// @param[in] priority The priority of the channel - /// @param[in] component The component of the channel - /// @param[in] origin Who set the channel (CustomString@IP) - /// @param[in] owner Specific owner string, might be empty - /// @param[in] callerComp The component that call this (e.g. PROTO/FLAT) - /// - void registerInput(int priority, hyperhdr::Components component, const QString& origin, const QString& owner, hyperhdr::Components callerComp); - - /// - /// @brief Revoke a registerInput() call by priority. We maintain all registered priorities in this scope - /// ATTENTION: This is MANDATORY if you change (priority change) or stop(clear/timeout) DURING lifetime. If this class destructs it's not needed - /// @param priority The priority to unregister - /// - void unregisterInput(int priority); - - /// - /// @brief Handle the instance switching - /// @param inst The requested instance - /// @return True on success else false - /// - bool setHyperhdrInstance(quint8 inst); - - /// - /// @brief Get all contrable components and their state - /// - std::map getAllComponents(); - - /// - /// @brief Check if Hyperhdr ist enabled - /// @return True when enabled else false - /// - bool isHyperhdrEnabled(); - - /// - /// @brief Get all instances data - /// @return The instance data - /// - QVector getAllInstanceData(); - - /// - /// @brief Start instance - /// @param index The instance index - /// @param tan The tan - /// @return True on success else false - /// - bool startInstance(quint8 index, int tan = 0); - - /// - /// @brief Stop instance - /// @param index The instance index - /// - void stopInstance(quint8 index); - - QJsonObject getAverageColor(quint8 index); - - ////////////////////////////////// - /// AUTH / ADMINISTRATION METHODS - ////////////////////////////////// - - /// - /// @brief Delete instance. Requires ADMIN ACCESS - /// @param index The instance index - /// @param replyMsg The reply Msg - /// @return False with reply - /// - bool deleteInstance(quint8 index, QString& replyMsg); - - /// - /// @brief Create instance. Requires ADMIN ACCESS - /// @param name With given name - /// @return False with reply - /// - QString createInstance(const QString& name); - - /// - /// @brief Rename an instance. Requires ADMIN ACCESS - /// @param index The instance index - /// @param name With given name - /// @return False with reply - /// - QString setInstanceName(quint8 index, const QString& name); - - /// - /// @brief Save settings object. Requires ADMIN ACCESS - /// @param data The data object - /// - bool saveSettings(const QJsonObject& data); - - /// - /// @brief Test if we are authorized to use the interface - /// @return The result - /// - bool isAuthorized(); - - /// - /// @brief Test if we are authorized to use the admin interface - /// @return The result - /// - bool isAdminAuthorized(); - - /// - /// @brief Update the Password of Hyperhdr. Requires ADMIN ACCESS - /// @param password Old password - /// @param newPassword New password - /// @return True on success else false - /// - bool updateHyperhdrPassword(const QString& password, const QString& newPassword); - - /// - /// @brief Get a new token from AuthManager. Requires ADMIN ACCESS - /// @param comment The comment of the request - /// @param def The final definition - /// @return Empty string on success else error message - /// - QString createToken(const QString& comment, AuthManager::AuthDefinition& def); - - /// - /// @brief Rename a token by given id. Requires ADMIN ACCESS - /// @param id The id of the token - /// @param comment The new comment - /// @return Empty string on success else error message - /// - QString renameToken(const QString& id, const QString& comment); - - /// - /// @brief Delete a token by given id. Requires ADMIN ACCESS - /// @param id The id of the token - /// @return Empty string on success else error message - /// - QString deleteToken(const QString& id); - - /// - /// @brief Set a new token request - /// @param comment The comment - /// @param id The id - /// @param tan The tan - /// - void setNewTokenRequest(const QString& comment, const QString& id, const int& tan); - - /// - /// @brief Cancel new token request - /// @param comment The comment - /// @param id The id - /// - void cancelNewTokenRequest(const QString& comment, const QString& id); - - /// - /// @brief Handle a pending token request. Requires ADMIN ACCESS - /// @param id The id fo the request - /// @param accept True when it should be accepted, else false - /// @return True on success - bool handlePendingTokenRequest(const QString& id, bool accept); - - /// - /// @brief Get the current List of Tokens. Requires ADMIN ACCESS - /// @param def returns the defintions - /// @return True on success - /// - bool getTokenList(QVector& def); - - /// - /// @brief Get all current pending token requests. Requires ADMIN ACCESS - /// @return True on success - /// - bool getPendingTokenRequests(QVector& map); - - /// - /// @brief Is User Token Authorized. On success this will grant acces to API and ADMIN API - /// @param userToken The user Token - /// @return True on succes - /// - bool isUserTokenAuthorized(const QString& userToken); - - /// - /// @brief Get the current User Token (session token). Requires ADMIN ACCESS - /// @param userToken The user Token - /// @return True on success - /// - bool getUserToken(QString& userToken); - - /// - /// @brief Is a token authrized. On success this will grant acces to the API (NOT ADMIN API) - /// @param token The user Token - /// @return True on succes - /// - bool isTokenAuthorized(const QString& token); - - /// - /// @brief Is User authorized. On success this will grant acces to the API and ADMIN API - /// @param password The password of the User - /// @return True if authorized - /// - bool isUserAuthorized(const QString& password); - - bool isUserBlocked(); - - /// - /// @brief Test if Hyperhdr has the default PW - /// @return The result - /// - bool hasHyperhdrDefaultPw(); - - /// - /// @brief Logout revokes all authorizations - /// - void logout(); - - quint8 getCurrentInstanceIndex(); - - /// Reflect auth status of this client - bool _authorized; - bool _adminAuthorized; - - /// Is this a local connection - bool _localConnection; - - AuthManager* _authManager; - HyperHdrIManager* _instanceManager; - - Logger* _log; - HyperHdrInstance* _hyperhdr; - -signals: - /// - /// @brief The API might decide to block connections for security reasons, this emitter should close the socket - /// - void forceClose(); - - /// - /// @brief Emits whenever a new Token request is pending. This signal is just active when ADMIN ACCESS has been granted - /// @param id The id of the request - /// @param comment The comment of the request; If the commen is EMPTY the request has been revoked by the caller. So remove it from the pending list - /// - void onPendingTokenRequest(const QString& id, const QString& comment); - - /// - /// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance it will emit. - /// @param success If true the request was accepted else false and no token was created - /// @param token The new token that is now valid - /// @param comment The comment that was part of the request - /// @param id The id that was part of the request - /// @param tan The tan that was part of the request - /// - void onTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan); - - /// - /// @brief Handle emits from HyperhdrIManager of startInstance request, just if QObject matches with this instance it will emit. - /// @param tan The tan that was part of the request - /// - void onStartInstanceResponse(const int& tan); - -private slots: - /// - /// @brief Is called whenever a Hyperhdr instance wants the current register list - /// @param callerInstance The instance should be returned in the answer call - /// - void requestActiveRegister(QObject* callerInstance); - -private: - void stopDataConnectionss(); - - // Contains all active register call data - std::map _activeRegisters; - - // current instance index - quint8 _currInstanceIndex; -}; diff --git a/include/api/BaseAPI.h b/include/api/BaseAPI.h new file mode 100644 index 000000000..4bbb79806 --- /dev/null +++ b/include/api/BaseAPI.h @@ -0,0 +1,159 @@ +#pragma once + +#include +#include +#include +#include + +class QNetworkReply; +class SoundCapture; +class DiscoveryWrapper; + +class BaseAPI : public QObject +{ + Q_OBJECT + +public: + BaseAPI(Logger* log, bool localConnection, QObject* parent); + + struct ImageCmdData + { + int priority; + QString origin; + int64_t duration; + int width; + int height; + int scale; + QString format; + QString imgName; + QByteArray data; + }; + + struct EffectCmdData + { + int priority; + int duration; + QString pythonScript; + QString origin; + QString effectName; + QString data; + QJsonObject args; + }; + + + struct registerData + { + hyperhdr::Components component; + QString origin; + QString owner; + hyperhdr::Components callerComp; + }; + + typedef std::map MapRegister; + typedef QMap MapAuthDefs; + +protected: + virtual void stopDataConnections() = 0; + virtual void removeSubscriptions() = 0; + virtual void addSubscriptions() = 0; + + void init(); + + bool clearPriority(int priority, QString& replyMsg, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + std::map getAllComponents(); + QVector getAllInstanceData(); + QJsonObject getAverageColor(quint8 index); + quint8 getCurrentInstanceIndex(); + QString installLut(QNetworkReply* reply, QString fileName, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time); + bool isHyperhdrEnabled(); + void registerInput(int priority, hyperhdr::Components component, const QString& origin, const QString& owner, hyperhdr::Components callerComp); + void unregisterInput(int priority); + + void setColor(int priority, const std::vector& ledColors, int timeout_ms = -1, const QString& origin = "API", hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + bool setComponentState(const QString& comp, bool& compState, QString& replyMsg, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + bool setEffect(const EffectCmdData& dat, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + void setFlatbufferUserLUT(QString userLUTfile); + bool setHyperhdrInstance(quint8 inst); + bool setImage(ImageCmdData& data, hyperhdr::Components comp, QString& replyMsg, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + void setLedMappingType(int type, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + void setSourceAutoSelect(bool state, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + void setVideoModeHdr(int hdr, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + void setVisiblePriority(int priority, hyperhdr::Components callerComp = hyperhdr::COMP_INVALID); + + bool startInstance(quint8 index, int tan = 0); + void stopInstance(quint8 index); + + ////////////////////////////////// + /// AUTH / ADMINISTRATION METHODS + ////////////////////////////////// + + bool deleteInstance(quint8 index, QString& replyMsg); + QString createInstance(const QString& name); + QString setInstanceName(quint8 index, const QString& name); + bool saveSettings(const QJsonObject& data); + bool isAuthorized(); + bool isAdminAuthorized(); + bool updateHyperhdrPassword(const QString& password, const QString& newPassword); + QString createToken(const QString& comment, AccessManager::AuthDefinition& def); + QString renameToken(const QString& id, const QString& comment); + QString deleteToken(const QString& id); + void setNewTokenRequest(const QString& comment, const QString& id, const int& tan); + void cancelNewTokenRequest(const QString& comment, const QString& id); + bool handlePendingTokenRequest(const QString& id, bool accept); + bool getTokenList(QVector& def); + bool getPendingTokenRequests(QVector& map); + bool isUserTokenAuthorized(const QString& userToken); + bool getUserToken(QString& userToken); + bool isTokenAuthorized(const QString& token); + bool isUserAuthorized(const QString& password); + bool isUserBlocked(); + bool hasHyperhdrDefaultPw(); + void logout(); + void putSystemInfo(QJsonObject& system); + + bool _adminAuthorized; + + Logger* _log; + std::shared_ptr _instanceManager; + std::shared_ptr _accessManager; + std::shared_ptr _hyperhdr; + std::shared_ptr _soundCapture; + std::shared_ptr _videoGrabber; + std::shared_ptr _systemGrabber; + std::shared_ptr _performanceCounters; + std::shared_ptr _discoveryWrapper; + + struct { + bool init = false; + QString cpuModelType; + QString cpuModelName; + QString cpuHardware; + QString cpuRevision; + + QString kernelType; + QString kernelVersion; + QString architecture; + QString wordSize; + QString productType; + QString productVersion; + QString prettyName; + QString hostName; + QString domainName; + } _sysInfo; + +signals: + void SignalPendingTokenClientNotification(const QString& id, const QString& comment); + void SignalPerformClientDisconnection(); + void SignalTokenClientNotification(bool success, const QString& token, const QString& comment, const QString& id, const int& tan); + void SignalInstanceStartedClientNotification(const int& tan); + +private slots: + void requestActiveRegister(QObject* callerInstance); + +private: + bool _authorized; + bool _localConnection; + quint8 _currentInstanceIndex; + + std::map _activeRegisters; +}; diff --git a/include/api/CallbackAPI.h b/include/api/CallbackAPI.h new file mode 100644 index 000000000..8342e5b20 --- /dev/null +++ b/include/api/CallbackAPI.h @@ -0,0 +1,73 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + + #include + #include +#endif + +#include +#include +#include + +#ifdef ENABLE_BONJOUR + #include +#endif + + +class HyperHdrInstance; +class ComponentRegister; +class BonjourBrowserWrapper; +class PriorityMuxer; +class LutCalibrator; + +class CallbackAPI : public BaseAPI +{ + Q_OBJECT + +public: + CallbackAPI(Logger* log, bool localConnection, QObject* parent); + + void subscribe(QJsonArray subsArr); + +protected: + std::unique_ptr _lutCalibrator; + Image _liveImage; + + void stopDataConnections() override = 0; + void removeSubscriptions() override; + void addSubscriptions() override; + bool subscribeFor(const QString& cmd, bool unsubscribe = false); + +signals: + void SignalCallbackToClient(QJsonObject); + +protected slots: + virtual void handleIncomingColors(const std::vector& ledValues) = 0; + virtual void handlerInstanceImageUpdated(const Image& image) = 0; + +private slots: + void componentStateHandler(hyperhdr::Components comp, bool state); + void priorityUpdateHandler(); + void imageToLedsMappingChangeHandler(int mappingType); + void signalAdjustmentUpdatedHandler(const QJsonArray& newConfig); + void videoModeHdrChangeHandler(hyperhdr::Components component, bool enable); + void videoStreamChangedHandler(QString device, QString videoMode); + void settingsChangeHandler(settings::type type, const QJsonDocument& data); + void ledsConfigChangeHandler(settings::type type, const QJsonDocument& data); + void instancesListChangedHandler(); + void tokenChangeHandler(const QVector& def); + void signalBenchmarkUpdateHandler(int status, QString message); + void lutCalibrationUpdateHandler(const QJsonObject& data); + void performanceUpdateHandler(const QJsonObject& data); +#ifdef ENABLE_BONJOUR + void signalDiscoveryFoundServiceHandler(DiscoveryRecord::Service type, QList records); +#endif + +private: + QStringList _availableCommands; + QStringList _subscribedCommands; + void doCallback(const QString& cmd, const QVariant& data); +}; diff --git a/include/api/HyperAPI.h b/include/api/HyperAPI.h new file mode 100644 index 000000000..5c5a989c1 --- /dev/null +++ b/include/api/HyperAPI.h @@ -0,0 +1,118 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + #include + + #include +#endif + +#include +#include +#include + +class QNetworkReply; + +class HyperAPI : public CallbackAPI +{ + Q_OBJECT + +public: + HyperAPI(QString peerAddress, Logger* log, bool localConnection, QObject* parent, bool noListener = false); + void handleMessage(const QString& message, const QString& httpAuthHeader = ""); + void initialize(); + +public slots: + void streamLedcolorsUpdate(const std::vector& ledColors); + void incommingLogMessage(const Logger::T_LOG_MESSAGE&); + hyperhdr::Components getActiveComponent(); + +private slots: + void newPendingTokenRequest(const QString& id, const QString& comment); + void handleTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan); + void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name = QString()); + void handleLedColorsTimer(); + void lutDownloaded(QNetworkReply* reply, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time); + +protected slots: + void handleIncomingColors(const std::vector& ledValues) override; + void handlerInstanceImageUpdated(const Image& image) override; + void sendImage(); + +signals: + void SignalCallbackBinaryImageMessage(Image); + void SignalForwardJsonMessage(QJsonObject); + void SignalCallbackJsonMessage(QJsonObject); + +protected: + void stopDataConnections() override; + +private: + std::shared_ptr _logsManager; + + bool _noListener; + + /// The peer address of the client + QString _peerAddress; + + // streaming buffers + QJsonObject _streaming_leds_reply; + QJsonObject _streaming_image_reply; + QJsonObject _streaming_logging_reply; + + /// flag to determine state of log streaming + bool _streaming_logging_activated; + + /// timer for led color refresh + QTimer* _ledStreamTimer; + + /// led stream refresh interval + qint64 _colorsStreamingInterval; + qint64 _lastSentImage; + + /// the current streaming led values + std::vector _currentLedValues; + + bool handleInstanceSwitch(quint8 instance = 0, bool forced = false); + void handleColorCommand(const QJsonObject& message, const QString& command, int tan); + void handleImageCommand(const QJsonObject& message, const QString& command, int tan); + void handleEffectCommand(const QJsonObject& message, const QString& command, int tan); + void handleSysInfoCommand(const QJsonObject& message, const QString& command, int tan); + void handleServerInfoCommand(const QJsonObject& message, const QString& command, int tan); + void handleClearCommand(const QJsonObject& message, const QString& command, int tan); + void handleClearallCommand(const QJsonObject& message, const QString& command, int tan); + void handleAdjustmentCommand(const QJsonObject& message, const QString& command, int tan); + void handleSourceSelectCommand(const QJsonObject& message, const QString& command, int tan); + void handleConfigCommand(const QJsonObject& message, const QString& command, int tan); + void handleSchemaGetCommand(const QJsonObject& message, const QString& command, int tan); + void handleConfigSetCommand(const QJsonObject& message, const QString& command, int tan); + void handleComponentStateCommand(const QJsonObject& message, const QString& command, int tan); + void handleLedColorsCommand(const QJsonObject& message, const QString& command, int tan); + void handleLoggingCommand(const QJsonObject& message, const QString& command, int tan); + void handleProcessingCommand(const QJsonObject& message, const QString& command, int tan); + void handleVideoModeHdrCommand(const QJsonObject& message, const QString& command, int tan); + void handleLutCalibrationCommand(const QJsonObject& message, const QString& command, int tan); + void handleAuthorizeCommand(const QJsonObject& message, const QString& command, int tan); + void handleInstanceCommand(const QJsonObject& message, const QString& command, int tan); + void handleLedDeviceCommand(const QJsonObject& message, const QString& command, int tan); + void handleHelpCommand(const QJsonObject& message, const QString& command, int tan); + void handleCropCommand(const QJsonObject& message, const QString& command, int tan); + void handleVideoControlsCommand(const QJsonObject& message, const QString& command, int tan); + void handleBenchmarkCommand(const QJsonObject& message, const QString& command, int tan); + void handleLutInstallCommand(const QJsonObject& message, const QString& command, int tan); + void handleSmoothingCommand(const QJsonObject& message, const QString& command, int tan); + void handleCurrentStateCommand(const QJsonObject& message, const QString& command, int tan); + void handleTunnel(const QJsonObject& message, const QString& command, int tan); + void handleNotImplemented(const QString& command, int tan); + void handleSaveDB(const QJsonObject& message, const QString& command, int tan); + void handleLoadDB(const QJsonObject& message, const QString& command, int tan); + void handleLoadSignalCalibration(const QJsonObject& message, const QString& command, int tan); + void handlePerformanceCounters(const QJsonObject& message, const QString& command, int tan); + + void sendSuccessReply(const QString& command = "", int tan = 0); + void sendSuccessDataReply(const QJsonDocument& doc, const QString& command = "", int tan = 0); + void sendErrorReply(const QString& error, const QString& command = "", int tan = 0); + + bool isLocal(QString hostname); +}; diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h deleted file mode 100644 index f3136e17e..000000000 --- a/include/api/JsonAPI.h +++ /dev/null @@ -1,337 +0,0 @@ -#pragma once - -// parent class -#include - -#include -#include -#include - -// qt includes -#include -#include -#include -#include - -class QTimer; -class JsonCB; -class AuthManager; - -class JsonAPI : public API -{ - Q_OBJECT - -public: - /// - /// Constructor - /// - /// @param peerAddress provide the Address of the peer - /// @param log The Logger class of the creator - /// @param parent Parent QObject - /// @param localConnection True when the sender has origin home network - /// @param noListener if true, this instance won't listen for hyperHDR push events - /// - JsonAPI(QString peerAddress, Logger* log, bool localConnection, QObject* parent, bool noListener = false); - - /// - /// Handle an incoming JSON message - /// - /// @param message the incoming message as string - /// - void handleMessage(const QString& message, const QString& httpAuthHeader = ""); - - /// - /// @brief Initialization steps - /// - void initialize(); - -public slots: - /// - /// @brief Is called whenever the current HyperHDR instance pushes new led raw values (if enabled) - /// @param ledColors The current led colors - /// - void streamLedcolorsUpdate(const std::vector& ledColors); - - /// - /// @brief Push images whenever hyperHDR emits (if enabled) - /// @param image The current image - /// - void setImage(); - - /// - /// @brief Process and push new log messages from logger (if enabled) - /// - void incommingLogMessage(const Logger::T_LOG_MESSAGE&); - - void releaseLock(); - - hyperhdr::Components getActiveComponent(); - -private slots: - /// - /// @brief Handle emits from API of a new Token request. - /// @param id The id of the request - /// @param comment The comment which needs to be accepted - /// - void newPendingTokenRequest(const QString& id, const QString& comment); - - /// - /// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance we are allowed to send response. - /// @param success If true the request was accepted else false and no token was created - /// @param token The new token that is now valid - /// @param comment The comment that was part of the request - /// @param id The id that was part of the request - /// @param tan The tan that was part of the request - /// - void handleTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan); - - /// - /// @brief Handle whenever the state of a instance (HyperHDRIManager) changes according to enum instanceState - /// @param instaneState A state from enum - /// @param instance The index of instance - /// @param name The name of the instance, just available with H_CREATED - /// - void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name = QString()); - - void handleLedColorsIncoming(const std::vector& ledValues); - - void handleLedColorsTimer(); - - void lutDownloaded(QNetworkReply* reply, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time); - -signals: - /// - /// Signal emits with the reply message provided with handleMessage() - /// - void callbackMessage(QJsonObject); - - /// - /// Signal emits whenever a JSON-message should be forwarded - /// - void forwardJsonMessage(QJsonObject); - -private: - // true if further callbacks are forbidden (http) - bool _noListener; - - /// The peer address of the client - QString _peerAddress; - - // The JsonCB instance which handles data subscription/notifications - JsonCB* _jsonCB; - - // streaming buffers - QJsonObject _streaming_leds_reply; - QJsonObject _streaming_image_reply; - QJsonObject _streaming_logging_reply; - - /// flag to determine state of log streaming - bool _streaming_logging_activated; - - /// timer for led color refresh - QTimer* _ledStreamTimer; - - /// led stream refresh interval - qint64 _colorsStreamingInterval; - - /// the current streaming led values - std::vector _currentLedValues; - - // when the last image was send to protect buffer overflow - uint64_t _lastSendImage; - - QSemaphore _semaphore; - - /// - /// @brief Handle the switches of HyperHDR instances - /// @param instance the instance to switch - /// @param forced indicate if it was a forced switch by system - /// @return true on success. false if not found - /// - bool handleInstanceSwitch(quint8 instance = 0, bool forced = false); - - /// - /// Handle an incoming JSON Color message - /// - /// @param message the incoming message - /// - void handleColorCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON Image message - /// - /// @param message the incoming message - /// - void handleImageCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON Effect message - /// - /// @param message the incoming message - /// - void handleEffectCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON System info message - /// - /// @param message the incoming message - /// - void handleSysInfoCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON Server info message - /// - /// @param message the incoming message - /// - void handleServerInfoCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON Clear message - /// - /// @param message the incoming message - /// - void handleClearCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON Clearall message - /// - /// @param message the incoming message - /// - void handleClearallCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON Adjustment message - /// - /// @param message the incoming message - /// - void handleAdjustmentCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON SourceSelect message - /// - /// @param message the incoming message - /// - void handleSourceSelectCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON GetConfig message and check subcommand - /// - /// @param message the incoming message - /// - void handleConfigCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON GetSchema message from handleConfigCommand() - /// - /// @param message the incoming message - /// - void handleSchemaGetCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON SetConfig message from handleConfigCommand() - /// - /// @param message the incoming message - /// - void handleConfigSetCommand(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON Component State message - /// - /// @param message the incoming message - /// - void handleComponentStateCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON Led Colors message - /// - /// @param message the incoming message - /// - void handleLedColorsCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON Logging message - /// - /// @param message the incoming message - /// - void handleLoggingCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON Processing message - /// - /// @param message the incoming message - /// - void handleProcessingCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON VideoMode message - /// - /// @param message the incoming message - /// - void handleVideoModeHdrCommand(const QJsonObject& message, const QString& command, int tan); - - void handleLutCalibrationCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON plugin message - /// - /// @param message the incoming message - /// - void handleAuthorizeCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON instance message - /// - /// @param message the incoming message - /// - void handleInstanceCommand(const QJsonObject& message, const QString& command, int tan); - - /// Handle an incoming JSON Led Device message - /// - /// @param message the incoming message - /// - void handleLedDeviceCommand(const QJsonObject& message, const QString& command, int tan); - - void handleHelpCommand(const QJsonObject& message, const QString& command, int tan); - - void handleCropCommand(const QJsonObject& message, const QString& command, int tan); - - void handleVideoControlsCommand(const QJsonObject& message, const QString& command, int tan); - - void handleBenchmarkCommand(const QJsonObject& message, const QString& command, int tan); - - void handleLutInstallCommand(const QJsonObject& message, const QString& command, int tan); - - void handleSmoothingCommand(const QJsonObject& message, const QString& command, int tan); - - void handleCurrentStateCommand(const QJsonObject& message, const QString& command, int tan); - - void handleTunnel(const QJsonObject& message, const QString& command, int tan); - - /// - /// Handle an incoming JSON message of unknown type - /// - void handleNotImplemented(const QString& command, int tan); - - void handleSaveDB(const QJsonObject& message, const QString& command, int tan); - - void handleLoadDB(const QJsonObject& message, const QString& command, int tan); - - void handleLoadSignalCalibration(const QJsonObject& message, const QString& command, int tan); - - void handlePerformanceCounters(const QJsonObject& message, const QString& command, int tan); - - /// - /// Send a standard reply indicating success - /// - void sendSuccessReply(const QString& command = "", int tan = 0); - - /// - /// Send a standard reply indicating success with data - /// - void sendSuccessDataReply(const QJsonDocument& doc, const QString& command = "", int tan = 0); - - /// - /// Send an error message back to the client - /// - /// @param error String describing the error - /// - void sendErrorReply(const QString& error, const QString& command = "", int tan = 0); - - /// - /// @brief Kill all signal/slot connections to stop possible data emitter - /// - void stopDataConnections(); - - bool isLocal(QString hostname); -}; diff --git a/include/api/JsonCB.h b/include/api/JsonCB.h deleted file mode 100644 index c52d67cc1..000000000 --- a/include/api/JsonCB.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once - -// qt incl -#include -#include - -#include -#include -#include - -#ifdef ENABLE_BONJOUR - #include -#endif - - -class HyperHdrInstance; -class ComponentRegister; -class BonjourBrowserWrapper; -class PriorityMuxer; - -class JsonCB : public QObject -{ - Q_OBJECT - -public: - JsonCB(QObject* parent); - - /// - /// @brief Subscribe to future data updates given by cmd - /// @param cmd The cmd which will be subscribed for - /// @param unsubscribe Revert subscription - /// @return True on success, false if not found - /// - bool subscribeFor(const QString& cmd, bool unsubscribe = false); - - /// - /// @brief Get all possible commands to subscribe for - /// @return The list of commands - /// - QStringList getCommands(); - - /// - /// @brief Get all subscribed commands - /// @return The list of commands - /// - QStringList getSubscribedCommands(); - - /// - /// @brief Reset subscriptions, disconnect all signals - /// - void resetSubscriptions(); - - /// - /// @brief Re-apply all current subs to a new HyperHDR instance, the connections to the old instance will be dropped - /// - void setSubscriptionsTo(HyperHdrInstance* hyperhdr); - - void handleLutInstallUpdate(const QJsonObject& data); - -signals: - /// - /// @brief Emits whenever a new json mesage callback is ready to send - /// @param The JsonObject message - /// - void newCallback(QJsonObject); - -private slots: - /// - /// @brief handle component state changes - /// - void handleComponentState(hyperhdr::Components comp, bool state); - -#ifdef ENABLE_BONJOUR - void handleNetworkDiscoveryChange(DiscoveryRecord::Service type, QList records); -#endif - - /// - /// @brief handle emits from PriorityMuxer - /// - void handlePriorityUpdate(); - - /// - /// @brief Handle imageToLedsMapping updates - /// - void handleImageToLedsMappingChange(int mappingType); - - /// - /// @brief Handle the adjustment update - /// - void handleAdjustmentChange(); - - /// - /// @brief Handle video mode change - /// @param mode The new videoMode - /// - void handleVideoModeHdrChange(int hdr); - - void handleGrabberStateChange(QString device, QString videoMode); - - /// - /// @brief Handle a config part change. This does NOT include (global) changes from other hyperhdr instances - /// @param type The settings type from enum - /// @param data The data as QJsonDocument - /// - void handleSettingsChange(settings::type type, const QJsonDocument& data); - - /// - /// @brief Handle led config specific updates (required for led color streaming with positional display) - /// @param type The settings type from enum - /// @param data The data as QJsonDocument - /// - void handleLedsConfigChange(settings::type type, const QJsonDocument& data); - - /// - /// @brief Handle HyperHDR instance manager change - /// - void handleInstanceChange(); - - /// - /// @brief Handle AuthManager token changes - /// - void handleTokenChange(const QVector& def); - - void handleBenchmarkUpdate(int status, QString message); - - void handleLutCalibrationUpdate(const QJsonObject& data); - - void handlePerformanceUpdate(const QJsonObject& data); - -private: - /// pointer of HyperHDR instance - HyperHdrInstance* _hyperhdr; - /// contains all available commands - QStringList _availableCommands; - /// contains active subscriptions - QStringList _subscribedCommands; - /// construct callback msg - void doCallback(const QString& cmd, const QVariant& data); -}; diff --git a/include/base/AccessManager.h b/include/base/AccessManager.h new file mode 100644 index 000000000..9227c7043 --- /dev/null +++ b/include/base/AccessManager.h @@ -0,0 +1,89 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + + #include + #include +#endif + +class AuthTable; +class MetaTable; +class QTimer; + +class AccessManager : public QObject +{ + Q_OBJECT +private: + friend class HyperHdrDaemon; + AccessManager(QObject* parent, bool readonlyMode); + +public: + ~AccessManager(); + + struct AuthDefinition + { + QString id; + QString comment; + QObject* caller; + int tan; + uint64_t timeoutTime; + QString token; + QString lastUse; + }; + + QString getID() const; + bool isAuthRequired() const; + bool isLocalAuthRequired() const; + bool isLocalAdminAuthRequired() const; + bool resetHyperhdrUser(); + bool isTokenAuthBlocked() const; + +public slots: + + bool isUserAuthorized(const QString& user, const QString& pw); + bool isUserAuthBlocked() const; + bool isTokenAuthorized(const QString& token); + bool isUserTokenAuthorized(const QString& usr, const QString& token); + + AccessManager::AuthDefinition createToken(const QString& comment); + bool renameToken(const QString& id, const QString& comment); + bool deleteToken(const QString& id); + bool updateUserPassword(const QString& user, const QString& pw, const QString& newPw); + void setNewTokenRequest(QObject* caller, const QString& comment, const QString& id, const int& tan = 0); + void cancelNewTokenRequest(QObject* caller, const QString&, const QString& id); + + void handlePendingTokenRequest(const QString& id, bool accept); + + QVector getPendingRequests() const; + QString getUserToken(const QString& usr = "Hyperhdr") const; + QVector getTokenList() const; + void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + void savePipewire(const QString& wToken); + const QString loadPipewire(); + +signals: + void newPendingTokenRequest(const QString& id, const QString& comment); + void SignalTokenUpdated(QVector); + void SignalTokenNotifyClient(bool success, QObject* caller, const QString& token, const QString& comment, const QString& id, const int& tan); + +private: + void setAuthBlock(bool user = false); + + std::unique_ptr _authTable; + std::unique_ptr _metaTable; + QString _uuid; + QMap _pendingRequests; + bool _authRequired; + bool _localAuthRequired; + bool _localAdminAuthRequired; + QTimer* _timer; + QTimer* _authBlockTimer; + QVector _userAuthAttempts; + QVector _tokenAuthAttempts; + +private slots: + void checkTimeout(); + void checkAuthBlockTimeout(); +}; diff --git a/include/base/AllHdrHeaders_pch.h b/include/base/AllHdrHeaders_pch.h new file mode 100644 index 000000000..bd1691c24 --- /dev/null +++ b/include/base/AllHdrHeaders_pch.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include +//included: +//included: +#include +//#included: +//#included: +#include +#include diff --git a/include/base/AllHeaders_pch.h b/include/base/AllHeaders_pch.h index 87e56b6fb..d9e82595f 100644 --- a/include/base/AllHeaders_pch.h +++ b/include/base/AllHeaders_pch.h @@ -1,83 +1,43 @@ -#ifndef ALLHEADERS_PCH_H_ -#define ALLHEADERS_PCH_H_ +#ifndef PCH_ENABLED + #define PCH_ENABLED -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#endif // ALLHEADERS_PCH_H_ +#endif // PCH_ENABLED diff --git a/include/base/AuthManager.h b/include/base/AuthManager.h deleted file mode 100644 index 61320874e..000000000 --- a/include/base/AuthManager.h +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once - -#include -#include - -//qt -#include -#include - -class AuthTable; -class MetaTable; -class QTimer; - -/// -/// @brief Manage the authorization of user and tokens. This class is created once as part of the HyperHdrDaemon -/// To work with the global instance use AuthManager::getInstance() -/// -class AuthManager : public QObject -{ - Q_OBJECT -private: - friend class HyperHdrDaemon; - /// constructor is private, can be called from HyperHdrDaemon - AuthManager(QObject* parent = nullptr, bool readonlyMode = false); - -public: - struct AuthDefinition - { - QString id; - QString comment; - QObject* caller; - int tan; - uint64_t timeoutTime; - QString token; - QString lastUse; - }; - - /// - /// @brief Get the unique id (imported from removed class 'Stats') - /// @return The unique id - /// - QString getID() const; - - /// - /// @brief Check authorization is required according to the user setting - /// @return True if authorization required else false - /// - bool isAuthRequired() const; - - /// - /// @brief Check if authorization is required for local network connections - /// @return True if authorization required else false - /// - bool isLocalAuthRequired() const; - - /// - /// @brief Check if authorization is required for local network connections for admin access - /// @return True if authorization required else false - /// - bool isLocalAdminAuthRequired() const; - - /// - /// @brief Reset HyperHdr user - /// @return True on success else false - /// - bool resetHyperhdrUser(); - - /// - /// @brief Check if user auth is temporary blocked due to failed attempts - /// @return True on blocked and no further Auth requests will be accepted - /// - - - /// - /// @brief Check if token auth is temporary blocked due to failed attempts - /// @return True on blocked and no further Auth requests will be accepted - /// - bool isTokenAuthBlocked() const; - - /// Pointer of this instance - static AuthManager* manager; - /// Get Pointer of this instance - static AuthManager* getInstance(); - -public slots: - - /// - /// @brief Check if user is authorized - /// @param user The username - /// @param pw The password - /// @return True if authorized else false - /// - bool isUserAuthorized(const QString& user, const QString& pw); - - bool isUserAuthBlocked() const; - - /// - /// @brief Check if token is authorized - /// @param token The token - /// @return True if authorized else false - /// - bool isTokenAuthorized(const QString& token); - - /// - /// @brief Check if token is authorized - /// @param usr The username - /// @param token The token - /// @return True if authorized else false - /// - bool isUserTokenAuthorized(const QString& usr, const QString& token); - - /// - /// @brief Create a new token and skip the usual chain - /// @param comment The comment that should be used for - /// @return The new Auth definition - /// - AuthManager::AuthDefinition createToken(const QString& comment); - - /// - /// @brief Rename a token by id - /// @param id The token id - /// @param comment The new comment - /// @return True on success else false (or not found) - /// - bool renameToken(const QString& id, const QString& comment); - - /// - /// @brief Delete a token by id - /// @param id The token id - /// @return True on success else false (or not found) - /// - bool deleteToken(const QString& id); - - /// - /// @brief Change password of user - /// @param user The username - /// @param pw The CURRENT password - /// @param newPw The new password - /// @return True on success else false - /// - bool updateUserPassword(const QString& user, const QString& pw, const QString& newPw); - - /// - /// @brief Generate a new pending token request with the provided comment and id as identifier helper - /// @param caller The QObject of the caller to deliver the reply - /// @param comment The comment as ident helper - /// @param id The id created by the caller - /// @param tan The tan created by the caller - /// - void setNewTokenRequest(QObject* caller, const QString& comment, const QString& id, const int& tan = 0); - - /// - /// @brief Cancel a pending token request with the provided comment and id as identifier helper - /// @param caller The QObject of the caller to deliver the reply - /// @param id The id created by the caller - /// - void cancelNewTokenRequest(QObject* caller, const QString&, const QString& id); - - /// - /// @brief Handle a token request by id, generate token and inform token caller or deny - /// @param id The id of the request - /// @param accept The accept or deny the request - /// - void handlePendingTokenRequest(const QString& id, bool accept); - - /// - /// @brief Get pending requests - /// @return All pending requests - /// - QVector getPendingRequests() const; - - /// - /// @brief Get the current valid token for user. Make sure this call is allowed! - /// @param usr the defined user - /// @return The token - /// - QString getUserToken(const QString& usr = "Hyperhdr") const; - - /// - /// @brief Get all available token entries - /// - QVector getTokenList() const; - - /// - /// @brief Handle settings update from HyperHdr Settingsmanager emit - /// @param type settings type from enum - /// @param config configuration object - /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - - - void savePipewire(const QString& wToken); - - const QString loadPipewire(); - -signals: - /// - /// @brief Emits whenever a new token Request has been created along with the id and comment - /// @param id The id of the request - /// @param comment The comment of the request; If the comment is EMPTY, it's a revoke of the caller! - /// - void newPendingTokenRequest(const QString& id, const QString& comment); - - /// - /// @brief Emits when the user has accepted or denied a token - /// @param success If true the request was accepted else false and no token will be available - /// @param caller The origin caller instance who requested this token - /// @param token The new token that is now valid - /// @param comment The comment that was part of the request - /// @param id The id that was part of the request - /// @param tan The tan that was part of the request - /// - void tokenResponse(bool success, QObject* caller, const QString& token, const QString& comment, const QString& id, const int& tan); - - /// - /// @brief Emits whenever the token list changes - /// @param data The full list of tokens - /// - void tokenChange(QVector); - -private: - /// - /// @brief Increment counter for token/user auth - /// @param user If true we increment USER auth instead of token - /// - void setAuthBlock(bool user = false); - - /// Database interface for auth table - AuthTable* _authTable; - - /// Database interface for meta table - MetaTable* _metaTable; - - /// Unique ID (imported from removed class 'Stats') - QString _uuid; - - /// All pending requests - QMap _pendingRequests; - - /// Reflect state of global auth - bool _authRequired; - - /// Reflect state of local auth - bool _localAuthRequired; - - /// Reflect state of local admin auth - bool _localAdminAuthRequired; - - /// Timer for counting against pendingRequest timeouts - QTimer* _timer; - - // Timer which cleans up the block counter - QTimer* _authBlockTimer; - - // Contains timestamps of failed user login attempts - QVector _userAuthAttempts; - - // Contains timestamps of failed token login attempts - QVector _tokenAuthAttempts; - -private slots: - /// - /// @brief Check timeout of pending requests - /// - void checkTimeout(); - - /// - /// @brief Check if there are timeouts for failed login attempts - /// - void checkAuthBlockTimeout(); -}; diff --git a/include/base/BGEffectHandler.h b/include/base/BGEffectHandler.h deleted file mode 100644 index e516fa181..000000000 --- a/include/base/BGEffectHandler.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include - -/// -/// @brief Handle the background Effect settings, reacts on runtime to settings changes -/// -class BGEffectHandler : public QObject -{ - Q_OBJECT - -public: - BGEffectHandler(HyperHdrInstance* hyperhdr); - -private slots: - /// - /// @brief Handle settings update from HyperHdr Settingsmanager emit or this constructor - /// @param type settingType from enum - /// @param config configuration object - /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - -private: - HyperHdrInstance* _hyperhdr; -}; diff --git a/include/base/ColorAdjustment.h b/include/base/ColorAdjustment.h deleted file mode 100644 index 83eafa264..000000000 --- a/include/base/ColorAdjustment.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -// STL includes -#include - -// Utils includes -#include -#include - -class ColorAdjustment -{ -public: - /// Unique identifier for this color transform - QString _id; - - /// The BLACK (RGB-Channel) adjustment - RgbChannelAdjustment _rgbBlackAdjustment; - /// The WHITE (RGB-Channel) adjustment - RgbChannelAdjustment _rgbWhiteAdjustment; - /// The RED (RGB-Channel) adjustment - RgbChannelAdjustment _rgbRedAdjustment; - /// The GREEN (RGB-Channel) adjustment - RgbChannelAdjustment _rgbGreenAdjustment; - /// The BLUE (RGB-Channel) adjustment - RgbChannelAdjustment _rgbBlueAdjustment; - /// The CYAN (RGB-Channel) adjustment - RgbChannelAdjustment _rgbCyanAdjustment; - /// The MAGENTA (RGB-Channel) adjustment - RgbChannelAdjustment _rgbMagentaAdjustment; - /// The YELLOW (RGB-Channel) adjustment - RgbChannelAdjustment _rgbYellowAdjustment; - - RgbTransform _rgbTransform; - - static ColorAdjustment* createColorAdjustment(quint8 instance, const QJsonObject& adjustmentConfig) - { - const QString id = adjustmentConfig["id"].toString("default"); - - ColorAdjustment* adjustment = new ColorAdjustment(); - adjustment->_id = id; - adjustment->_rgbBlackAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "black", 0, 0, 0); - adjustment->_rgbWhiteAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "white", 255, 255, 255); - adjustment->_rgbRedAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "red", 255, 0, 0); - adjustment->_rgbGreenAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "green", 0, 255, 0); - adjustment->_rgbBlueAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "blue", 0, 0, 255); - adjustment->_rgbCyanAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "cyan", 0, 255, 255); - adjustment->_rgbMagentaAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "magenta", 255, 0, 255); - adjustment->_rgbYellowAdjustment = RgbChannelAdjustment::createRgbChannelAdjustment(instance, adjustmentConfig, "yellow", 255, 255, 0); - adjustment->_rgbTransform = RgbTransform::createRgbTransform(instance, adjustmentConfig); - - adjustment->_rgbRedAdjustment.setCorrection(adjustmentConfig["temperatureRed"].toInt(255)); - adjustment->_rgbBlueAdjustment.setCorrection(adjustmentConfig["temperatureBlue"].toInt(255)); - adjustment->_rgbGreenAdjustment.setCorrection(adjustmentConfig["temperatureGreen"].toInt(255)); - - return adjustment; - } -}; diff --git a/include/base/ColorCalibration.h b/include/base/ColorCalibration.h new file mode 100644 index 000000000..da89bcb65 --- /dev/null +++ b/include/base/ColorCalibration.h @@ -0,0 +1,60 @@ +#pragma once + +/* ColorCalibration.h +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include +#endif + +#include +#include + +class ColorCalibration +{ +private: + QString _id; + + std::unique_ptr _blackCalibration; + std::unique_ptr _whiteCalibration; + std::unique_ptr _redCalibration; + std::unique_ptr _greenCalibration; + std::unique_ptr _blueCalibration; + std::unique_ptr _cyanCalibration; + std::unique_ptr _magentaCalibration; + std::unique_ptr _yellowCalibration; + + std::unique_ptr _colorspaceCalibration; +public: + ColorCalibration(uint8_t instance, const QJsonObject& adjustmentConfig); + + QString getId(); + void setBackLightEnabled(bool enabled); + void calibrate(ColorRgb& color); + void putCurrentConfig(QJsonObject& adjustment); + void updateConfig(const QJsonObject& adjustment); +}; diff --git a/include/base/ComponentController.h b/include/base/ComponentController.h new file mode 100644 index 000000000..78c1671df --- /dev/null +++ b/include/base/ComponentController.h @@ -0,0 +1,38 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + + #include + #include +#endif + +class HyperHdrInstance; + +class ComponentController : public QObject +{ + Q_OBJECT + +public: + ComponentController(HyperHdrInstance* hyperhdr); + virtual ~ComponentController(); + + int isComponentEnabled(hyperhdr::Components comp) const; + const std::map& getComponentsState() const; + +signals: + void SignalComponentStateChanged(hyperhdr::Components comp, bool state); + void SignalRequestComponent(hyperhdr::Components component, bool enabled); + +public slots: + void setNewComponentState(hyperhdr::Components comp, bool activated); + +private slots: + void handleCompStateChangeRequest(hyperhdr::Components comps, bool activated); + +private: + Logger* _log; + std::map _componentStates; + std::map _prevComponentStates; +}; diff --git a/include/base/ComponentRegister.h b/include/base/ComponentRegister.h deleted file mode 100644 index 0cb88c2a3..000000000 --- a/include/base/ComponentRegister.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include - -// STL includes -#include - -#include - -class HyperHdrInstance; - -/// -/// @brief The component register reflects and manages the current state of all components and HyperHDR as a whole -/// It emits also real component state changes (triggert from the specific component), which can be used for listening APIs (Network Clients/Plugins) -/// -class ComponentRegister : public QObject -{ - Q_OBJECT - -public: - ComponentRegister(HyperHdrInstance* hyperhdr); - ~ComponentRegister() override; - - /// - /// @brief Check if a component is currently enabled - /// @param comp The component from enum - /// @return True if component is running else false. Not found is -1 - /// - int isComponentEnabled(hyperhdr::Components comp) const; - - /// contains all components and their state - std::map getRegister() const { return _componentStates; } - -signals: - /// - /// @brief Emits whenever a component changed (really) the state - /// @param comp The component - /// @param state The new state of the component - /// - void updatedComponentState(hyperhdr::Components comp, bool state); - -public slots: - /// - /// @brief is called whenever a component change a state, DO NOT CALL FROM API, use signal hyperhdr->compStateChangeRequest - /// @param comp The component - /// @param state The new state of the component - /// - void setNewComponentState(hyperhdr::Components comp, bool activated); - -private slots: - /// - /// @brief Handle COMP_ALL changes from Hyperhdr->compStateChangeRequest - /// - void handleCompStateChangeRequest(hyperhdr::Components comps, bool activated); - -private: - /// HyperHDR instance - HyperHdrInstance* _hyperhdr; - /// Logger instance - Logger* _log; - /// current state of all components - std::map _componentStates; - /// on hyperHDR off we save the previous states of all components - std::map _prevComponentStates; - // helper to prevent self emit chains - bool _inProgress = false; -}; diff --git a/include/base/DetectionAutomatic.h b/include/base/DetectionAutomatic.h index 2a5193c58..c6dc51e38 100644 --- a/include/base/DetectionAutomatic.h +++ b/include/base/DetectionAutomatic.h @@ -1,18 +1,21 @@ #pragma once -#include -#include -#include -#include -#include - -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include +#endif + #include -#include -#include -#include -#include class DetectionAutomatic : public QObject { @@ -41,6 +44,16 @@ class DetectionAutomatic : public QObject void setAutomaticCalibrationData(QString signature, int quality, int width, int height, std::vector sdrVec, std::vector hdrVec); + virtual int getHdrToneMappingEnabled() = 0; + virtual void setHdrToneMappingEnabled(int mode) = 0; + virtual int getFpsSoftwareDecimation() = 0; + virtual void setFpsSoftwareDecimation(int decimation) = 0; + virtual int getActualFps() = 0; + virtual QString getSignature() = 0; + +signals: + void SignalSaveCalibration(QString saveData); + public slots: QJsonDocument startCalibration(); @@ -82,8 +95,7 @@ public slots: calibration(); void reset(); - void buildPoints(int _width, int _height); - QString getSignature(); + void buildPoints(QString _signature, int _width, int _height); } calibrationData, checkData; void calibrateFrame(Image& image); diff --git a/include/base/DetectionManual.h b/include/base/DetectionManual.h index c1f94883a..7072ac4e5 100644 --- a/include/base/DetectionManual.h +++ b/include/base/DetectionManual.h @@ -1,15 +1,18 @@ #pragma once -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include + #include + #include + #include +#endif -#include -#include #include -#include -#include class Grabber; diff --git a/include/base/Grabber.h b/include/base/Grabber.h index faacd5dab..067af8f30 100644 --- a/include/base/Grabber.h +++ b/include/base/Grabber.h @@ -1,24 +1,27 @@ #pragma once -#include -#include -#include -#include -#include - -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include +#endif + #include -#include -#include #include #include #include -#include -#include - - #if defined(_WIN32) || defined(WIN32) // Windows #include @@ -39,10 +42,9 @@ class Grabber : public DetectionAutomatic, public DetectionManual Q_OBJECT public: - Grabber(const QString& configurationPath, const QString& grabberName = "", int width = 0, int height = 0, - int cropLeft = 0, int cropRight = 0, int cropTop = 0, int cropBottom = 0); + Grabber(const QString& configurationPath, const QString& grabberName); - ~Grabber(); + virtual ~Grabber(); static const QString AUTO_SETTING; static const int AUTO_FPS; @@ -50,12 +52,10 @@ class Grabber : public DetectionAutomatic, public DetectionManual struct DevicePropertiesItem; - virtual void setHdrToneMappingEnabled(int mode) = 0; + virtual void setHdrToneMappingEnabled(int mode) override = 0; virtual void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom); - virtual void alternativeCaching(bool alternative); - bool trySetWidthHeight(int width, int height); bool trySetInput(int input); @@ -64,6 +64,8 @@ class Grabber : public DetectionAutomatic, public DetectionManual int getImageHeight(); + bool isInitialized(); + void setEnabled(bool enable); QString getVideoDeviceName(const QString& devicePath) const; @@ -81,11 +83,17 @@ class Grabber : public DetectionAutomatic, public DetectionManual virtual void uninit() = 0; - void setFpsSoftwareDecimation(int decimation); + void enableHardwareAcceleration(bool hardware); + + void setMonitorNits(int nits); + + void setFpsSoftwareDecimation(int decimation) override; + + int getFpsSoftwareDecimation() override; - int getFpsSoftwareDecimation(); + QString getSignature() override; - int getActualFps(); + int getActualFps() override; void setEncoding(QString enc); @@ -107,12 +115,14 @@ class Grabber : public DetectionAutomatic, public DetectionManual void resetCounter(int64_t from); - int getHdrToneMappingEnabled(); + int getHdrToneMappingEnabled() override; void setSignalDetectionEnable(bool enable); void setAutoSignalDetectionEnable(bool enable); + void benchmarkCapture(int status, QString message); + QList getVideoDeviceModesFullInfo(const QString& devicePath); QString getConfigurationPath(); @@ -145,9 +155,9 @@ public slots: virtual void stop() = 0; - virtual void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) = 0; + virtual void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) = 0; - virtual void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) = 0; + virtual void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) = 0; void revive(); @@ -156,14 +166,20 @@ public slots: QStringList getVideoDevices() const; signals: - void newFrame(const Image& image); + void SignalNewCapturedFrame(const Image& image); + + void SignalCapturingException(const char* err); - void readError(const char* err); + void SignalSetNewComponentStateToAllInstances(hyperhdr::Components component, bool enable); + + void SignalBenchmarkUpdate(int status, QString message); protected: void loadLutFile(PixelFormat color, const QList& files); - void processSystemFrameBGRA(uint8_t* source, int lineSize = 0); + int getTargetSystemFrameDimension(int& targetSizeX, int& targetSizeY); + + void processSystemFrameBGRA(uint8_t* source, int lineSize = 0, bool useLut = true); void processSystemFrameBGR(uint8_t* source, int lineSize = 0); @@ -171,6 +187,10 @@ public slots: void processSystemFrameRGBA(uint8_t* source, int lineSize = 0); + void processSystemFramePQ10(uint8_t* source, int lineSize = 0); + + void handleNewFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin); + struct DeviceControlCapability { bool enabled; @@ -227,9 +247,10 @@ public slots: int64_t frameBegin; int averageFrame; unsigned int badFrame, goodFrame, segment; + bool directAccess; } frameStat; - volatile uint64_t _currentFrame; + std::atomic _currentFrame; QString _deviceName; @@ -240,12 +261,13 @@ public slots: bool _restartNeeded; bool _initialized; int _fpsSoftwareDecimation; + bool _hardware; PixelFormat _actualVideoFormat; int _actualWidth, _actualHeight, _actualFPS; QString _actualDeviceName; - - uint8_t* _lutBuffer; + uint _targetMonitorNits; + MemoryBuffer _lut; bool _lutBufferInit; int _lineLength; @@ -254,6 +276,9 @@ public slots: bool _signalDetectionEnabled; bool _signalAutoDetectionEnabled; QSemaphore _synchro; + + int _benchmarkStatus; + QString _benchmarkMessage; }; bool sortDevicePropertiesItem(const Grabber::DevicePropertiesItem& v1, const Grabber::DevicePropertiesItem& v2); diff --git a/include/base/GrabberHelper.h b/include/base/GrabberHelper.h new file mode 100644 index 000000000..101b79089 --- /dev/null +++ b/include/base/GrabberHelper.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class GrabberHelper : public QObject { + Q_OBJECT + + QObject* _grabberInstance = nullptr; + +signals: + void SignalCreateGrabber(); + +public: + ~GrabberHelper(); + void setGrabber(QObject* grabber); + SystemWrapper* systemWrapper(); + GrabberWrapper* grabberWrapper(); + + QSemaphore linker { 0 }; +}; diff --git a/include/base/GrabberWrapper.h b/include/base/GrabberWrapper.h index 10bfa2540..f7e7c3ad0 100644 --- a/include/base/GrabberWrapper.h +++ b/include/base/GrabberWrapper.h @@ -1,61 +1,46 @@ #pragma once -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include +#endif + #include #include class GlobalSignals; class QTimer; -/// -/// This class will be inherted by FramebufferWrapper and others which contains the real capture interface -/// class GrabberWrapper : public QObject { Q_OBJECT public: - GrabberWrapper(const QString& grabberName, Grabber* ggrabber); + GrabberWrapper(const QString& grabberName); ~GrabberWrapper() override; - static GrabberWrapper* instance; - static GrabberWrapper* getInstance() { return instance; } - - QMap getVideoCurrentMode() const; - - bool isCEC(); - - void setCecStartStop(int cecHdrStart, int cecHdrStop); - - int getFpsSoftwareDecimation(); - - int getActualFps(); - - void revive(); - + bool isCEC(); bool getAutoResume(); - void benchmarkCapture(int status, QString message); - public slots: - void newFrame(const Image& image); - void readError(const char* err); - void cecKeyPressedHandler(int key); + void capturingExceptionHandler(const char* err); bool start(); void stop(); + void revive(); + + void benchmarkCapture(int status, QString message); QJsonObject getJsonInfo(); @@ -64,38 +49,41 @@ public slots: QJsonDocument getCalibrationInfo(); private slots: - void handleSourceRequest(hyperhdr::Components component, int instanceIndex, bool listen); + void signalRequestSourceHandler(hyperhdr::Components component, int instanceIndex, bool listen); signals: /// /// @brief Emit the final processed image /// - void systemImage(const QString& name, const Image& image); - void HdrChanged(int mode); - void StateChanged(QString device, QString videoMode); - void cecKeyPressed(int key); - void benchmarkUpdate(int status, QString message); - void setBrightnessContrastSaturationHue(int brightness, int contrast, int saturation, int hue); - void instancePauseChanged(int instance, bool isEnabled); - -public: - int getHdrToneMappingEnabled(); + void SignalNewVideoImage(const QString& name, const Image& image); + void SignalVideoStreamChanged(QString device, QString videoMode); + void SignalCecKeyPressed(int key); + void SignalBenchmarkUpdate(int status, QString message); + void SignalInstancePauseChanged(int instance, bool isEnabled); + void SignalSetNewComponentStateToAllInstances(hyperhdr::Components component, bool enable); + void SignalSaveCalibration(QString saveData); public slots: + void signalCecKeyPressedHandler(int key); + int getHdrToneMappingEnabled(); void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold); void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom); void setSignalDetectionOffset(double verticalMin, double horizontalMin, double verticalMax, double horizontalMax); void setSignalDetectionEnable(bool enable); void setDeviceVideoStandard(const QString& device); - void setFpsSoftwareDecimation(int decimation); - void setHdrToneMappingEnabled(int mode); + void setFpsSoftwareDecimation(int decimation); void setEncoding(QString enc); - void setBrightnessContrastSaturationHueHandler(int brightness, int contrast, int saturation, int hue); + void setBrightnessContrastSaturationHue(int brightness, int contrast, int saturation, int hue); void setQFrameDecimation(int setQframe); void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - void instancePauseChangedHandler(int instance, bool isEnabled); + void signalInstancePauseChangedHandler(int instance, bool isEnabled); + QString getVideoCurrentModeResolution(); protected: + void setHdrToneMappingEnabled(int mode); + void setCecStartStop(int cecHdrStart, int cecHdrStop); + + QMap getVideoCurrentMode() const; DetectionAutomatic::calibrationPoint parsePoint(int width, int height, QJsonObject element, bool& ok); QString _grabberName; @@ -104,7 +92,7 @@ public slots: bool _configLoaded; - Grabber* _grabber; + std::unique_ptr _grabber; int _cecHdrStart; int _cecHdrStop; @@ -112,8 +100,6 @@ public slots: bool _isPaused; bool _pausingModeEnabled; - int _benchmarkStatus; - QString _benchmarkMessage; QList _running_clients; QList _paused_clients; }; diff --git a/include/base/HyperHdrIManager.h b/include/base/HyperHdrIManager.h deleted file mode 100644 index 1bc121f06..000000000 --- a/include/base/HyperHdrIManager.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once - -// util -#include -#include -#include - -// qt -#include - -class HyperHdrInstance; -class InstanceTable; - -namespace hyperhdr -{ - enum class InstanceState { - H_STARTED, - H_ON_STOP, - H_STOPPED, - H_CREATED, - H_PRE_DELETE, - H_DELETED - }; -}; - -using namespace hyperhdr; - -/// -/// @brief HyperHDRInstanceManager manages the instances of the the HyperHDR class -/// -class HyperHdrIManager : public QObject -{ - Q_OBJECT - -public: - struct PendingRequests - { - QObject* caller; - int tan; - }; - - // global instance pointer - static HyperHdrIManager* getInstance() { return HIMinstance; } - static HyperHdrIManager* HIMinstance; - QString getRootPath() { return _rootPath; } - bool areInstancesReady(); - -public slots: - void setSmoothing(int time); - - bool isCEC(); - - void setSignalStateByCEC(bool enable); - - const QJsonObject getBackup(); - - QString restoreBackup(const QJsonObject& message); - - /// - /// @brief Is given instance running? - /// @param inst The instance to check - /// @return True when running else false - /// - bool IsInstanceRunning(quint8 inst) const { return _runningInstances.contains(inst); } - - /// - /// @brief Get a HyperHDR instance by index - /// @param intance the index - /// @return HyperHDR instance, if index is not found returns instance 0 - /// - HyperHdrInstance* getHyperHdrInstance(quint8 instance = 0); - - /// - /// @brief Get instance data of all instaces in db + running state - /// - QVector getInstanceData() const; - - /// - /// @brief Start a HyperHDR instance - /// @param instance Instance index - /// @param block If true return when thread has been started - /// @return Return true on success, false if not found in db - /// - bool startInstance(quint8 inst, bool block = false, QObject* caller = nullptr, int tan = 0); - - /// - /// @brief Stop a HyperHDR instance - /// @param instance Instance index - /// @return Return true on success, false if not found in db - /// - bool stopInstance(quint8 inst); - - QJsonObject getAverageColor(quint8 index); - - /// - /// @brief Toggle the state of all HyperHDR instances - /// @param pause If true all instances toggle to pause, else to resume - /// - void toggleStateAllInstances(bool pause = false); - - void hibernate(bool wakeUp); - - /// - /// @brief Create a new HyperHDR instance entry in db - /// @param name The friendly name of the instance - /// @param start If true it will be started after creation (async) - /// @return Return true on success false if name is already in use or a db error occurred - /// - bool createInstance(const QString& name, bool start = false); - - /// - /// @brief Delete HyperHDR instance entry in db. Cleanup also all associated table data for this instance - /// @param inst The instance index - /// @return Return true on success, false if not found or not allowed - /// - bool deleteInstance(quint8 inst); - - /// - /// @brief Assign a new name to the given instance - /// @param inst The instance index - /// @param name The instance name index - /// @return Return true on success, false if not found - /// - bool saveName(quint8 inst, const QString& name); - - void saveCalibration(QString saveData); - - void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name); - -signals: - /// - /// @brief Emits whenever the state of a instance changes according to enum instanceState - /// @param instaneState A state from enum - /// @param instance The index of instance - /// @param name The name of the instance, just available with H_CREATED - /// - void instanceStateChanged(InstanceState state, quint8 instance, const QString& name = QString()); - - /// - /// @brief Emits whenever something changes, the lazy version of instanceStateChanged (- H_ON_STOP) + saveName() emit - /// - void change(); - - /// - /// @brief Emits when the user has requested to start a instance - /// @param caller The origin caller instance who requested - /// @param tan The tan that was part of the request - /// - void startInstanceResponse(QObject* caller, const int& tan); - -signals: - /// - /// @brief PIPE settings events from HyperHDR - /// - void settingsChanged(settings::type type, const QJsonDocument& data); - - /// - /// @brief PIPE videoMode request changes from HyperHDR to HyperHDRDaemon - /// - void requestVideoModeHdr(int hdr); - - /// - /// @brief PIPE component state changes from HyperHDR to HyperHDRDaemon - /// - void compStateChangeRequest(hyperhdr::Components component, bool enable); - - void setNewComponentStateToAllInstances(hyperhdr::Components component, bool enable); - -private slots: - /// - /// @brief handle started signal of HyperHDR instances - /// - void handleStarted(); - - /// - /// @brief handle finished signal of HyperHDR instances - /// - void handleFinished(); - -private: - friend class HyperHdrDaemon; - /// - /// @brief Construct the Manager - /// @param The root path of all userdata - /// - HyperHdrIManager(const QString& rootPath, QObject* parent = nullptr, bool readonlyMode = false); - - /// - /// @brief Start all instances that are marked as enabled in db. Non blocking - /// - void startAll(); - - /// - /// @brief Stop all instances, used from HyperHDR deamon - /// - void stopAll(); - - /// - /// @brief check if a instance is allowed for management. Instance 0 represents the root instance - /// @apram inst The instance to check - /// - bool isInstAllowed(quint8 inst) const { return (inst > 0); } - -private: - Logger* _log; - InstanceTable* _instanceTable; - const QString _rootPath; - QMap _runningInstances; - QList _startQueue; - - bool _readonlyMode; - int _fireStarter; - - /// All pending requests - QMap _pendingRequests; -}; diff --git a/include/base/HyperHdrInstance.h b/include/base/HyperHdrInstance.h index 2abbaef7a..2cb602bcb 100644 --- a/include/base/HyperHdrInstance.h +++ b/include/base/HyperHdrInstance.h @@ -1,242 +1,106 @@ #pragma once -// stl includes -#include -#include - -// QT includes -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include - +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #include + #include + #include + #include +#endif #include -#include -#include -#include - -// Effect engine includes #include #include - #include -// settings utils -#include - -// Forward class declaration -class ImageProcessor; -class MessageForwarder; -class LinearSmoothing; +class Muxer; +class ComponentController; +class ImageToLedManager; +class NetworkForwarder; +class Smoothing; class EffectEngine; -class MultiColorAdjustment; +class LedCalibration; class ColorAdjustment; -class SettingsManager; -class BGEffectHandler; +class InstanceConfig; class VideoControl; class SystemControl; class BoblightServer; class RawUdpServer; class LedDeviceWrapper; -class ImageProcessingUnit; class Logger; +class HyperHdrManager; +class SoundCapture; +class GrabberHelper; -/// -/// The main class of HyperHDR. This gives other 'users' access to the attached LedDevice through -/// the priority muxer. -/// class HyperHdrInstance : public QObject { Q_OBJECT -public: - /// Type definition of the info structure used by the priority muxer - using InputInfo = PriorityMuxer::InputInfo; - - /// - /// Destructor; cleans up resources - /// - ~HyperHdrInstance() override; - bool isCEC(); - - void setSignalStateByCEC(bool enable); - - /// - /// free all alocated objects, should be called only from constructor or before restarting hyperhdr - /// - void freeObjects(); - - ImageProcessor* getImageProcessor() const { return _imageProcessor; } - - /// - /// @brief Get instance index of this instance - /// @return The index of this instance - /// +public: + HyperHdrInstance(quint8 instance, bool readonlyMode, QString name); + ~HyperHdrInstance(); + quint8 getInstanceIndex() const { return _instIndex; } - - /// - /// @brief Return the size of led grid - /// QSize getLedGridSize() const { return _ledGridSize; } - - /// gets the methode how image is maped to leds - int getLedMappingType() const; - - /// forward smoothing config + bool getScanParameters(size_t led, double& hscanBegin, double& hscanEnd, double& vscanBegin, double& vscanEnd) const; unsigned updateSmoothingConfig(unsigned id, int settlingTime_ms = 200, double ledUpdateFrequency_hz = 25.0, bool directMode = false); - /// - /// @brief Get the current active led device - /// @return The device name - /// - QString getActiveDeviceType() const; - - ImageProcessor* getImageProcessor(); - - void updateLedsValues(int priority, const std::vector& ledColors); + static void signalTerminateTriggered(); + static bool isTerminated(); + static int getTotalRunningCount(); public slots: - - QJsonObject getJsonInfo(bool full); - + bool clear(int priority, bool forceClearAll = false); QJsonObject getAverageColor(); - - void setSmoothing(int time); - - void identifyLed(const QJsonObject& params); - - bool getReadOnlyMode() { return _readOnlyMode; }; - - void saveCalibration(QString saveData); - - void saveGrabberParams(int hardware_brightness, int hardware_contrast, int hardware_saturation); - - /// - /// Updates the priority muxer with the current time and (re)writes the led color with applied - /// transforms. - /// - void update(); - - void requestForColors(); - - void updateResult(std::vector _ledBuffer); - - /// - /// Returns the number of attached leds - /// + hyperhdr::Components getComponentForPriority(int priority); + hyperhdr::Components getCurrentPriorityActiveComponent(); + int getCurrentPriority() const; + std::list getEffects() const; int getLedCount() const; - - /// - /// @brief Register a new input by priority, the priority is not active (timeout -100 isn't muxer recognized) until you start to update the data with setInput() - /// A repeated call to update the base data of a known priority won't overwrite their current timeout - /// @param[in] priority The priority of the channel - /// @param[in] component The component of the channel - /// @param[in] origin Who set the channel (CustomString@IP) - /// @param[in] owner Specific owner string, might be empty - /// @param[in] smooth_cfg The smooth id to use - /// + bool getReadOnlyMode() const { return _readOnlyMode; }; + QJsonDocument getSetting(settings::type type) const; + int hasLedClock(); + void identifyLed(const QJsonObject& params); + int isComponentEnabled(hyperhdr::Components comp) const; + void putJsonConfig(QJsonObject& info) const; + void putJsonInfo(QJsonObject& info, bool full); void registerInput(int priority, hyperhdr::Components component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = 0); - - /// - /// @brief Update the current color of a priority (prev registered with registerInput()) - /// DO NOT use this together with setInputImage() at the same time! - /// @param priority The priority to update - /// @param ledColors The colors - /// @param timeout_ms The new timeout (defaults to -1 endless) - /// @param clearEffect Should be true when NOT called from an effect - /// @return True on success, false when priority is not found - /// - bool setInput(int priority, const std::vector& ledColors, int timeout_ms = -1, bool clearEffect = true); - - /// - /// @brief Update the current image of a priority (prev registered with registerInput()) - /// DO NOT use this together with setInput() at the same time! - /// @param priority The priority to update - /// @param image The new image - /// @param timeout_ms The new timeout (defaults to -1 endless) - /// @param clearEffect Should be true when NOT called from an effect - /// @return True on success, false when priority is not found - /// - bool setInputImage(int priority, const Image& image, int64_t timeout_ms = -1, bool clearEffect = true); - - /// - /// Writes a single color to all the leds for the given time and priority - /// Registers comp color or provided type against muxer - /// Should be never used to update leds continuous - /// - /// @param[in] priority The priority of the written color - /// @param[in] ledColors The color to write to the leds - /// @param[in] timeout_ms The time the leds are set to the given color [ms] - /// @param[in] origin The setter - /// @param clearEffect Should be true when NOT called from an effect - /// + void saveCalibration(QString saveData); + bool saveSettings(const QJsonObject& config, bool correct = false); + void setSignalStateByCEC(bool enable); + bool setInputLeds(int priority, const std::vector& ledColors, int timeout_ms = -1, bool clearEffect = true); + void setInputImage(int priority, const Image& image, int64_t timeout_ms = -1, bool clearEffect = true); + void signalSetGlobalImageHandler(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin, QString clientDescription); void setColor(int priority, const std::vector& ledColors, int timeout_ms = -1, const QString& origin = "System", bool clearEffects = true); - - /// - /// @brief Set the given priority to inactive - /// @param priority The priority - /// @return True on success false if not found - /// + void signalSetGlobalColorHandler(int priority, const std::vector& ledColor, int timeout_ms, hyperhdr::Components origin, QString clientDescription); bool setInputInactive(quint8 priority); - - /// - /// Returns the list with unique adjustment identifiers - /// @return The list with adjustment identifiers - /// - QStringList getAdjustmentIds() const; - - /// - /// Returns the ColorAdjustment with the given identifier - /// @return The adjustment with the given identifier (or nullptr if the identifier does not exist) - /// - ColorAdjustment* getAdjustment(const QString& id) const; - - /// Tell HyperHDR that the corrections have changed and the leds need to be updated - void adjustmentsUpdated(); - - /// - /// Clears the given priority channel. This will switch the led-colors to the colors of the next - /// lower priority channel (or off if no more channels are set) - /// - /// @param[in] priority The priority channel. -1 clears all priorities - /// @param[in] forceClearAll Force the clear - /// @return True on success else false (not found) - /// - bool clear(int priority, bool forceClearAll = false); - - /// ############# - // EFFECTENGINE - /// - /// @brief Get a pointer to the effect engine - /// @return EffectEngine instance pointer - /// - - EffectEngine* getEffectEngineInstance() const { return _effectEngine; } - - - /// Run the specified effect on the given priority channel and optionally specify a timeout - /// @param effectName Name of the effec to run - /// @param priority The priority channel of the effect - /// @param timeout The timeout of the effect (after the timout, the effect will be cleared) + void setLedMappingType(int mappingType); + void setNewComponentState(hyperhdr::Components component, bool state); + void setSetting(settings::type type, QString config); + void setSourceAutoSelect(bool state); + void setSmoothing(int time); + bool setVisiblePriority(int priority); + bool sourceAutoSelectEnabled() const; + void start(); + void update(); + void updateAdjustments(const QJsonObject& config); + void updateResult(std::vector _ledBuffer); + int setEffect(const QString& effectName, int priority, int timeout = -1, const QString& origin = "System"); - - /// Run the specified effect on the given priority channel and optionally specify a timeout - /// @param effectName Name of the effec to run - /// @param args arguments of the effect script - /// @param priority The priority channel of the effect - /// @param timeout The timeout of the effect (after the timout, the effect will be cleared) int setEffect(const QString& effectName , const QJsonObject& args , int priority @@ -245,295 +109,59 @@ public slots: , const QString& imageData = "" ); - /// Get the list of available effects - /// @return The list of available effects - std::list getEffects() const; - - /// Get the list of active effects - /// @return The list of active effects - std::list getActiveEffects() const; - - - /// ############# - /// PRIORITYMUXER - /// - /// @brief Get a pointer to the priorityMuxer instance - /// @return PriorityMuxer instance pointer - /// - PriorityMuxer* getMuxerInstance() { return &_muxer; } - - /// - /// @brief enable/disable automatic/priorized source selection - /// @param state The new state - /// - void setSourceAutoSelect(bool state); - - /// - /// @brief set current input source to visible - /// @param priority the priority channel which should be vidible - /// @return true if success, false on error - /// - bool setVisiblePriority(int priority); - - /// gets current state of automatic/priorized source selection - /// @return the state - bool sourceAutoSelectEnabled() const; - - /// - /// Returns the current priority - /// - /// @return The current priority - /// - int getCurrentPriority() const; - - /// - /// Returns true if current priority is given priority - /// - /// @return bool - /// - bool isCurrentPriority(int priority) const; - - /// - /// Returns a list of all registered priorities - /// - /// @return The list with priorities - /// - QList getActivePriorities() const; - - /// - /// Returns the information of a specific priorrity channel - /// - /// @param[in] priority The priority channel - /// - /// @return The information of the given, a not found priority will return lowest priority as fallback - /// - const PriorityMuxer::InputInfo& getPriorityInfo(int priority) const; - - PriorityMuxer::InputInfo getCurrentPriorityInfo(); - - /// ############# - /// SETTINGSMANAGER - /// - /// @brief Get a setting by settings::type from SettingsManager - /// @param type The settingsType from enum - /// @return Data Document - /// - QJsonDocument getSetting(settings::type type) const; - - /// gets the current json config object from SettingsManager - /// @return json config - QJsonObject getQJsonConfig() const; - - /// - /// @brief Save a complete json config - /// @param config The entire config object - /// @param correct If true will correct json against schema before save - /// @return True on success else false - /// - bool saveSettings(const QJsonObject& config, bool correct = false); - - /// ############ - /// COMPONENTREGISTER - /// - /// @brief Get the component Register - /// return Component register pointer - /// - ComponentRegister& getComponentRegister() { return _componentRegister; } - - /// - /// @brief Called from components to update their current state. DO NOT CALL FROM USERS - /// @param[in] component The component from enum - /// @param[in] state The state of the component [true | false] - /// - void setNewComponentState(hyperhdr::Components component, bool state); - - /// - /// @brief Get a list of all contrable components and their current state - /// @return list of components - /// - std::map getAllComponents() const; - - /// - /// @brief Test if a component is enabled - /// @param The component to test - /// @return Component state - /// - int isComponentEnabled(hyperhdr::Components comp) const; - - /// sets the methode how image is maped to leds at ImageProcessor - void setLedMappingType(int mappingType); - - /// - /// @brief Init after thread start - /// - void start(); - - /// - /// @brief Stop the execution of this thread, helper to properly track eventing - /// - void stop(); - signals: - /// Signal which is emitted when a priority channel is actively cleared - /// This signal will not be emitted when a priority channel time out - void channelCleared(int priority); - - /// Signal which is emitted when all priority channels are actively cleared - /// This signal will not be emitted when a priority channel time out - void allChannelsCleared(); - - /// - /// @brief Emits whenever a user request a component state change, it's up the component to listen - /// and update the component state at the componentRegister - /// @param component The component from enum - /// @param enabled The new state of the component - /// - void compStateChangeRequest(hyperhdr::Components component, bool enabled); - - /// - /// @brief Emits whenever the imageToLedsMapping has changed - /// @param mappingType The new mapping type - /// - void imageToLedsMappingChanged(int mappingType); - - /// - /// @brief Emits whenever the visible priority delivers a image which is applied in update() - /// priorities with ledColors won't emit this signal - /// @param image The current image - /// - void onCurrentImage(); - - /// Signal which is emitted, when a new json message should be forwarded - void forwardJsonMessage(QJsonObject); - - /// Signal which is emitted, when a new system proto image should be forwarded - void forwardSystemProtoMessage(const QString&, const Image&); - - /// Signal which is emitted, when a new V4l proto image should be forwarded - void forwardV4lProtoMessage(const QString&, const Image&); - - /// - /// @brief Emits whenever a config part changed. SIGNAL PIPE helper for SettingsManager -> HyperHdrDaemon - /// @param type The settings type from enum - /// @param data The data as QJsonDocument - /// - void settingsChanged(settings::type type, const QJsonDocument& data); - - /// - /// @brief Emits whenever the adjustments have been updated - /// - void adjustmentChanged(); - - /// - /// @brief Emits whenever new data should be pushed to the LedDeviceWrapper which forwards it to the threaded LedDevice - /// - void ledDeviceData(const std::vector& ledValues); - - /// - /// @brief Emits whenever new untransformed ledColos data is available, reflects the current visible device - /// - void rawLedColors(const std::vector& ledValues); - - /// - /// @brief Emits before thread quit is requested - /// - void finished(); - - /// - /// @brief Emits after thread has been started - /// - void started(); + void SignalComponentStateChanged(hyperhdr::Components comp, bool state); + void SignalPrioritiesChanged(); + void SignalVisiblePriorityChanged(quint8 priority); + void SignalVisibleComponentChanged(hyperhdr::Components comp); + void SignalInstancePauseChanged(int instance, bool isEnabled); + void SignalRequestComponent(hyperhdr::Components component, bool enabled); + void SignalImageToLedsMappingChanged(int mappingType); + void SignalInstanceImageUpdated(const Image& ret); + void SignalForwardJsonMessage(QJsonObject); + void SignalInstanceSettingsChanged(settings::type type, const QJsonDocument& data); + void SignalAdjustmentUpdated(const QJsonArray& newConfig); + void SignalUpdateLeds(const std::vector& ledValues); + void SignalSmoothingClockTick(); + void SignalSmoothingRestarted(int suggestedInterval); + void SignalRawColorsChanged(const std::vector& ledValues); + void SignalInstanceJustStarted(); private slots: - /// - /// @brief Handle whenever the visible component changed - /// @param comp The new component - /// void handleVisibleComponentChanged(hyperhdr::Components comp); - - /// - /// @brief Apply settings updates for LEDS and COLOR - /// @param type The type from enum - /// @param config The configuration - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - - void handlePriorityChangedLedDevice(const quint8& priority); private: - friend class HyperHdrIManager; - - /// - /// @brief Constructs the HyperHdr instance, just accessible for HyperHdrIManager - /// @param instance The instance index - /// - HyperHdrInstance(quint8 instance, bool readonlyMode = false, QString name = ""); - - /// instance index - const quint8 _instIndex; - - QTime _bootEffect; - - std::unique_ptr _imageProcessingUnit; - - /// Settings manager of this instance - SettingsManager* _settingsManager; - - /// Register that holds component states - ComponentRegister _componentRegister; - - /// The specifiation of the led frame construction and picture integration + const quint8 _instIndex; + QTime _bootEffect; LedString _ledString; - /// Image Processor - ImageProcessor* _imageProcessor; - - /// The priority muxer - PriorityMuxer _muxer; - - /// The adjustment from raw colors to led colors - MultiColorAdjustment* _raw2ledAdjustment; - - /// The actual LedDeviceWrapper - LedDeviceWrapper* _ledDeviceWrapper; - - /// The smoothing LedDevice - LinearSmoothing* _smoothing; - - /// Effect engine - EffectEngine* _effectEngine; + std::unique_ptr _instanceConfig; + std::unique_ptr _componentController; + std::unique_ptr _imageProcessor; + std::unique_ptr _muxer; + std::unique_ptr _ledColorCalibration; + std::unique_ptr _ledDeviceWrapper; + std::unique_ptr _smoothing; + std::unique_ptr _effectEngine; + std::unique_ptr _videoControl; + std::unique_ptr _systemControl; + std::unique_ptr _boblightServer; + std::unique_ptr _rawUdpServer; - // Message forwarder - MessageForwarder* _messageForwarder; - - /// Logger instance Logger* _log; - - /// count of hardware leds int _hwLedCount; - QSize _ledGridSize; - /// Background effect instance, kept active to react on setting changes - BGEffectHandler* _BGEffectHandler; - - /// Capture control for Daemon native capture - VideoControl* _videoControl; - - SystemControl* _systemControl; - - /// buffer for leds (with adjustment) - std::vector _globalLedBuffer; - - /// Boblight instance - BoblightServer* _boblightServer; - RawUdpServer* _rawUdpServer; - + std::vector _currentLedColors; QString _name; bool _readOnlyMode; + static std::atomic _signalTerminate; + static std::atomic _totalRunningCount; + struct { qint64 token = 0; qint64 statBegin = 0; diff --git a/include/base/HyperHdrManager.h b/include/base/HyperHdrManager.h new file mode 100644 index 000000000..50f4cfc32 --- /dev/null +++ b/include/base/HyperHdrManager.h @@ -0,0 +1,118 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + + #include + #include + #include +#endif + +class HyperHdrInstance; +class InstanceTable; +class SoundCapture; +class GrabberHelper; + +namespace hyperhdr +{ + enum class InstanceState { + START, + STOP + }; +}; + +using namespace hyperhdr; + +class HyperHdrManager : public QObject +{ + Q_OBJECT + +public: + ~HyperHdrManager(); + + struct PendingRequests + { + QObject* caller; + int tan; + }; + + QString getRootPath(); + bool areInstancesReady(); + +public slots: + void setSmoothing(int time); + + void setSignalStateByCEC(bool enable); + + const QJsonObject getBackup(); + + QString restoreBackup(const QJsonObject& message); + + bool IsInstanceRunning(quint8 inst) const { return _runningInstances.contains(inst); } + + std::shared_ptr getHyperHdrInstance(quint8 instance = 0); + + QVector getInstanceData() const; + + bool startInstance(quint8 inst, QObject* caller = nullptr, int tan = 0); + + bool stopInstance(quint8 inst); + + QJsonObject getAverageColor(quint8 index); + + void toggleStateAllInstances(bool pause = false); + + void hibernate(bool wakeUp); + + bool createInstance(const QString& name, bool start = false); + + bool deleteInstance(quint8 inst); + + bool saveName(quint8 inst, const QString& name); + + void saveCalibration(QString saveData); + + void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name); + +signals: + void SignalInstanceStateChanged(InstanceState state, quint8 instance, const QString& name = QString()); + + void SignalInstancesListChanged(); + + void SignalStartInstanceResponse(QObject* caller, const int& tan); + + void SignalSettingsChanged(settings::type type, const QJsonDocument& data); + + void SignalCompStateChangeRequest(hyperhdr::Components component, bool enable); + + void SignalSetNewComponentStateToAllInstances(hyperhdr::Components component, bool enable); + + void SignalInstancePauseChanged(int instance, bool isEnabled); + +private slots: + void handleInstanceJustStarted(); + +private: + friend class HyperHdrDaemon; + + HyperHdrManager(const QString& rootPath, bool readonlyMode); + + void startAll(); + + void stopAllonExit(); + + bool isInstAllowed(quint8 inst) const { return (inst > 0); } + +private: + + Logger* _log; + std::unique_ptr _instanceTable; + const QString _rootPath; + QMap> _runningInstances; + QMap> _startingInstances; + + bool _readonlyMode; + int _fireStarter; + + QMap _pendingRequests; +}; diff --git a/include/base/ImageColorAveraging.h b/include/base/ImageColorAveraging.h new file mode 100644 index 000000000..c7c0cb732 --- /dev/null +++ b/include/base/ImageColorAveraging.h @@ -0,0 +1,60 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include + #include +#endif + +#include + +namespace hyperhdr +{ + class ImageColorAveraging + { + public: + ImageColorAveraging( + Logger* _log, + const int mappingType, + const bool sparseProcessing, + const unsigned width, + const unsigned height, + const unsigned horizontalBorder, + const unsigned verticalBorder, + const quint8 instanceIndex, + const std::vector& leds); + + unsigned width() const; + unsigned height() const; + + unsigned horizontalBorder() const; + unsigned verticalBorder() const; + + void Process(std::vector& colors, const Image& image, uint16_t* advanced); + + private: + void getMeanLedColor(std::vector& ledColors, const Image& image) const; + void getUniLedColor(std::vector& ledColors, const Image& image) const; + void getMeanAdvLedColor(std::vector& ledColors, const Image& image, uint16_t* lut) const; + + const unsigned _width; + const unsigned _height; + const unsigned _horizontalBorder; + const unsigned _verticalBorder; + int _mappingType; + + std::vector> _colorsMap; + std::vector _colorsGroups; + + int _groupMin; + int _groupMax; + + ColorRgb calcMeanColor(const Image& image, const std::vector& colors) const; + ColorRgb calcMeanAdvColor(const Image& image, const std::vector& colors, uint16_t* lut) const; + ColorRgb calcMeanColor(const Image& image) const; + }; +} diff --git a/include/base/ImageProcessingUnit.h b/include/base/ImageProcessingUnit.h deleted file mode 100644 index 1e3aaa3bb..000000000 --- a/include/base/ImageProcessingUnit.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -class ImageProcessingUnit : public QObject -{ - Q_OBJECT - -public: - ImageProcessingUnit(HyperHdrInstance* hyperhdr); - ~ImageProcessingUnit(); - -signals: - void dataReadySignal(std::vector result); - void processImageSignal(); - void queueImageSignal(int priority, const Image& image); - void clearQueueImageSignal(); - -private slots: - void queueImage(int priority, const Image& image); - void clearQueueImage(); - void processImage(); - -private: - int _priority; - HyperHdrInstance* _hyperhdr; - Image _frameBuffer; -}; diff --git a/include/base/ImageProcessor.h b/include/base/ImageProcessor.h deleted file mode 100644 index 264bf9765..000000000 --- a/include/base/ImageProcessor.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include - -#include - -// Utils includes -#include - -#include -#include -#include - -// settings -#include - -// Black border includes -#include - -class HyperHdrInstance; -class ImageProcessingUnit; - -/// -/// The ImageProcessor translates an RGB-image to RGB-values for the LEDs. The processing is -/// performed in two steps. First the average color per led-region is computed. Second a -/// color-transform is applied based on a gamma-correction. -/// -class ImageProcessor : public QObject -{ - Q_OBJECT - -public: - friend class ImageProcessingUnit; - - /// - /// Constructs an image-processor for translating an image to led-color values based on the - /// given led-string specification - /// @param[in] ledString LedString data - /// @param[in] hyperhdr HyperHDR instance pointer - /// - ImageProcessor(const LedString& ledString, HyperHdrInstance* hyperhdr); - - ~ImageProcessor() override; - - /// - /// Specifies the width and height of 'incoming' images. This will resize the buffer-image to - /// match the given size. - /// NB All earlier obtained references will be invalid. - /// - /// @param[in] width The new width of the buffer-image - /// @param[in] height The new height of the buffer-image - /// - void setSize(unsigned width, unsigned height); - - /// - /// @brief Update the led string (eg on settings change) - /// - void setLedString(const LedString& ledString); - - /// Returns state of black border detector - bool blackBorderDetectorEnabled() const; - - /// Returns the current _mappingType - int getLedMappingType() const; - - static int mappingTypeToInt(const QString& mappingType); - static QString mappingTypeToStr(int mappingType); - - void setSparseProcessing(bool sparseProcessing); - -public slots: - - void setBlackbarDetectDisable(bool enable); - - void setLedMappingType(int mapType); - -public: - /// - /// Specifies the width and height of 'incoming' images. This will resize the buffer-image to - /// match the given size. - /// NB All earlier obtained references will be invalid. - /// - /// @param[in] image The dimensions taken from image - /// - void setSize(const Image& image); - - void verifyBorder(const Image& image); - - /// - /// Get the hscan and vscan parameters for a single led - /// - /// @param[in] led Index of the led - /// @param[out] hscanBegin begin of the hscan - /// @param[out] hscanEnd end of the hscan - /// @param[out] vscanBegin begin of the hscan - /// @param[out] vscanEnd end of the hscan - /// @return true if the parameters could be retrieved - bool getScanParameters(size_t led, double& hscanBegin, double& hscanEnd, double& vscanBegin, double& vscanEnd) const; - -private: - void registerProcessingUnit( - const unsigned width, - const unsigned height, - const unsigned horizontalBorder, - const unsigned verticalBorder); - -private slots: - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - -private: - Logger* _log; - /// The Led-string specification - LedString _ledString; - - /// The processor for black border detection - hyperhdr::BlackBorderProcessor* _borderProcessor; - - /// The mapping of image-pixels to LEDs - std::shared_ptr _imageToLedColors; - - /// Type of image 2 led mapping - int _mappingType; - - bool _sparseProcessing; - - // lut advanced operator - uint16_t advanced[256]; - - quint8 _instanceIndex; -}; diff --git a/include/base/ImageToLedManager.h b/include/base/ImageToLedManager.h new file mode 100644 index 000000000..0c74759a7 --- /dev/null +++ b/include/base/ImageToLedManager.h @@ -0,0 +1,66 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + + #include + #include + + #include + #include +#endif + +#include +#include +#include + +class HyperHdrInstance; + +class ImageToLedManager : public QObject +{ + Q_OBJECT + +public: + ImageToLedManager(const LedString& ledString, HyperHdrInstance* hyperhdr); + + void setSize(unsigned width, unsigned height); + void setLedString(const LedString& ledString); + bool blackBorderDetectorEnabled() const; + int getLedMappingType() const; + + static int mappingTypeToInt(const QString& mappingType); + static QString mappingTypeToStr(int mappingType); + + void setSparseProcessing(bool sparseProcessing); + void processFrame(std::vector& colors, const Image& frameBuffer); + +public slots: + + void setBlackbarDetectDisable(bool enable); + void setLedMappingType(int mapType); + +public: + void setSize(const Image& image); + void verifyBorder(const Image& image); + bool getScanParameters(size_t led, double& hscanBegin, double& hscanEnd, double& vscanBegin, double& vscanEnd) const; + +private: + void registerProcessingUnit( + const unsigned width, + const unsigned height, + const unsigned horizontalBorder, + const unsigned verticalBorder); + +private slots: + void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + +private: + quint8 _instanceIndex; + Logger* _log; + LedString _ledString; + hyperhdr::BlackBorderProcessor* _borderProcessor; + std::unique_ptr _colorAveraging; + int _mappingType; + bool _sparseProcessing; + uint16_t _advanced[256]; +}; diff --git a/include/base/ImageToLedsMap.h b/include/base/ImageToLedsMap.h deleted file mode 100644 index 35652ce72..000000000 --- a/include/base/ImageToLedsMap.h +++ /dev/null @@ -1,103 +0,0 @@ - -#pragma once - -// STL includes -#include -#include -#include -#include - - -#include -#include - - -#include - -namespace hyperhdr -{ - - /// - /// The ImageToLedsMap holds a mapping of indices into an image to leds. It can be used to - /// calculate the average (or mean) color per led for a specific region. - /// - class ImageToLedsMap - { - public: - - /// - /// Constructs an mapping from the absolute indices in an image to each led based on the border - /// definition given in the list of leds. The map holds absolute indices to any given image, - /// provided that it is row-oriented. - /// The mapping is created purely on size (width and height). The given borders are excluded - /// from indexing. - /// - /// @param[in] width The width of the indexed image - /// @param[in] height The width of the indexed image - /// @param[in] horizontalBorder The size of the horizontal border (0=no border) - /// @param[in] verticalBorder The size of the vertical border (0=no border) - /// @param[in] leds The list with led specifications - /// - ImageToLedsMap( - Logger* _log, - const int mappingType, - const bool sparseProcessing, - const unsigned width, - const unsigned height, - const unsigned horizontalBorder, - const unsigned verticalBorder, - const quint8 instanceIndex, - const std::vector& leds); - - /// - /// Returns the width of the indexed image - /// - /// @return The width of the indexed image [pixels] - /// - unsigned width() const; - - /// - /// Returns the height of the indexed image - /// - /// @return The height of the indexed image [pixels] - /// - unsigned height() const; - - unsigned horizontalBorder() const; - unsigned verticalBorder() const; - - std::vector Process(const Image& image, uint16_t* advanced); - - private: - std::vector getMeanLedColor(const Image& image) const; - - std::vector getUniLedColor(const Image& image) const; - - std::vector getMeanAdvLedColor(const Image& image, uint16_t* lut) const; - - /// The width of the indexed image - const unsigned _width; - /// The height of the indexed image - const unsigned _height; - - const unsigned _horizontalBorder; - - const unsigned _verticalBorder; - - int _mappingType; - - /// The absolute indices into the image for each led - std::vector> _colorsMap; - std::vector _colorsGroups; - - int _groupMin; - int _groupMax; - - ColorRgb calcMeanColor(const Image& image, const std::vector& colors) const; - - ColorRgb calcMeanAdvColor(const Image& image, const std::vector& colors, uint16_t* lut) const; - - ColorRgb calcMeanColor(const Image& image) const; - }; - -} diff --git a/include/base/InstanceConfig.h b/include/base/InstanceConfig.h new file mode 100644 index 000000000..9610ff517 --- /dev/null +++ b/include/base/InstanceConfig.h @@ -0,0 +1,41 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + + #include + + #include + #include +#endif + +class SettingsTable; + +class InstanceConfig : public QObject +{ + Q_OBJECT + +public: + InstanceConfig(bool master, quint8 instanceIndex, QObject* parent, bool readonlyMode); + ~InstanceConfig(); + + bool upgradeDB(QJsonObject& dbConfig); + bool saveSettings(QJsonObject config, bool correct = false); + QJsonDocument getSetting(settings::type type) const; + void saveSetting(settings::type key, QString saveData); + QJsonObject getSettings() const; + +signals: + void SignalInstanceSettingsChanged(settings::type type, const QJsonDocument& data); + +private: + Logger* _log; + std::unique_ptr _sTable; + QJsonObject _qconfig; + bool _readonlyMode; + + static QJsonObject _schemaJson; + static QMutex _lockerSettingsManager; + static std::atomic _backupMade; +}; diff --git a/include/base/LedCalibration.h b/include/base/LedCalibration.h new file mode 100644 index 000000000..205115c02 --- /dev/null +++ b/include/base/LedCalibration.h @@ -0,0 +1,62 @@ +#pragma once + +/* LedCalibration.h +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include + #include + + #include + + #include +#endif + +#include + +class LedCalibration +{ +public: + LedCalibration(quint8 instance, int ledCnt, const QJsonObject& colorConfig); + ~LedCalibration(); + + LedCalibration(const LedCalibration& t) = delete; + LedCalibration& operator=(const LedCalibration& x) = delete; + + void setAdjustmentForLed(int index, std::shared_ptr& adjustment, size_t startLed, size_t endLed); + bool verifyAdjustments() const; + void setBacklightEnabled(bool enable); + QJsonArray getAdjustmentState() const; + void applyAdjustment(std::vector& ledColors); + void updateConfig(const QJsonObject& adjustment); + +private: + std::vector> _calibrationConfig; + std::vector> _perLedConfig; + Logger* _log; +}; diff --git a/include/base/LedString.h b/include/base/LedString.h index 675a19982..0459f1981 100644 --- a/include/base/LedString.h +++ b/include/base/LedString.h @@ -1,133 +1,44 @@ - #pragma once -// STL includes -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include -// Local includes -#include + #include + #include -// QT includes -#include -#include -#include -#include + #include +#endif -// Forward class declarations namespace Json { class Value; } -/// Enumeration containing the possible orders of device color byte data -enum class ColorOrder -{ - ORDER_RGB, ORDER_RBG, ORDER_GRB, ORDER_BRG, ORDER_GBR, ORDER_BGR -}; - -inline QString colorOrderToString(ColorOrder colorOrder) +class LedString { - switch (colorOrder) - { - case ColorOrder::ORDER_RGB: - return "rgb"; - case ColorOrder::ORDER_RBG: - return "rbg"; - case ColorOrder::ORDER_GRB: - return "grb"; - case ColorOrder::ORDER_BRG: - return "brg"; - case ColorOrder::ORDER_GBR: - return "gbr"; - case ColorOrder::ORDER_BGR: - return "bgr"; - default: - return "not-a-colororder"; - } -} -inline ColorOrder stringToColorOrder(const QString& order) -{ - if (order == "rgb") - { - return ColorOrder::ORDER_RGB; - } - else if (order == "bgr") - { - return ColorOrder::ORDER_BGR; - } - else if (order == "rbg") - { - return ColorOrder::ORDER_RBG; - } - else if (order == "brg") - { - return ColorOrder::ORDER_BRG; - } - else if (order == "gbr") - { - return ColorOrder::ORDER_GBR; - } - else if (order == "grb") +public: + enum class ColorOrder { - return ColorOrder::ORDER_GRB; - } - - std::cout << "Unknown color order defined (" << order.toStdString() << "). Using RGB." << std::endl; - return ColorOrder::ORDER_RGB; -} + ORDER_RGB, ORDER_RBG, ORDER_GRB, ORDER_BRG, ORDER_GBR, ORDER_BGR + }; -/// -/// The Led structure contains the definition of the image portion used to determine a single led's -/// color. -/// @verbatim -/// |--------------------image--| -/// | minX maxX | -/// | |-----|minY | -/// | | | | -/// | |-----|maxY | -/// | | -/// | | -/// | | -/// |---------------------------| -/// @endverbatim -/// -struct Led -{ - /// The minimum vertical scan line included for this leds color - double minX_frac; - /// The maximum vertical scan line included for this leds color - double maxX_frac; - /// The minimum horizontal scan line included for this leds color - double minY_frac; - /// The maximum horizontal scan line included for this leds color - double maxY_frac; + static QString colorOrderToString(ColorOrder colorOrder); + static ColorOrder stringToColorOrder(const QString& order); - bool disabled; + struct Led + { + double minX_frac; + double maxX_frac; + double minY_frac; + double maxY_frac; - int group; -}; + bool disabled; + int group; + }; -/// -/// The LedString contains the image integration information of the leds -/// -class LedString -{ - -public: - /// - /// Returns the led specifications - /// - /// @return The list with led specifications - /// std::vector& leds(); - - /// - /// Returns the led specifications - /// - /// @return The list with led specifications - /// const std::vector& leds() const; - - /// the color order ColorOrder colorOrder = ColorOrder::ORDER_RGB; bool hasDisabled = false; @@ -137,6 +48,5 @@ class LedString static QSize getLedLayoutGridSize(const QJsonArray& ledConfigArray); private: - /// The list with led specifications std::vector mLeds; }; diff --git a/include/base/LinearSmoothing.h b/include/base/LinearSmoothing.h deleted file mode 100644 index f85b8d4d9..000000000 --- a/include/base/LinearSmoothing.h +++ /dev/null @@ -1,194 +0,0 @@ -#pragma once - -// STL includes -#include - -// Qt includes -#include - -// hyperhdr incluse -#include -#include - -// settings -#include - -class QTimer; -class Logger; -class HyperHdrInstance; - -/// Linear Smooting class -/// -/// This class processes the requested led values and forwards them to the device after applying -/// a linear smoothing effect. This class can be handled as a generic LedDevice. -class LinearSmoothing : public QObject -{ - Q_OBJECT - -public: - /// Constructor - /// @param config The configuration document smoothing - /// @param hyperhdr The HyperHDR parent instance - /// - LinearSmoothing(const QJsonDocument& config, HyperHdrInstance* hyperhdr); - ~LinearSmoothing(); - - /// LED values as input for the smoothing filter - /// - /// @param ledValues The color-value per led - /// @return Zero on success else negative - /// - - void setEnable(bool enable); - bool pause() const; - bool enabled() const; - - void updateLedValues(const std::vector& ledValues); - -public slots: - /// - /// @brief Handle settings update from HyperHDR Settingsmanager emit or this constructor - /// @param type settingyType from enum - /// @param config configuration object - /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - - /// - /// @brief select a smoothing cfg given by cfg index from addConfig() - /// @param cfg The index to use - /// @param force Overwrite in any case the current values (used for cfg 0 settings update) - /// - /// @return On success return else false (and falls back to cfg 0) - /// - bool selectConfig(unsigned cfg, bool force); // = false - - /// Timer callback which writes updated led values to the led device - void updateLeds(); - - /// - /// @brief Handle component state changes - /// @param component The component - /// @param state The requested state - /// - void componentStateChange(hyperhdr::Components component, bool state); - - /// - /// @brief Update a smoothing cfg which can be used with selectConfig() - /// In case the ID does not exist, a smoothing cfg is added - /// - /// @param cfgID Smoothing configuration item to be updated - /// @param settlingTime_ms The buffer time - /// @param ledUpdateFrequency_hz The frequency of update - /// @param updateDelay The delay - /// - /// @return The index of the cfg which can be passed to selectConfig() - /// - unsigned updateConfig(unsigned cfgID, int settlingTime_ms, double ledUpdateFrequency_hz, bool directModee); - - void updateCurrentConfig(int settlingTime_ms); -private: - - /** - * Pushes the colors into the output queue and popping the head to the led-device - * - * @param ledColors The colors to queue - */ - void queueColors(const std::vector& ledColors); - void clearQueuedColors(bool deviceEnabled = false, bool restarting = false); - inline uint8_t computeColor(int64_t k, int color); - void setupAdvColor(int64_t deltaTime, float& kOrg, float& kMin, float& kMid, float& kAbove, float& kMax); - inline uint8_t computeAdvColor(int limitMin, int limitAverage, int limitMax, float kMin, float kMid, float kAbove, float kMax, int color); - - /// - /// @brief Add a new smoothing cfg which can be used with selectConfig() - /// @param settlingTime_ms The buffer time - /// @param ledUpdateFrequency_hz The frequency of update - /// @param updateDelay The delay - /// - /// @return The index of the cfg which can be passed to selectConfig() - /// - unsigned addConfig(int settlingTime_ms, double ledUpdateFrequency_hz = 25.0, bool directMode = false); - - uint8_t clamp(int x); - - void Antiflickering(); - - void LinearSetup(const std::vector& ledValues); - - void LinearSmoothingProcessing(bool correction); - - void DebugOutput(); - - /// Logger instance - Logger* _log; - - /// HyperHDR instance - HyperHdrInstance* _hyperhdr; - - /// The interval at which to update the leds (msec) - int64_t _updateInterval; - - /// The time after which the updated led values have been fully applied (msec) - int64_t _settlingTime; - - /// The Qt timer object - QTimer* _timer; - - /// The target led data - std::vector _targetValues; - - /// The previously written led data - std::vector _previousValues; - std::vector _previousTimeouts; - - /// Flag for dis/enable continuous output to led device regardless there is new data or not - bool _continuousOutput; - - int32_t _antiFlickeringTreshold; - - int32_t _antiFlickeringStep; - - int64_t _antiFlickeringTimeout; - - bool _flushFrame; - - int64_t _targetTime; - - int64_t _previousTime; - - /// Flag for pausing - bool _pause; - - enum class SmoothingType { Linear = 0, Alternative = 1 }; - - class SmoothingCfg - { - public: - bool _pause; - int64_t _settlingTime; - int64_t _updateInterval; - bool _directMode; - SmoothingType _type; - int _antiFlickeringTreshold; - int _antiFlickeringStep; - int64_t _antiFlickeringTimeout; - - SmoothingCfg(); - - SmoothingCfg(bool pause, int64_t settlingTime, int64_t updateInterval, bool directMode, SmoothingType type = SmoothingType::Linear, int antiFlickeringTreshold = 0, int antiFlickeringStep = 0, int64_t antiFlickeringTimeout = 0); - - static QString EnumToString(SmoothingType type); - }; - - /// smooth config list - std::vector _cfgList; - - unsigned _currentConfigId; - bool _enabled; - bool _directMode; - SmoothingType _smoothingType; - bool _infoUpdate; - bool _infoInput; - int _coolDown; - int _debugCounter; -}; diff --git a/include/base/MessageForwarder.h b/include/base/MessageForwarder.h deleted file mode 100644 index 52b2e41c9..000000000 --- a/include/base/MessageForwarder.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -// STL includes -#include -#include -#include -#include - -// QT includes -#include -#include -#include -#include -#include -#include - -// Utils includes -#include -#include -#include -#include -#include - -#include - -// Forward declaration -class HyperHdrInstance; -class QTcpSocket; -class FlatBufferConnection; -class MessageForwarderHelper; - -class MessageForwarder : public QObject -{ - Q_OBJECT - -public: - MessageForwarder(HyperHdrInstance* hyperhdr); - ~MessageForwarder() override; - - void addJsonSlave(const QString& slave); - void addFlatbufferSlave(const QString& slave); - -private slots: - /// - /// @brief Handle settings update from Hyperhdr Settingsmanager emit or this constructor - /// @param type settingyType from enum - /// @param config configuration object - /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - - /// - /// @brief Handle component state change MessageForwarder - /// @param component The component from enum - /// @param enable The new state - /// - void handleCompStateChangeRequest(hyperhdr::Components component, bool enable); - - /// - /// @brief Handle priority updates from Priority Muxer - /// @param priority The new visible priority - /// - void handlePriorityChanges(quint8 priority); - - /// - /// @brief Forward message to all json slaves - /// @param message The JSON message to send - /// - void forwardJsonMessage(const QJsonObject& message); - - /// - /// @brief Forward image to all flatbuffer slaves - /// @param image The flatbuffer image to send - /// - void forwardFlatbufferMessage(const QString& name, const Image& image); - - /// - /// @brief Forward message to a single json slave - /// @param message The JSON message to send - /// @param socket The TCP-Socket with the connection to the slave - /// - void sendJsonMessage(const QJsonObject& message, QTcpSocket* socket); - -private: - /// Hyperhdr instance - HyperHdrInstance* _hyperhdr; - - /// Logger instance - Logger* _log; - - /// Muxer instance - PriorityMuxer* _muxer; - - // JSON connection for forwarding - QStringList _jsonSlaves; - - /// Proto connection for forwarding - QStringList _flatSlaves; - - /// Flag if forwarder is enabled - bool _forwarder_enabled = true; - - const int _priority; - - MessageForwarderHelper* _messageForwarderHelper; -}; - -class MessageForwarderHelper : public QObject -{ - Q_OBJECT - -private: - QList _forwardClients; - bool _free; - -public: - MessageForwarderHelper(); - - ~MessageForwarderHelper(); - -signals: - void addClient(const QString& origin, const QString& address, int priority, bool skipReply); - void clearClients(); - -public slots: - bool isFree(); - void forwardImage(const Image& image); - void addClientHandler(const QString& origin, const QString& address, int priority, bool skipReply); - void clearClientsHandler(); -}; diff --git a/include/base/MultiColorAdjustment.h b/include/base/MultiColorAdjustment.h deleted file mode 100644 index b73ff7d3a..000000000 --- a/include/base/MultiColorAdjustment.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -// STL includes -#include -#include -#include -#include - - -#include -#include - -/// -/// The LedColorTransform is responsible for performing color transformation from 'raw' colors -/// received as input to colors mapped to match the color-properties of the LEDs. -/// -class MultiColorAdjustment -{ -public: - MultiColorAdjustment(quint8 instance, int ledCnt); - ~MultiColorAdjustment(); - - /** - * Adds a new ColorAdjustment to this MultiColorTransform - * - * @param adjustment The new ColorAdjustment (ownership is transferred) - */ - void addAdjustment(ColorAdjustment* adjustment); - - void setAdjustmentForLed(const QString& id, int startLed, int endLed); - - bool verifyAdjustments() const; - - void setBacklightEnabled(bool enable); - - /// - /// Returns the identifier of all the unique ColorAdjustment - /// - /// @return The list with unique id's of the ColorAdjustment - QStringList getAdjustmentIds() const; - - /// - /// Returns the pointer to the ColorAdjustment with the given id - /// - /// @param id The identifier of the ColorAdjustment - /// - /// @return The ColorAdjustment with the given id (or nullptr if it does not exist) - /// - ColorAdjustment* getAdjustment(const QString& id); - - /// - /// Performs the color adjustment from raw-color to led-color - /// - /// @param ledColors The list with raw colors - /// - void applyAdjustment(std::vector& ledColors); - - static MultiColorAdjustment* createLedColorsAdjustment(quint8 instance, int ledCnt, const QJsonObject& colorConfig); - -private: - /// List with transform ids - QStringList _adjustmentIds; - - /// List with unique ColorTransforms - std::vector _adjustment; - - /// List with a pointer to the ColorAdjustment for each individual led - std::vector _ledAdjustments; - - // logger instance - Logger* _log; -}; diff --git a/include/base/Muxer.h b/include/base/Muxer.h new file mode 100644 index 000000000..e711e341c --- /dev/null +++ b/include/base/Muxer.h @@ -0,0 +1,87 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + + #include + #include + + #include + #include + #include +#endif + +#define SMOOTHING_DEFAULT_CONFIG 0 + +class QTimer; +class Logger; + +class Muxer : public QObject +{ + Q_OBJECT +public: + struct InputInfo + { + int priority; + int64_t timeout; + ColorRgb staticColor; + hyperhdr::Components componentId; + QString origin; + unsigned smooth_cfg; + QString owner; + }; + + const static int LOWEST_PRIORITY; + const static int HIGHEST_EFFECT_PRIORITY; + const static int LOWEST_EFFECT_PRIORITY; + + Muxer(int instanceIndex, int ledCount, QObject* parent); + ~Muxer() override; + + void setEnable(bool enable); + bool setSourceAutoSelectEnabled(bool enabel, bool update = true); + bool isSourceAutoSelectEnabled() const { return _sourceAutoSelectEnabled; } + bool setPriority(int priority); + int getCurrentPriority() const { return _currentPriority; } + int getPreviousPriority() const { return _previousPriority; } + bool hasPriority(int priority) const; + QList getPriorities() const; + const InputInfo& getInputInfo(int priority) const; + const QMap& getInputInfoTable() const; + void registerInput(int priority, hyperhdr::Components component, const QString& origin = "System", const ColorRgb& staticColor = ColorRgb::BLACK, unsigned smooth_cfg = SMOOTHING_DEFAULT_CONFIG, const QString& owner = ""); + bool setInput(int priority, int64_t timeout_ms); + bool setInputInactive(int priority); + bool clearInput(int priority); + void clearAll(bool forceClearAll = false); + +signals: + void SignalTimeRunner_Internal(); + void SignalVisiblePriorityChanged(quint8 priority); + void SignalVisibleComponentChanged(hyperhdr::Components comp); + void SignalPrioritiesChanged(); + void SignalTimeTrigger_Internal(); + +private slots: + void timeTrigger(); + void setCurrentTime(); + +private: + hyperhdr::Components getComponentOfPriority(int priority) const; + + Logger* _log; + int _currentPriority; + int _previousPriority; + int _manualSelectedPriority; + hyperhdr::Components _prevVisComp = hyperhdr::COMP_INVALID; + QMap _activeInputs; + InputInfo _lowestPriorityInfo; + bool _sourceAutoSelectEnabled; + + QTimer* _updateTimer; + QTimer* _timer; + QTimer* _blockTimer; +}; diff --git a/include/base/NetworkForwarder.h b/include/base/NetworkForwarder.h new file mode 100644 index 000000000..045c80ed3 --- /dev/null +++ b/include/base/NetworkForwarder.h @@ -0,0 +1,66 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + + #include + #include + #include + #include + #include +#endif + +// Forward declaration +class HyperHdrInstance; +class QTcpSocket; +class FlatBufferConnection; + +class NetworkForwarder : public QObject +{ + Q_OBJECT + +public: + NetworkForwarder(); + ~NetworkForwarder() override; + + void addJsonSlave(const QString& slave, const QJsonObject& obj); + void addFlatbufferSlave(const QString& slave, const QJsonObject& obj); + +signals: + void SignalForwardImage(); + +public slots: + void startedHandler(); + void signalForwardImageHandler(); + void handlerInstanceImageUpdated(const Image& ret); + +private slots: + void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + void handleCompStateChangeRequest(hyperhdr::Components component, bool enable); + void forwardJsonMessage(const QJsonObject& message); + void sendJsonMessage(const QJsonObject& message, QTcpSocket* socket); + +private: + std::weak_ptr _instanceZero; + + Logger* _log; + QStringList _jsonSlaves; + QStringList _flatSlaves; + bool _forwarderEnabled; + + const int _priority; + + QList _forwardClients; + std::atomic _hasImage; + Image _image; +}; diff --git a/include/base/PriorityMuxer.h b/include/base/PriorityMuxer.h deleted file mode 100644 index 6dcc8efa6..000000000 --- a/include/base/PriorityMuxer.h +++ /dev/null @@ -1,282 +0,0 @@ -#pragma once - -// STL includes -#include -#include - -// QT includes -#include -#include -#include -#include -#include - -// Utils includes -#include -#include -#include - -// global defines -#define SMOOTHING_MODE_DEFAULT 0 -#define SMOOTHING_MODE_PAUSE 1 - -class QTimer; -class Logger; - -/// -/// The PriorityMuxer handles the priority channels. Led values input/ images are written to the priority map -/// and the muxer keeps track of all active priorities. The current priority can be queried and per -/// priority the led colors. Handles also manual/auto selection mode, provides a lot of signals to hook into priority related events -/// -class PriorityMuxer : public QObject -{ - Q_OBJECT -public: - /// - /// The information structure for a single priority channel - /// - struct InputInfo - { - /// The priority of this channel - int priority; - /// The absolute timeout of the channel - int64_t timeoutTime_ms; - /// The colors for each led of the channel - std::vector ledColors; - /// The raw Image (size should be preprocessed!) - Image image; - /// The component - hyperhdr::Components componentId; - /// Who set it - QString origin; - /// id of smoothing config - unsigned smooth_cfg; - /// specific owner description - QString owner; - }; - - /// The lowest possible priority, which is used when no priority channels are active - const static int LOWEST_PRIORITY; - - const static int HIGHEST_EFFECT_PRIORITY; - const static int LOWEST_EFFECT_PRIORITY; - - /// - /// Constructs the PriorityMuxer for the given number of LEDs (used to switch to black when - /// there are no priority channels - /// - /// @param ledCount The number of LEDs - /// - PriorityMuxer(int instanceIndex, int ledCount, QObject* parent); - - /// - /// Destructor - /// - ~PriorityMuxer() override; - - /// - /// @brief Start/Stop the PriorityMuxer update timer; On disabled no priority and timeout updates will be performend - /// @param enable The new state - /// - void setEnable(bool enable); - - /// @brief Enable or disable auto source selection - /// @param enable True if it should be enabled else false - /// @param update True to update _currentPriority - INTERNAL usage. - /// @return True if changed has been applied, false if the state is unchanged - /// - bool setSourceAutoSelectEnabled(bool enabel, bool update = true); - - /// - /// @brief Get the state of source auto selection - /// @return True if enabled, else false - /// - bool isSourceAutoSelectEnabled() const { return _sourceAutoSelectEnabled; } - - /// - /// @brief Overwrite current lowest priority with manual selection; On success disables auto selection - /// @param priority The - /// @return True on success, false if priority not found - /// - bool setPriority(int priority); - - /// - /// @brief Update all LED-Colors with min length of >= 1 to fit the new led length - /// @param[in] ledCount The count of LEDs - /// - void updateLedColorsLength(int ledCount); - - /// - /// Returns the current priority - /// - /// @return The current priority - /// - int getCurrentPriority() const { return _currentPriority; } - - /// - /// Returns the previous priority before current priority - /// - /// @return The previous priority - /// - int getPreviousPriority() const { return _previousPriority; } - - /// - /// Returns the state (enabled/disabled) of a specific priority channel - /// @param priority The priority channel - /// @return True if the priority channel exists else false - /// - bool hasPriority(int priority) const; - - /// - /// Returns the number of active priorities - /// - /// @return The list with active priorities - /// - QList getPriorities() const; - - /// - /// Returns the information of a specified priority channel. - /// If a priority is no longer available the _lowestPriorityInfo (255) is returned - /// - /// @param priority The priority channel - /// - /// @return The information for the specified priority channel - /// - const InputInfo& getInputInfo(int priority) const; - - const QMap& getInputInfoTable() const; - - /// - /// @brief Register a new input by priority, the priority is not active (timeout -100 isn't muxer recognized) until you start to update the data with setInput() - /// A repeated call to update the base data of a known priority won't overwrite their current timeout - /// @param[in] priority The priority of the channel - /// @param[in] component The component of the channel - /// @param[in] origin Who set the channel (CustomString@IP) - /// @param[in] owner Specific owner string, might be empty - /// @param[in] smooth_cfg The smooth id to use - /// - void registerInput(int priority, hyperhdr::Components component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = SMOOTHING_MODE_DEFAULT); - - /// - /// @brief Update the current color of a priority (previous registered with registerInput()) - /// @param priority The priority to update - /// @param ledColors The colors - /// @param timeout_ms The new timeout (defaults to -1 endless) - /// @return True on success, false when priority is not found - /// - bool setInput(int priority, const std::vector& ledColors, int64_t timeout_ms = -1); - - /// - /// @brief Update the current image of a priority (prev registered with registerInput()) - /// @param priority The priority to update - /// @param image The new image - /// @param timeout_ms The new timeout (defaults to -1 endless) - /// @return True on success, false when priority is not found - /// - bool setInputImage(int priority, const Image& image, int64_t timeout_ms = -1); - - /// - /// @brief Set the given priority to inactive - /// @param priority The priority - /// @return True on success false if not found - /// - bool setInputInactive(int priority); - - /// - /// Clears the specified priority channel and update _currentPriority on success - /// - /// @param[in] priority The priority of the channel to clear - /// @return True if priority has been cleared else false (not found) - /// - bool clearInput(int priority); - - /// - /// Clears all priority channels - /// - void clearAll(bool forceClearAll = false); - - /// - /// @brief Queue a manual push where muxer doesn't recognize them (e.g. continuous single color pushes) - /// - void queuePush() { emit timeRunner(); } - - void updateLedsValues(int priority, const std::vector& ledColors); - -signals: - /// - /// @brief Signal which emits when a effect or color with timeout > -1 is running, once per second - /// - void timeRunner(); - - /// - /// @brief Emits whenever the visible priority has changed - /// @param priority The new visible priority - /// - void visiblePriorityChanged(quint8 priority); - - /// - /// @brief Emits whenever the current visible component changed - /// @param comp The new component - /// - void visibleComponentChanged(hyperhdr::Components comp); - - /// - /// @brief Emits whenever something changes which influences the priorities listing - /// Emits also in 1s interval when a COLOR or EFFECT is running with a timeout > -1 - /// - void prioritiesChanged(); - - /// - /// internal used signal to resolve treading issues with timer - /// - void signalTimeTrigger(); - -private slots: - /// - /// Slot which is called to adapt to 1s interval for signal timeRunner() / prioritiesChanged() - /// - void timeTrigger(); - - /// - /// Updates the current time. Channels with a configured time out will be checked and cleared if - /// required. - /// - void setCurrentTime(); - -private: - /// - /// @brief Get the component of the given priority - /// @return The component - /// - hyperhdr::Components getComponentOfPriority(int priority) const; - - /// Logger instance - Logger* _log; - - /// The current priority (lowest value in _activeInputs) - int _currentPriority; - - /// The previous priority before current priority - int _previousPriority; - - /// The manual select priority set with setPriority - int _manualSelectedPriority; - - // The last visible component - hyperhdr::Components _prevVisComp = hyperhdr::COMP_INVALID; - - /// The mapping from priority channel to led-information - QMap _activeInputs; - - /// The information of the lowest priority channel - InputInfo _lowestPriorityInfo; - - // Reflect the state of auto select - bool _sourceAutoSelectEnabled; - - // Timer to update Muxer times independent - QTimer* _updateTimer; - - QTimer* _timer; - QTimer* _blockTimer; -}; diff --git a/include/utils/RawUdpServer.h b/include/base/RawUdpServer.h similarity index 78% rename from include/utils/RawUdpServer.h rename to include/base/RawUdpServer.h index 47a206cb5..29637c8d6 100644 --- a/include/utils/RawUdpServer.h +++ b/include/base/RawUdpServer.h @@ -4,7 +4,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -27,12 +27,12 @@ * SOFTWARE. */ -// utils -#include -#include +#ifndef PCH_ENABLED + #include -// qt -#include + #include + #include +#endif class QUdpSocket; class NetOrigin; @@ -44,42 +44,26 @@ class RawUdpServer : public QObject Q_OBJECT public: - RawUdpServer(HyperHdrInstance* hyperhdr, const QJsonDocument& config, QObject* parent = nullptr); + RawUdpServer(HyperHdrInstance* ownerInstance, const QJsonDocument& config); ~RawUdpServer() override; public slots: - /// - /// @brief Handle settings update - /// @param type The type from enum - /// @param config The configuration - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - void initServer(); - void readPendingDatagrams(); - void dataTimeout(); private: - /// - /// @brief Start the server with current _port - /// void startServer(); - - /// - /// @brief Stop server - /// void stopServer(); -private: QUdpSocket* _server; Logger* _log; quint16 _port; int _priority; bool _initialized; - HyperHdrInstance* _hyperhdr; + HyperHdrInstance* _ownerInstance; const QJsonDocument _config; QTimer* _inactiveTimer; }; diff --git a/include/base/SettingsManager.h b/include/base/SettingsManager.h deleted file mode 100644 index f21bb4bc7..000000000 --- a/include/base/SettingsManager.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include - -// qt includes -#include - - -class SettingsTable; - -/// -/// @brief Manage the settings read write from/to configuration file, on settings changed will emit a signal to update components accordingly -/// -class SettingsManager : public QObject -{ - Q_OBJECT - -public: - /// - /// @brief Construct a settings manager and assign a hyperhdr instance - /// @params instance Instance index of HyperHDRInstanceManager - /// @params parent The parent hyperhdr instance - /// - SettingsManager(quint8 instance, QObject* parent = nullptr, bool readonlyMode = false); - - /// - /// @brief Save a complete json configuration - /// @param config The entire config object - /// @param correct If true will correct json against schema before save - /// @return True on success else false - /// - bool saveSettings(QJsonObject config, bool correct = false); - - /// - /// @brief get a single setting json from configuration - /// @param type The settings::type from enum - /// @return The requested json data as QJsonDocument - /// - QJsonDocument getSetting(settings::type type) const; - - void saveSetting(settings::type key, QString saveData); - - /// - /// @brief get the full settings object of this instance (with global settings) - /// @return The requested json - /// - QJsonObject getSettings() const; - -signals: - /// - /// @brief Emits whenever a configuration part changed. - /// @param type The settings type from enum - /// @param data The data as QJsonDocument - /// - void settingsChanged(settings::type type, const QJsonDocument& data); - -private: - /// Logger instance - Logger* _log; - - /// instance of database table interface - SettingsTable* _sTable; - - /// the schema - static QJsonObject schemaJson; - - /// the current configuration of this instance - QJsonObject _qconfig; - - bool _readonlyMode; -}; diff --git a/include/base/Smoothing.h b/include/base/Smoothing.h new file mode 100644 index 000000000..1b080d902 --- /dev/null +++ b/include/base/Smoothing.h @@ -0,0 +1,113 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + + #include + #include +#endif + +#include + +class QTimer; +class Logger; +class HyperHdrInstance; + +class Smoothing : public QObject +{ + Q_OBJECT + +public: + Smoothing(const QJsonDocument& config, HyperHdrInstance* hyperhdr); + + void SetEnable(bool enable); + bool isPaused() const; + bool isEnabled() const; + + void UpdateLedValues(const std::vector& ledValues); + unsigned UpdateConfig(unsigned cfgID, int settlingTime_ms, double ledUpdateFrequency_hz, bool directModee); + void UpdateCurrentConfig(int settlingTime_ms); + bool SelectConfig(unsigned cfg, bool force); + int GetSuggestedInterval(); + +signals: + void SignalMasterClockTick(); + +public slots: + void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + +private slots: + + void updateLeds(); + void componentStateChange(hyperhdr::Components component, bool state); + +private: + + void queueColors(const std::vector& ledColors); + void clearQueuedColors(bool deviceEnabled = false, bool restarting = false); + inline uint8_t computeColor(int64_t k, int color); + void setupAdvColor(int64_t deltaTime, float& kOrg, float& kMin, float& kMid, float& kAbove, float& kMax); + inline uint8_t computeAdvColor(int limitMin, int limitAverage, int limitMax, float kMin, float kMid, float kAbove, float kMax, int color); + + unsigned addConfig(int settlingTime_ms, double ledUpdateFrequency_hz = 25.0, bool directMode = false); + uint8_t clamp(int x); + void antiflickering(std::vector& newTarget); + void linearSetup(const std::vector& ledValues); + void linearSmoothingProcessing(bool correction); + void debugOutput(std::vector& _targetValues); + + Logger* _log; + HyperHdrInstance* _hyperhdr; + int64_t _updateInterval; + int64_t _settlingTime; + QMutex _setupSynchro, _dataSynchro; + + std::vector _targetColorsUnsafe; + std::vector _targetColorsCopy; + std::vector _currentColors; + std::vector _currentTimeouts; + + + bool _continuousOutput; + int32_t _antiFlickeringTreshold; + int32_t _antiFlickeringStep; + int64_t _antiFlickeringTimeout; + bool _flushFrame; + int64_t _targetTimeUnsafe; + int64_t _targetTimeCopy; + int64_t _previousTime; + bool _pause; + + enum class SmoothingType { Linear = 0, Alternative = 1 }; + + struct SmoothingConfig + { + bool pause; + int64_t settlingTime; + int64_t updateInterval; + bool directMode; + SmoothingType type; + int antiFlickeringTreshold; + int antiFlickeringStep; + int64_t antiFlickeringTimeout; + + SmoothingConfig(bool __pause, int64_t __settlingTime, int64_t __updateInterval, bool __directMode, + SmoothingType __type = SmoothingType::Linear, int __antiFlickeringTreshold = 0, int __antiFlickeringStep = 0, + int64_t __antiFlickeringTimeout = 0); + + static QString EnumToString(SmoothingType type); + }; + + std::vector> _configurations; + + unsigned _currentConfigId; + std::atomic_bool _enabled; + std::atomic_bool _hasData; + bool _connected; + bool _directMode; + SmoothingType _smoothingType; + bool _infoUpdate; + bool _infoInput; + int _coolDown; +}; diff --git a/include/base/SoundCapture.h b/include/base/SoundCapture.h index 943c2dd57..e6e686cee 100644 --- a/include/base/SoundCapture.h +++ b/include/base/SoundCapture.h @@ -1,131 +1,60 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include -#define SOUNDCAP_N_WAVE 1024 -#define SOUNDCAP_LOG2_N_WAVE 10 + #include + #include +#endif -#define SOUNDCAP_RESULT_RES 8 +#include -class SoundCaptureResult -{ - friend class SoundCapture; - - int32_t _maxAverage; - bool _validData; - int32_t pureResult[SOUNDCAP_RESULT_RES]; - int32_t lastResult[SOUNDCAP_RESULT_RES]; - - int32_t maxResult[SOUNDCAP_RESULT_RES]; - uint8_t pureScaledResult[SOUNDCAP_RESULT_RES]; - uint8_t deltas[SOUNDCAP_RESULT_RES]; - uint8_t buffScaledResult[SOUNDCAP_RESULT_RES]; - QColor color[SOUNDCAP_RESULT_RES]; - int32_t averageDelta; - - QColor _lastPrevColor; - int32_t _scaledAverage; - int32_t _oldScaledAverage; - int32_t _currentMax; - - MovingTarget mtWorking, mtInternal; - - -public: - SoundCaptureResult(); - - void ClearResult(); - - void AddResult(int samplerIndex, uint32_t val); - - void Smooth(); - - bool isDataValid(); - - void ResetData(); - - void GetBufResult(uint8_t* dest, size_t size); - - QColor getRangeColor(uint8_t index) const; - - void RestoreFullLum(QColor& color, int scale = 32); +class AnimationBaseMusic; - int32_t getValue(int isMulti); - - int32_t getValue3Step(int isMulti); - - bool GetStats(uint32_t& scaledAverage, uint32_t& currentMax, QColor& averageColor, QColor* fastColor = NULL, QColor* slowColor = NULL); - -private: - bool hasMiddleAverage(int middle); - - bool hasMiddleSlow(int middle); - - bool hasMiddleFast(int middle); - - void CalculateRgbDelta(QColor currentColor, QColor prevColor, QColor selcolor, int& ab_r, int& ab_g, int& ab_b); -}; - -class SoundCapture : public QObject +class SoundCapture : public QObject { Q_OBJECT protected: - static SoundCapture* _soundInstance; + Logger* _logger; SoundCapture(const QJsonDocument& effectConfig, QObject* parent = nullptr); ~SoundCapture(); - - static bool AnaliseSpectrum(int16_t soundBuffer[], int sizeP); - - QList _availableDevices; - bool _isActive; - QString _selectedDevice; - static bool _isRunning; - QList _instances; - static uint32_t _resultIndex; - static SoundCaptureResult _resultFFT; + + QList _availableDevices; + bool _isActive; + QString _selectedDevice; + QString _normalizedName; + bool _isRunning; + QList _instances; public: - static SoundCapture* getInstance(); + bool analyzeSpectrum(int16_t soundBuffer[], int sizeP); - SoundCaptureResult* hasResult(uint32_t& lastIndex); SoundCaptureResult* hasResult(AnimationBaseMusic* effect, uint32_t& lastIndex, bool* newAverage, bool* newSlow, bool* newFast, int* isMulti); - void ForcedClose(); + virtual void start() = 0; + virtual void stop() = 0; public slots: QJsonObject getJsonInfo(); - uint32_t getCaptureInstance(); - void releaseCaptureInstance(uint32_t instance); - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + uint32_t open(); + void close(uint32_t instance); + void settingsChangedHandler(settings::type type, const QJsonDocument& config); private: QList getDevices() const; bool getActive() const; QString getSelectedDevice() const; - - virtual void Start() = 0; - virtual void Stop() = 0; - uint32_t _maxInstance; static uint32_t _noSoundCounter; static bool _noSoundWarning; static bool _soundDetectedInfo; - static QSemaphore _semaphore; - - - // FFT - static int16_t _imgBuffer[SOUNDCAP_N_WAVE * 2]; - static int16_t _lutSin[SOUNDCAP_N_WAVE - SOUNDCAP_N_WAVE / 4]; - static inline int16_t FIX_MPY(int16_t a, int16_t b); static int32_t fix_fft(int16_t fr[], int16_t fi[], int16_t exp, bool inverse); }; diff --git a/include/base/SoundCaptureResult.h b/include/base/SoundCaptureResult.h new file mode 100644 index 000000000..14c6e29bd --- /dev/null +++ b/include/base/SoundCaptureResult.h @@ -0,0 +1,78 @@ +#pragma once + +#ifndef PCH_ENABLED + #include +#endif + +#define SOUNDCAP_N_WAVE 1024 +#define SOUNDCAP_LOG2_N_WAVE 10 +#define SOUNDCAP_RESULT_RES 8 + +struct MovingTarget +{ + QColor _averageColor; + QColor _fastColor; + QColor _slowColor; + int32_t _targetAverageR; + int32_t _targetAverageG; + int32_t _targetAverageB; + int32_t _targetAverageCounter; + int32_t _targetSlowR; + int32_t _targetSlowG; + int32_t _targetSlowB; + int32_t _targetSlowCounter; + int32_t _targetFastR; + int32_t _targetFastG; + int32_t _targetFastB; + int32_t _targetFastCounter; + + void Clear(); + void CopyFrom(MovingTarget* source); +}; + +class SoundCaptureResult +{ + friend class SoundCapture; + + int32_t _maxAverage; + bool _validData; + uint32_t _resultIndex; + int32_t pureResult[SOUNDCAP_RESULT_RES]; + int32_t lastResult[SOUNDCAP_RESULT_RES]; + + int32_t maxResult[SOUNDCAP_RESULT_RES]; + uint8_t pureScaledResult[SOUNDCAP_RESULT_RES]; + uint8_t deltas[SOUNDCAP_RESULT_RES]; + uint8_t buffScaledResult[SOUNDCAP_RESULT_RES]; + QColor color[SOUNDCAP_RESULT_RES]; + int32_t averageDelta; + + QColor _lastPrevColor; + int32_t _scaledAverage; + int32_t _oldScaledAverage; + int32_t _currentMax; + + MovingTarget mtWorking, mtInternal; + + +public: + SoundCaptureResult(); + + void ClearResult(); + void AddResult(int samplerIndex, uint32_t val); + void Smooth(); + uint32_t getResultIndex(); + void ResetData(); + void GetBufResult(uint8_t* dest, size_t size); + QColor getRangeColor(uint8_t index) const; + void RestoreFullLum(QColor& color, int scale = 32); + int32_t getValue(int isMulti); + int32_t getValue3Step(int isMulti); + bool GetStats(uint32_t& scaledAverage, uint32_t& currentMax, QColor& averageColor, QColor* fastColor = NULL, QColor* slowColor = NULL); + +private: + bool hasMiddleAverage(int middle); + bool hasMiddleSlow(int middle); + bool hasMiddleFast(int middle); + void CalculateRgbDelta(QColor currentColor, QColor prevColor, QColor selcolor, int& ab_r, int& ab_g, int& ab_b); +}; diff --git a/include/base/SystemControl.h b/include/base/SystemControl.h index 251cd9369..bf2cdf1eb 100644 --- a/include/base/SystemControl.h +++ b/include/base/SystemControl.h @@ -1,69 +1,43 @@ #pragma once -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + + #include + #include + #include + #include +#endif class HyperHdrInstance; -/// -/// @brief Capture Control class which is a interface to the HyperHdrDaemon native capture classes. -/// It controls the instance based enable/disable of capture feeds and PriorityMuxer registrations -/// class SystemControl : public QObject { Q_OBJECT public: SystemControl(HyperHdrInstance* hyperhdr); + ~SystemControl(); - quint8 getCapturePriority() - { - return _sysCaptPrio; - } + quint8 getCapturePriority(); bool isCEC(); -signals: - void setSysCaptureEnableSignal(bool enable); + void setSysCaptureEnable(bool enable); private slots: - void setSysCaptureEnable(bool enable); - /// - /// @brief Handle component state change of V4L and SystemCapture - /// @param component The component from enum - /// @param enable The new state - /// void handleCompStateChangeRequest(hyperhdr::Components component, bool enable); - - /// - /// @brief Handle settings update from HyperHdr Settingsmanager emit or this constructor - /// @param type settingyType from enum - /// @param config configuration object - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - - /// - /// @brief forward v4l image - /// @param image The image - /// void handleSysImage(const QString& name, const Image& image); - - /// - /// @brief Is called from _v4lInactiveTimer to set source after specific time to inactive - /// void setSysInactive(); private: - /// HyperHdr instance HyperHdrInstance* _hyperhdr; bool _sysCaptEnabled; bool _alive; quint8 _sysCaptPrio; QString _sysCaptName; - QTimer _sysInactiveTimer; + QTimer* _sysInactiveTimer; bool _isCEC; }; diff --git a/include/base/SystemWrapper.h b/include/base/SystemWrapper.h index 74bde6323..0b94c1655 100644 --- a/include/base/SystemWrapper.h +++ b/include/base/SystemWrapper.h @@ -1,23 +1,25 @@ #pragma once -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include +#endif -#include -#include -#include -#include -#include #include class GlobalSignals; class QTimer; -/// List of HyperHDR instances that requested screen capt static QList GRABBER_SYSTEM_CLIENTS; class SystemWrapper : public QObject @@ -28,28 +30,21 @@ class SystemWrapper : public QObject ~SystemWrapper() override; - static SystemWrapper* instance; - static SystemWrapper* getInstance() { return instance; } - virtual bool isActivated(bool forced); public slots: QJsonObject getJsonInfo(); - void newFrame(const Image& image); - void readError(const char* err); + void newCapturedFrameHandler(const Image& image); + void capturingExceptionHandler(const char* err); bool start(); void stop(); private slots: - void handleSourceRequest(hyperhdr::Components component, int hyperHdrInd, bool listen); + void signalRequestSourceHandler(hyperhdr::Components component, int hyperHdrInd, bool listen); signals: - /// - /// @brief Emit the final processed image - /// - void systemImage(const QString& name, const Image& image); - void StateChanged(QString device, QString videoMode); + void SignalSystemImage(const QString& name, const Image& image); public slots: void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold); @@ -62,12 +57,8 @@ public slots: protected: virtual QString getGrabberInfo(); - QString _grabberName; - Logger* _log; - bool _configLoaded; - Grabber* _grabber; }; diff --git a/include/base/VideoControl.h b/include/base/VideoControl.h index f461d34bb..ad049463a 100644 --- a/include/base/VideoControl.h +++ b/include/base/VideoControl.h @@ -1,71 +1,50 @@ #pragma once -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + + #include + #include + #include + #include +#endif class HyperHdrInstance; -/// -/// @brief Capture Control class which is a interface to the HyperHdrDaemon native capture classes. -/// It controls the instance based enable/disable of capture feeds and PriorityMuxer registrations -/// class VideoControl : public QObject { Q_OBJECT public: VideoControl(HyperHdrInstance* hyperhdr); + ~VideoControl(); - quint8 getCapturePriority() - { - return _usbCaptPrio; - } - + quint8 getCapturePriority(); bool isCEC(); - -signals: - void setUsbCaptureEnableSignal(bool enable); + void setUsbCaptureEnable(bool enable); private slots: - void setUsbCaptureEnable(bool enable); - /// - /// @brief Handle component state change of V4L and SystemCapture - /// @param component The component from enum - /// @param enable The new state - /// void handleCompStateChangeRequest(hyperhdr::Components component, bool enable); - - /// - /// @brief Handle settings update from HyperHdr Settingsmanager emit or this constructor - /// @param type settingyType from enum - /// @param config configuration object - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - - /// - /// @brief forward v4l image - /// @param image The image - /// - void handleUsbImage(const QString& name, const Image& image); - - /// - /// @brief Is called from _v4lInactiveTimer to set source after specific time to inactive - /// + void handleUsbImage(); + void handleIncomingUsbImage(const QString& name, const Image& image); void setUsbInactive(); private: - /// HyperHdr instance HyperHdrInstance* _hyperhdr; bool _usbCaptEnabled; bool _alive; quint8 _usbCaptPrio; QString _usbCaptName; - QTimer _usbInactiveTimer; + QTimer* _usbInactiveTimer; bool _isCEC; + struct { + QMutex mutex; + QString name; + Image frame; + } incoming; + static bool _stream; }; diff --git a/include/blackborder/BlackBorderDetector.h b/include/blackborder/BlackBorderDetector.h index e314a9fa7..d209bc4c0 100644 --- a/include/blackborder/BlackBorderDetector.h +++ b/include/blackborder/BlackBorderDetector.h @@ -1,98 +1,34 @@ -//#include #pragma once -// Utils includes #include namespace hyperhdr { - /// - /// Result structure of the detected blackborder. - /// struct BlackBorder { - /// Flag indicating if the border is unknown bool unknown; - - /// The size of the detected horizontal border int horizontalSize; - - /// The size of the detected vertical border int verticalSize; - - /// - /// Compares this BlackBorder to the given other BlackBorder - /// - /// @param[in] other The other BlackBorder - /// - /// @return True if this is the same border as other - /// bool operator== (const BlackBorder& other) const; }; - /// - /// The BlackBorderDetector performs detection of black-borders on a single image. - /// The detector will search for the upper left corner of the picture in the frame. - /// Based on detected black pixels it will give an estimate of the black-border. - /// class BlackBorderDetector { public: - /// - /// Constructs a black-border detector - /// @param[in] threshold The threshold which the black-border detector should use - /// BlackBorderDetector(double threshold); - - /// - /// Performs the actual black-border detection on the given image - /// - /// @param[in] image The image on which detection is performed - /// - /// @return The detected (or not detected) black border info - /// - uint8_t calculateThreshold(double blackborderThreshold) const; - - /// - /// default detection mode (3lines 4side detection) BlackBorder process(const Image& image) const; - - /// - /// classic detection mode (topleft single line mode) BlackBorder process_classic(const Image& image) const; - - - /// - /// osd detection mode (find x then y at detected x to avoid changes by osd overlays) BlackBorder process_osd(const Image& image) const; - - - /// - /// letterbox detection mode (5lines top-bottom only detection) BlackBorder process_letterbox(const Image& image) const; - - private: - - /// - /// Checks if a given color is considered black and therefore could be part of the border. - /// - /// @param[in] color The color to check - /// - /// @return True if the color is considered black else false - /// - inline bool isBlack(const ColorRgb& color) const { - // Return the simple compare of the color against black return (color.red < _blackborderThreshold) && (color.green < _blackborderThreshold) && (color.blue < _blackborderThreshold); } private: - /// Threshold for the black-border detector [0 .. 255] const uint8_t _blackborderThreshold; - }; -} // end namespace hyperhdr +} diff --git a/include/blackborder/BlackBorderProcessor.h b/include/blackborder/BlackBorderProcessor.h index 3186e0307..f2e50c35e 100644 --- a/include/blackborder/BlackBorderProcessor.h +++ b/include/blackborder/BlackBorderProcessor.h @@ -1,129 +1,54 @@ #pragma once -// QT includes -#include +#ifndef PCH_ENABLED + #include +#endif -// util #include #include #include - -// Local HyperHDR includes -#include "BlackBorderDetector.h" +#include class HyperHdrInstance; namespace hyperhdr { - /// - /// The BlackBorder processor is a wrapper around the black-border detector for keeping track of - /// detected borders and count of the type and size of detected borders. - /// class BlackBorderProcessor : public QObject { Q_OBJECT public: BlackBorderProcessor(HyperHdrInstance* hyperhdr, QObject* parent); ~BlackBorderProcessor() override; - /// - /// Return the current (detected) border - /// @return The current border - /// - BlackBorder getCurrentBorder() const; - /// - /// Return activation state of black border detector - /// @return The current border - /// + BlackBorder getCurrentBorder() const; bool enabled() const; - /// - /// Set activation state of black border detector - /// @param enable current state - /// void setEnabled(bool enable); - - /// - /// Sets the _hardDisabled state, if True prevents the enable from COMP_BLACKBORDER state emit (mimics wrong state to external!) - /// It's not possible to enable black-border detection from this method, if the user requested a disable! - /// @param disable The new state - /// void setHardDisable(bool disable); - - /// - /// Processes the image. This performs detection of black-border on the given image and - /// updates the current border accordingly. If the current border is updated the method call - /// will return true else false - /// - /// @param image The image to process - /// - /// @return True if a different border was detected than the current else false - /// bool process(const Image& image); + signals: + void setNewComponentState(hyperhdr::Components component, bool state); + private slots: - /// - /// @brief Handle settings update from HyperHDR Settingsmanager emit or this constructor - /// @param type settingType from enum - /// @param config configuration object - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - - /// - /// @brief Handle component state changes, it's not possible for BB to be enabled, when a hardDisable is active - /// void handleCompStateChangeRequest(hyperhdr::Components component, bool enable); private: - /// HyperHDR instance - HyperHdrInstance* _hyperhdr; - - /// - /// Updates the current border based on the newly detected border. Returns true if the - /// current border has changed. - /// - /// @param newDetectedBorder The newly detected border - /// @return True if the current border changed else false - /// bool updateBorder(const BlackBorder& newDetectedBorder); - - /// flag for black-border detector usage bool _enabled; - - /// The number of unknown-borders detected before it becomes the current border unsigned _unknownSwitchCnt; - - /// The number of horizontal/vertical borders detected before it becomes the current border unsigned _borderSwitchCnt; - - // The number of frames that are "ignored" before a new border gets set as _previousDetectedBorder unsigned _maxInconsistentCnt; - - /// The number of pixels to increase a detected border for removing blurry pixels unsigned _blurRemoveCnt; - - /// The border detection mode QString _detectionMode; - - /// The black-border detector - BlackBorderDetector* _detector; - - /// The current detected border + std::unique_ptr _borderDetector; BlackBorder _currentBorder; - - /// The border detected in the previous frame BlackBorder _previousDetectedBorder; - - /// The number of frame the previous detected border matched the incoming border unsigned _consistentCnt; - /// The number of frame the previous detected border NOT matched the incoming border unsigned _inconsistentCnt; - /// old threshold double _oldThreshold; - /// True when disabled in specific situations, this prevents to enable BB when the visible priority requested a disable bool _hardDisabled; - /// Reflect the last component state request from user (comp change) bool _userEnabled; - }; -} // end namespace hyperhdr +} diff --git a/include/bonjour/bonjourservicehelper.h b/include/bonjour/BonjourServiceHelper.h similarity index 95% rename from include/bonjour/bonjourservicehelper.h rename to include/bonjour/BonjourServiceHelper.h index 8e9f24ea4..e65b5e373 100644 --- a/include/bonjour/bonjourservicehelper.h +++ b/include/bonjour/BonjourServiceHelper.h @@ -1,9 +1,11 @@ #pragma once -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif + #include class BonjourServiceHelper; diff --git a/include/bonjour/bonjourserviceregister.h b/include/bonjour/BonjourServiceRegister.h similarity index 63% rename from include/bonjour/bonjourserviceregister.h rename to include/bonjour/BonjourServiceRegister.h index 001e307c4..3e8ff8ee6 100644 --- a/include/bonjour/bonjourserviceregister.h +++ b/include/bonjour/BonjourServiceRegister.h @@ -1,11 +1,13 @@ -#ifndef BONJOURSERVICEREGISTER_H -#define BONJOURSERVICEREGISTER_H +#pragma once -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif #include -#include + +class BonjourServiceHelper; class BonjourServiceRegister : public QObject { @@ -25,12 +27,12 @@ public slots: void requestToScanHandler(DiscoveryRecord::Service type); void messageFromFriendHandler(bool isExists, QString mdnsString, QString serverName, int port); - void resolveIpHandler(QString serverName, QString ip); + void signalIpResolvedHandler(QString serverName, QString ip); void onThreadExits(); signals: - void messageFromFriend(bool isWelcome, QString mdnsString, QString serverName, int port); - void resolveIp(QString serverName, QString ip); + void SignalMessageFromFriend(bool isWelcome, QString mdnsString, QString serverName, int port); + void SignalIpResolved(QString serverName, QString ip); private: void resolveIps(); @@ -39,6 +41,5 @@ public slots: DiscoveryRecord _serviceRecord; QHash _ips; DiscoveryRecord _result; + int _retry; }; - -#endif // BONJOURSERVICEREGISTER_H diff --git a/include/bonjour/DiscoveryRecord.h b/include/bonjour/DiscoveryRecord.h index 693b18bce..3d12931f0 100644 --- a/include/bonjour/DiscoveryRecord.h +++ b/include/bonjour/DiscoveryRecord.h @@ -4,7 +4,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -27,64 +27,15 @@ * SOFTWARE. */ -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif class DiscoveryRecord { public: - enum Service { Unknown = 0, HyperHDR, WLED, PhilipsHue, Pico, ESP32_S2, ESP, SerialPort }; - - static const QString getmDnsHeader(Service service) - { - switch (service) - { - case(Service::PhilipsHue): return QLatin1String("_hue._tcp"); break; - case(Service::WLED): return QLatin1String("_wled._tcp"); break; - case(Service::HyperHDR): return QLatin1String("_hyperhdr-http._tcp"); break; - default: return "SERVICE_UNKNOWN"; - } - } - - const QString getName() const - { - return getName(type); - } - - static const QString getName(Service _type) - { - switch (_type) - { - case(Service::PhilipsHue): return "PhilipsHue"; break; - case(Service::WLED): return "WLED"; break; - case(Service::HyperHDR): return "HyperHDR"; break; - case(Service::Pico): return "Pico/RP2040"; break; - case(Service::ESP32_S2): return "ESP32-S2"; break; - case(Service::ESP): return "ESP board"; break; - default: return "SERVICE_UNKNOWN"; - } - } - - void resetTTL() - { - ttl = 7; - } - - bool expired() - { - ttl >>= 1; - if (type == Service::WLED) - return ttl == 0; - else if (type == Service::Pico || type == Service::ESP32_S2 || type == Service::ESP) - return ttl <= 2; - else - return ttl <= 1; - } - - DiscoveryRecord() : type(Service::Unknown), port(-1), isExists(false) - { - resetTTL(); - } + enum Service { Unknown = 0, HyperHDR, WLED, PhilipsHue, Pico, ESP32_S2, ESP, SerialPort, REFRESH_ALL }; Service type; QString hostName; @@ -92,21 +43,19 @@ class DiscoveryRecord int port; bool isExists; unsigned int ttl; - - bool operator==(const DiscoveryRecord& other) const - { - return type == other.type - && address == other.address - && port == other.port; - } + DiscoveryRecord(); + + static const QString getmDnsHeader(Service service); + static const QString getName(Service _type); + + const QString getName() const; + void resetTTL(); + bool expired(); + - bool operator!=(const DiscoveryRecord& other) const - { - return type != other.type - || address != other.address - || port != other.port; - } + bool operator==(const DiscoveryRecord& other) const; + bool operator!=(const DiscoveryRecord& other) const; }; Q_DECLARE_METATYPE(DiscoveryRecord) diff --git a/include/bonjour/DiscoveryWrapper.h b/include/bonjour/DiscoveryWrapper.h index 1b919ee9c..83466fff8 100644 --- a/include/bonjour/DiscoveryWrapper.h +++ b/include/bonjour/DiscoveryWrapper.h @@ -4,7 +4,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -27,9 +27,10 @@ * SOFTWARE. */ -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif #include #include @@ -38,36 +39,29 @@ class DiscoveryWrapper : public QObject { Q_OBJECT -private: - friend class HyperHdrDaemon; +private: Logger* _log; - LedDevice* _serialDevice; - - DiscoveryWrapper(QObject* parent = nullptr); + std::unique_ptr _serialDevice; public: + DiscoveryWrapper(QObject* parent = nullptr); ~DiscoveryWrapper(); - static DiscoveryWrapper* instance; - static DiscoveryWrapper* getInstance() { return instance; } - public slots: QList getPhilipsHUE(); QList getWLED(); QList getHyperHDRServices(); - QList getAllServices(); - void requestServicesScan(); + QList getAllServices(); - void discoveryEventHandler(DiscoveryRecord message); - void requestToScanHandler(DiscoveryRecord::Service type); + void signalDiscoveryEventHandler(DiscoveryRecord message); + void signalDiscoveryRequestToScanHandler(DiscoveryRecord::Service type); signals: - void foundService(DiscoveryRecord::Service type, QList records); - void discoveryEvent(DiscoveryRecord message); - void requestToScan(DiscoveryRecord::Service type); + void SignalDiscoveryFoundService(DiscoveryRecord::Service type, QList records); private: + void requestServicesScan(); void gotMessage(QList& target, DiscoveryRecord message); void cleanUp(QList& target); diff --git a/include/cec/WrapperCEC.h b/include/cec/WrapperCEC.h new file mode 100644 index 000000000..f696469ae --- /dev/null +++ b/include/cec/WrapperCEC.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include +#endif + +#include + +class GlobalSignals; + +static QList CEC_CLIENTS; + +class WrapperCEC : public QObject +{ + Q_OBJECT + +public: + WrapperCEC(); + ~WrapperCEC() override; + +public slots: + void sourceRequestHandler(hyperhdr::Components component, int hyperHdrInd, bool listen); + +signals: + void SignalStateChange(bool enabled, QString info); + void SignalKeyPressed(int keyCode); + +private: + void enable(bool enabled); + + cecHandler* _cecHandler; + Logger* _log; +}; diff --git a/include/cec/cecHandler.h b/include/cec/cecHandler.h index 6602872ca..1492f5f6e 100644 --- a/include/cec/cecHandler.h +++ b/include/cec/cecHandler.h @@ -3,7 +3,6 @@ #include #include #include -#include class cecHandler : public QObject { @@ -15,21 +14,11 @@ class cecHandler : public QObject bool start(); void stop(); + Logger* _log; signals: void stateChange(bool enabled, QString info); void keyPressed(int keyCode); - -private: - static void handleCecLogMessage(void* context, const CEC::cec_log_message* message); - static void handleCecCommandMessage(void* context, const CEC::cec_command* command); - static void handleCecKeyPress(void* context, const CEC::cec_keypress* key); - - CEC::ICECCallbacks _cecCallbacks; - CEC::libcec_configuration _cecConfig; - CEC::ICECAdapter* _cecAdapter; - - Logger* _log; }; diff --git a/include/commandline/IntOption.h b/include/commandline/IntOption.h index cb8a352e3..63e5eb37b 100644 --- a/include/commandline/IntOption.h +++ b/include/commandline/IntOption.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include "ValidatorOption.h" namespace commandline diff --git a/include/commandline/Option.h b/include/commandline/Option.h index 78f8fa38c..027d741e2 100644 --- a/include/commandline/Option.h +++ b/include/commandline/Option.h @@ -1,9 +1,14 @@ #pragma once -#include -#include -#include + +#ifndef PCH_ENABLED + #include + #include + #include + + #include +#endif + #include -#include #include #include @@ -35,7 +40,7 @@ namespace commandline QString name() const; QString getError() const; QString value(Parser& parser) const; - const char* getCString(Parser& parser) const; + const char* getCString(Parser& parser) const &; virtual ~Option(); diff --git a/include/db/AuthTable.h b/include/db/AuthTable.h index 81f107382..56a114103 100644 --- a/include/db/AuthTable.h +++ b/include/db/AuthTable.h @@ -2,156 +2,36 @@ #include -/// -/// @brief Authentication table interface -/// +namespace hyperhdr { + const QString NO_AUTH = "No Authorization"; + const QString DEFAULT_CONFIG_USER = "Hyperhdr"; + const QString DEFAULT_CONFIG_PASSWORD = "hyperhdr"; +} + class AuthTable : public DBManager { public: - /// construct wrapper with auth table - AuthTable(const QString& rootPath = "", QObject* parent = nullptr, bool readonlyMode = false); - - /// - /// @brief Create a user record, if called on a existing user the auth is recreated - /// @param[in] user The username - /// @param[in] pw The password - /// @return true on success else false - /// + AuthTable(bool readonlyMode); bool createUser(const QString& user, const QString& pw); - void savePipewire(const QString& wToken); - - /// - /// @brief Test if user record exists - /// @param[in] user The user id - /// @return true on success else false - /// bool userExist(const QString& user); - - /// - /// @brief Test if a user is authorized for access with given pw. - /// @param user The user name - /// @param pw The password - /// @return True on success else false - /// bool isUserAuthorized(const QString& user, const QString& pw); - const QString loadPipewire(); - - /// - /// @brief Test if a user token is authorized for access. - /// @param usr The user name - /// @param token The token - /// @return True on success else false - /// bool isUserTokenAuthorized(const QString& usr, const QString& token); - - /// - /// @brief Update token of a user. It's an alternate login path which is replaced on startup. This token is NOT hashed(!) - /// @param user The user name - /// @return True on success else false - /// bool setUserToken(const QString& user); - - /// - /// @brief Get token of a user. This token is NOT hashed(!) - /// @param user The user name - /// @return The token - /// const QByteArray getUserToken(const QString& user); - - /// - /// @brief update password of given user. The user should be tested (isUserAuthorized) to verify this change - /// @param user The user name - /// @param newPw The new password to set - /// @return True on success else false - /// bool updateUserPassword(const QString& user, const QString& newPw); - - /// - /// @brief Reset password of HyperHDR user !DANGER! Used in HyperHDR main.cpp - /// @return True on success else false - /// bool resetHyperhdrUser(); - - /// - /// @brief Update 'last_use' column entry for the corresponding user - /// @param[in] user The user to search for - /// void updateUserUsed(const QString& user); - - /// - /// @brief Test if token record exists, updates last_use on success - /// @param[in] token The token id - /// @return true on success else false - /// bool tokenExist(const QString& token); - - /// - /// @brief Create a new token record with comment - /// @param[in] token The token id as plaintext - /// @param[in] comment The comment for the token (eg a human readable identifier) - /// @param[in] id The id for the token - /// @return true on success else false - /// bool createToken(const QString& token, const QString& comment, const QString& id); - - /// - /// @brief Delete token record by id - /// @param[in] id The token id - /// @return true on success else false - /// bool deleteToken(const QString& id); - - /// - /// @brief Rename token record by id - /// @param[in] id The token id - /// @param[in] comment The new comment - /// @return true on success else false - /// bool renameToken(const QString& id, const QString& comment); - - /// - /// @brief Get all 'comment', 'last_use' and 'id' column entries - /// @return A vector of all lists - /// const QVector getTokenList(); - - /// - /// @brief Test if id exists - /// @param[in] id The id - /// @return true on success else false - /// bool idExist(const QString& id); - - /// - /// @brief Get the passwort hash of a user from db - /// @param user The user name - /// @return password as hash - /// const QByteArray getPasswordHashOfUser(const QString& user); - - /// - /// @brief Calc the password hash of a user based on user name and password - /// @param user The user name - /// @param pw The password - /// @return The calced password hash - /// const QByteArray calcPasswordHashOfUser(const QString& user, const QString& pw); - - /// - /// @brief Create a password hash of plaintex password + salt - /// @param pw The plaintext password - /// @param salt The salt - /// @return The password hash with salt - /// const QByteArray hashPasswordWithSalt(const QString& pw, const QByteArray& salt); - - /// - /// @brief Create a token hash - /// @param token The plaintext token - /// @return The token hash - /// const QByteArray hashToken(const QString& token); }; diff --git a/include/db/DBManager.h b/include/db/DBManager.h index 1f9be0b2c..80d39b278 100644 --- a/include/db/DBManager.h +++ b/include/db/DBManager.h @@ -1,157 +1,64 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include +#endif + #include +#include + class QSqlDatabase; class QSqlQuery; typedef QPair CPair; typedef QVector VectorPair; -/// -/// @brief Database interface for SQLite3. -/// Inherit this class to create component specific methods based on this interface -/// Usage: setTable(name) once before you use read/write actions -/// To use another database use setDb(newDB) (defaults to "hyperhdr") -/// -/// Incompatible functions with SQlite3: -/// QSqlQuery::size() returns always -1 -/// -class DBManager : public QObject +#define CURRENT_HYPERHDR_DB_EXPORT_VERSION "HyperHDR_export_format_v20" + +class DBManager { - Q_OBJECT -public: - DBManager(QObject* parent = nullptr); - ~DBManager() override; +public: + virtual ~DBManager(); - /// set root path - void setRootPath(const QString& rootPath); - /// define the database to work with - void setDatabaseName(const QString& dbn); - /// set a table to work with + static void initializeDatabaseFilename(QFileInfo databaseName); void setTable(const QString& table); - /// get current database object set with setDB() QSqlDatabase getDB() const; - /// - /// @brief Create a table (if required) with the given columns. Older tables will be updated accordingly with missing columns - /// Does not delete or migrate old columns - /// @param[in] columns The columns of the table. Requires at least one entry! - /// @return True on success else false - /// bool createTable(QStringList& columns) const; - - /// - /// @brief Create a column if the column already exists returns false and logs error - /// @param[in] column The column of the table - /// @return True on success else false - /// bool createColumn(const QString& column) const; - - /// - /// @brief Check if at least one record exists in table with the conditions - /// @param[in] conditions The search conditions (WHERE) - /// @return True on success else false - /// bool recordExists(const VectorPair& conditions) const; - - /// - /// @brief Create a new record in table when the conditions find no existing entry. Add additional key:value pairs in columns - /// DO NOT repeat column keys between 'conditions' and 'columns' as they will be merged on creation - /// @param[in] conditions conditions to search for, as a record may exist and should be updated instead (WHERE) - /// @param[in] columns columns to create or update (optional) - /// @return True on success else false - /// bool createRecord(const VectorPair& conditions, const QVariantMap& columns = QVariantMap()) const; - - /// - /// @brief Update a record with conditions and additional key:value pairs in columns - /// @param[in] conditions conditions which rows should be updated (WHERE) - /// @param[in] columns columns to update - /// @return True on success else false - /// bool updateRecord(const VectorPair& conditions, const QVariantMap& columns) const; - - /// - /// @brief Get data of a single record, multiple records are not supported - /// @param[in] conditions condition to search for (WHERE) - /// @param[out] results results of query - /// @param[in] tColumns target columns to search in (optional) if not provided returns all columns - /// @param[in] tOrder target order columns with order by ASC/DESC (optional) - /// @return True on success else false - /// bool getRecord(const VectorPair& conditions, QVariantMap& results, const QStringList& tColumns = QStringList(), const QStringList& tOrder = QStringList()) const; - - /// - /// @brief Get data of multiple records, you need to specify the columns. This search is without conditions. Good to grab all data from db - /// @param[in] conditions condition to search for (WHERE) - /// @param[out] results results of query - /// @param[in] tColumns target columns to search in (optional) if not provided returns all columns - /// @param[in] tOrder target order columns with order by ASC/DESC (optional) - /// @return True on success else false - /// bool getRecords(QVector& results, const QStringList& tColumns = QStringList(), const QStringList& tOrder = QStringList()) const; - - /// - /// @brief Delete a record determined by conditions - /// @param[in] conditions conditions of the row to delete it (WHERE) - /// @return True on success; on error or not found false - /// bool deleteRecord(const VectorPair& conditions) const; - - /// - /// @brief Check if table exists in current database - /// @param[in] table The name of the table - /// @return True on success else false - /// bool tableExists(const QString& table) const; - - /// - /// @brief Delete a table, fails silent (return true) if table is not found - /// @param[in] table The name of the table - /// @return True on success else false - /// bool deleteTable(const QString& table) const; - - /// - /// @brief Sets a table in read-only mode. - /// Updates will not written to the table - /// @param[in] readOnly True read-only, false - read/write - /// void setReadonlyMode(bool readOnly); - bool migrateColumn(QString newColumn, QString oldColumn); - -public slots: const QJsonObject getBackup(); QString restoreBackup(const QJsonObject& backupData); + QString createLocalBackup(); protected: + DBManager(); Logger* _log; -private: - /// databse connection & file name, defaults to hyperhdr - QString _dbn = "hyperhdr"; - /// table in database +private: QString _table; bool _readonlyMode; - - /// addBindValue to query given by QVariantList void doAddBindValue(QSqlQuery& query, const QVariantList& variants) const; - static QString _rootPath; + static QFileInfo _databaseName; static QThreadStorage _databasePool; }; diff --git a/include/db/InstanceTable.h b/include/db/InstanceTable.h index 2324ef937..112a026fe 100644 --- a/include/db/InstanceTable.h +++ b/include/db/InstanceTable.h @@ -1,86 +1,24 @@ #pragma once -// db #include #include -/// -/// @brief Hyperhdr instance manager specific database interface. prepares also the HyperHDR database for all follow up usage (Init QtSqlConnection) along with db name -/// class InstanceTable : public DBManager { public: - InstanceTable(const QString& rootPath, QObject* parent = nullptr, bool readonlyMode = false); + InstanceTable(bool readonlyMode); - /// - /// @brief Create a new Hyperhdr instance entry, the name needs to be unique - /// @param name The name of the instance - /// @param[out] inst The id that has been assigned - /// @return True on success else false - /// bool createInstance(const QString& name, quint8& inst); - - /// - /// @brief Delete a Hyperhdr instance - /// @param inst The id that has been assigned - /// @return True on success else false - /// bool deleteInstance(quint8 inst); - - /// - /// @brief Assign a new name for the given instance - /// @param inst The instance index - /// @param name The new name of the instance - /// @return True on success else false (instance not found) - /// bool saveName(quint8 inst, const QString& name); - - - /// - /// @brief Get all instances with all columns - /// @param justEnabled return just enabled instances if true - /// @return The found instances - /// QVector getAllInstances(bool justEnabled = false); - - /// - /// @brief Test if instance record exists - /// @param[in] user The user id - /// @return true on success else false - /// bool instanceExist(quint8 inst); - - /// - /// @brief Get instance name by instance index - /// @param index The index to search for - /// @return The name of this index, may return NOT FOUND if not found - /// const QString getNamebyIndex(quint8 index); - - /// - /// @brief Update 'last_use' timestamp - /// @param inst The instance to update - /// void setLastUse(quint8 inst); - - /// - /// @brief Update 'enabled' column by instance index - /// @param inst The instance to update - /// @param newState True when enabled else false - /// void setEnable(quint8 inst, bool newState); - - /// - /// @brief Get state of 'enabled' column by instance index - /// @param inst The instance to get - /// @return True when enabled else false - /// bool isEnabled(quint8 inst); private: - /// - /// @brief Create first HyperHDR instance entry, if index 0 is not found. - /// void createInstance(); }; diff --git a/include/db/MetaTable.h b/include/db/MetaTable.h index 8933fc8f1..9dc98e6b3 100644 --- a/include/db/MetaTable.h +++ b/include/db/MetaTable.h @@ -2,19 +2,11 @@ #include -/// -/// @brief meta table specific database interface -/// class MetaTable : public DBManager { public: - /// construct wrapper with plugins table and columns - MetaTable(QObject* parent = nullptr, bool readonlyMode = false); + MetaTable(bool readonlyMode = false); - /// - /// @brief Get the uuid, if the uuid is not set it will be created - /// @return The uuid - /// QString getUUID() const; }; diff --git a/include/db/SettingsTable.h b/include/db/SettingsTable.h index 96ed456fc..91ee029c5 100644 --- a/include/db/SettingsTable.h +++ b/include/db/SettingsTable.h @@ -2,56 +2,20 @@ #include -/// -/// @brief settings table db interface -/// class SettingsTable : public DBManager { public: - /// construct wrapper with settings table - SettingsTable(quint8 instance, QObject* parent = nullptr); - - /// - /// @brief Create or update a settings record - /// @param[in] type type of setting - /// @param[in] config The configuration data - /// @return true on success else false - /// + SettingsTable(quint8 instance); bool createSettingsRecord(const QString& type, const QString& config) const; - - /// - /// @brief Test if record exist, type can be global setting or local (instance) - /// @param[in] type type of setting - /// @param[in] hyperhdr_inst The instance of hyperhdr assigned (might be empty) - /// @return true on success else false - /// bool recordExist(const QString& type) const; - - /// - /// @brief Get 'config' column of settings entry as QJsonDocument - /// @param[in] type The settings type - /// @return The QJsonDocument - /// QJsonDocument getSettingsRecord(const QString& type) const; - - /// - /// @brief Get 'config' column of settings entry as QString - /// @param[in] type The settings type - /// @return The QString - /// QString getSettingsRecordString(const QString& type) const; - bool deleteSettingsRecordString(const QString& type) const; - bool purge(const QString& type) const; - /// - /// @brief Delete all settings entries associated with this instance, called from InstanceTable of HyperHDRIManager - /// void deleteInstance() const; - bool isSettingGlobal(const QString& type) const; private: - const quint8 _hyperhdr_inst; + const quint8 _instance; }; diff --git a/include/effectengine/ActiveEffectDefinition.h b/include/effectengine/ActiveEffectDefinition.h index 14610ad11..9304bf3c7 100644 --- a/include/effectengine/ActiveEffectDefinition.h +++ b/include/effectengine/ActiveEffectDefinition.h @@ -9,5 +9,4 @@ struct ActiveEffectDefinition QString name; int priority; int timeout; - QJsonObject args; }; diff --git a/include/effectengine/Animation4Music_PulseBlue.h b/include/effectengine/Animation4Music_PulseBlue.h index 9cd92a9f7..6f2357b4d 100644 --- a/include/effectengine/Animation4Music_PulseBlue.h +++ b/include/effectengine/Animation4Music_PulseBlue.h @@ -6,8 +6,6 @@ class Animation4Music_PulseBlue : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseBlue(); diff --git a/include/effectengine/Animation4Music_PulseGreen.h b/include/effectengine/Animation4Music_PulseGreen.h index 0c28f4b70..18b8c1c60 100644 --- a/include/effectengine/Animation4Music_PulseGreen.h +++ b/include/effectengine/Animation4Music_PulseGreen.h @@ -6,8 +6,6 @@ class Animation4Music_PulseGreen : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseGreen(); diff --git a/include/effectengine/Animation4Music_PulseMulti.h b/include/effectengine/Animation4Music_PulseMulti.h index b5ec2a999..501de8c81 100644 --- a/include/effectengine/Animation4Music_PulseMulti.h +++ b/include/effectengine/Animation4Music_PulseMulti.h @@ -6,8 +6,6 @@ class Animation4Music_PulseMulti : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseMulti(); diff --git a/include/effectengine/Animation4Music_PulseMultiFast.h b/include/effectengine/Animation4Music_PulseMultiFast.h index 53c0f88eb..74461766d 100644 --- a/include/effectengine/Animation4Music_PulseMultiFast.h +++ b/include/effectengine/Animation4Music_PulseMultiFast.h @@ -6,8 +6,6 @@ class Animation4Music_PulseMultiFast : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseMultiFast(); diff --git a/include/effectengine/Animation4Music_PulseMultiSlow.h b/include/effectengine/Animation4Music_PulseMultiSlow.h index 2a825f5fb..c57053d8d 100644 --- a/include/effectengine/Animation4Music_PulseMultiSlow.h +++ b/include/effectengine/Animation4Music_PulseMultiSlow.h @@ -6,8 +6,6 @@ class Animation4Music_PulseMultiSlow : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseMultiSlow(); diff --git a/include/effectengine/Animation4Music_PulseRed.h b/include/effectengine/Animation4Music_PulseRed.h index 0a16e04d3..48b1d8e8f 100644 --- a/include/effectengine/Animation4Music_PulseRed.h +++ b/include/effectengine/Animation4Music_PulseRed.h @@ -6,8 +6,6 @@ class Animation4Music_PulseRed : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseRed(); diff --git a/include/effectengine/Animation4Music_PulseWhite.h b/include/effectengine/Animation4Music_PulseWhite.h index 4eabd5ec5..e2526dff2 100644 --- a/include/effectengine/Animation4Music_PulseWhite.h +++ b/include/effectengine/Animation4Music_PulseWhite.h @@ -6,8 +6,6 @@ class Animation4Music_PulseWhite : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseWhite(); diff --git a/include/effectengine/Animation4Music_PulseYellow.h b/include/effectengine/Animation4Music_PulseYellow.h index cddf52ddc..c48ceee99 100644 --- a/include/effectengine/Animation4Music_PulseYellow.h +++ b/include/effectengine/Animation4Music_PulseYellow.h @@ -6,8 +6,6 @@ class Animation4Music_PulseYellow : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_PulseYellow(); diff --git a/include/effectengine/Animation4Music_QuatroBlue.h b/include/effectengine/Animation4Music_QuatroBlue.h index cb0ce853a..c5569ac89 100644 --- a/include/effectengine/Animation4Music_QuatroBlue.h +++ b/include/effectengine/Animation4Music_QuatroBlue.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroBlue : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroBlue(); diff --git a/include/effectengine/Animation4Music_QuatroGreen.h b/include/effectengine/Animation4Music_QuatroGreen.h index 99849128c..d1ba34c27 100644 --- a/include/effectengine/Animation4Music_QuatroGreen.h +++ b/include/effectengine/Animation4Music_QuatroGreen.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroGreen : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroGreen(); diff --git a/include/effectengine/Animation4Music_QuatroMulti.h b/include/effectengine/Animation4Music_QuatroMulti.h index 35bf2162c..9af49741f 100644 --- a/include/effectengine/Animation4Music_QuatroMulti.h +++ b/include/effectengine/Animation4Music_QuatroMulti.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroMulti : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroMulti(); diff --git a/include/effectengine/Animation4Music_QuatroMultiFast.h b/include/effectengine/Animation4Music_QuatroMultiFast.h index e282ee915..e8d6cf52a 100644 --- a/include/effectengine/Animation4Music_QuatroMultiFast.h +++ b/include/effectengine/Animation4Music_QuatroMultiFast.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroMultiFast : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroMultiFast(); diff --git a/include/effectengine/Animation4Music_QuatroMultiSlow.h b/include/effectengine/Animation4Music_QuatroMultiSlow.h index e3590b0d4..b023b2c94 100644 --- a/include/effectengine/Animation4Music_QuatroMultiSlow.h +++ b/include/effectengine/Animation4Music_QuatroMultiSlow.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroMultiSlow : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroMultiSlow(); diff --git a/include/effectengine/Animation4Music_QuatroRed.h b/include/effectengine/Animation4Music_QuatroRed.h index bcc0f7a23..116eb4138 100644 --- a/include/effectengine/Animation4Music_QuatroRed.h +++ b/include/effectengine/Animation4Music_QuatroRed.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroRed : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroRed(); diff --git a/include/effectengine/Animation4Music_QuatroWhite.h b/include/effectengine/Animation4Music_QuatroWhite.h index e805e9401..649a43f32 100644 --- a/include/effectengine/Animation4Music_QuatroWhite.h +++ b/include/effectengine/Animation4Music_QuatroWhite.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroWhite : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroWhite(); diff --git a/include/effectengine/Animation4Music_QuatroYellow.h b/include/effectengine/Animation4Music_QuatroYellow.h index 8bb5a6bb5..56fc2e448 100644 --- a/include/effectengine/Animation4Music_QuatroYellow.h +++ b/include/effectengine/Animation4Music_QuatroYellow.h @@ -6,8 +6,6 @@ class Animation4Music_QuatroYellow : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_QuatroYellow(); diff --git a/include/effectengine/Animation4Music_StereoBlue.h b/include/effectengine/Animation4Music_StereoBlue.h index f8e6d9ef9..abd8dcd12 100644 --- a/include/effectengine/Animation4Music_StereoBlue.h +++ b/include/effectengine/Animation4Music_StereoBlue.h @@ -6,8 +6,6 @@ class Animation4Music_StereoBlue : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoBlue(); diff --git a/include/effectengine/Animation4Music_StereoGreen.h b/include/effectengine/Animation4Music_StereoGreen.h index 9901f2db9..2a3403607 100644 --- a/include/effectengine/Animation4Music_StereoGreen.h +++ b/include/effectengine/Animation4Music_StereoGreen.h @@ -6,8 +6,6 @@ class Animation4Music_StereoGreen : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoGreen(); diff --git a/include/effectengine/Animation4Music_StereoMulti.h b/include/effectengine/Animation4Music_StereoMulti.h index b1ca269f5..9a3d1f747 100644 --- a/include/effectengine/Animation4Music_StereoMulti.h +++ b/include/effectengine/Animation4Music_StereoMulti.h @@ -6,8 +6,6 @@ class Animation4Music_StereoMulti : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoMulti(); diff --git a/include/effectengine/Animation4Music_StereoMultiFast.h b/include/effectengine/Animation4Music_StereoMultiFast.h index 152f509b1..f4526fd6a 100644 --- a/include/effectengine/Animation4Music_StereoMultiFast.h +++ b/include/effectengine/Animation4Music_StereoMultiFast.h @@ -6,8 +6,6 @@ class Animation4Music_StereoMultiFast : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoMultiFast(); diff --git a/include/effectengine/Animation4Music_StereoMultiSlow.h b/include/effectengine/Animation4Music_StereoMultiSlow.h index 22e56d421..8d61ac541 100644 --- a/include/effectengine/Animation4Music_StereoMultiSlow.h +++ b/include/effectengine/Animation4Music_StereoMultiSlow.h @@ -6,8 +6,6 @@ class Animation4Music_StereoMultiSlow : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoMultiSlow(); diff --git a/include/effectengine/Animation4Music_StereoRed.h b/include/effectengine/Animation4Music_StereoRed.h index f789bb39d..b5fbb7c50 100644 --- a/include/effectengine/Animation4Music_StereoRed.h +++ b/include/effectengine/Animation4Music_StereoRed.h @@ -6,8 +6,6 @@ class Animation4Music_StereoRed : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoRed(); diff --git a/include/effectengine/Animation4Music_StereoWhite.h b/include/effectengine/Animation4Music_StereoWhite.h index a56b62e5e..60d2f444d 100644 --- a/include/effectengine/Animation4Music_StereoWhite.h +++ b/include/effectengine/Animation4Music_StereoWhite.h @@ -6,8 +6,6 @@ class Animation4Music_StereoWhite : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoWhite(); diff --git a/include/effectengine/Animation4Music_StereoYellow.h b/include/effectengine/Animation4Music_StereoYellow.h index 192abeb26..95349a02d 100644 --- a/include/effectengine/Animation4Music_StereoYellow.h +++ b/include/effectengine/Animation4Music_StereoYellow.h @@ -6,8 +6,6 @@ class Animation4Music_StereoYellow : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_StereoYellow(); diff --git a/include/effectengine/Animation4Music_TestEq.h b/include/effectengine/Animation4Music_TestEq.h index b030a4513..fe6077cad 100644 --- a/include/effectengine/Animation4Music_TestEq.h +++ b/include/effectengine/Animation4Music_TestEq.h @@ -6,8 +6,6 @@ class Animation4Music_TestEq : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_TestEq(); @@ -24,4 +22,5 @@ class Animation4Music_TestEq : public AnimationBaseMusic bool getImage(Image& image) override; private: uint32_t _internalIndex; + int _oldMulti; }; diff --git a/include/effectengine/Animation4Music_WavesPulse.h b/include/effectengine/Animation4Music_WavesPulse.h index 37b5948b2..c08dc934d 100644 --- a/include/effectengine/Animation4Music_WavesPulse.h +++ b/include/effectengine/Animation4Music_WavesPulse.h @@ -6,8 +6,6 @@ class Animation4Music_WavesPulse : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_WavesPulse(); diff --git a/include/effectengine/Animation4Music_WavesPulseFast.h b/include/effectengine/Animation4Music_WavesPulseFast.h index e7d82d941..414fb36d5 100644 --- a/include/effectengine/Animation4Music_WavesPulseFast.h +++ b/include/effectengine/Animation4Music_WavesPulseFast.h @@ -6,8 +6,6 @@ class Animation4Music_WavesPulseFast : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_WavesPulseFast(); diff --git a/include/effectengine/Animation4Music_WavesPulseSlow.h b/include/effectengine/Animation4Music_WavesPulseSlow.h index 3c6468909..877179374 100644 --- a/include/effectengine/Animation4Music_WavesPulseSlow.h +++ b/include/effectengine/Animation4Music_WavesPulseSlow.h @@ -6,8 +6,6 @@ class Animation4Music_WavesPulseSlow : public AnimationBaseMusic { - Q_OBJECT - public: Animation4Music_WavesPulseSlow(); diff --git a/include/effectengine/AnimationBase.h b/include/effectengine/AnimationBase.h index 0e6e4325e..c59b60f4e 100644 --- a/include/effectengine/AnimationBase.h +++ b/include/effectengine/AnimationBase.h @@ -1,18 +1,27 @@ #pragma once -#include +#ifndef PCH_ENABLED + #include + #include +#endif + #include + #include #include #include - -class AnimationBase : public QObject +template +AnimationBase* EffectFactory() { - Q_OBJECT + return new T(); +} +class AnimationBase +{ public: AnimationBase(QString name); + virtual ~AnimationBase() = default; QString GetName(); virtual bool Play(QPainter* painter) = 0; virtual void Init(QImage& hyperImage, int hyperLatchTime) = 0; @@ -27,7 +36,6 @@ class AnimationBase : public QObject private: QString _name; int _sleepTime; - bool _isDevice; bool _stopMe; protected: void setStopMe(bool stopMe); diff --git a/include/effectengine/AnimationBaseMusic.h b/include/effectengine/AnimationBaseMusic.h index b5b87b3fa..9ea2da424 100644 --- a/include/effectengine/AnimationBaseMusic.h +++ b/include/effectengine/AnimationBaseMusic.h @@ -1,44 +1,26 @@ #pragma once #include +#include -struct MovingTarget -{ - QColor _averageColor; - QColor _fastColor; - QColor _slowColor; - int32_t _targetAverageR; - int32_t _targetAverageG; - int32_t _targetAverageB; - int32_t _targetAverageCounter; - int32_t _targetSlowR; - int32_t _targetSlowG; - int32_t _targetSlowB; - int32_t _targetSlowCounter; - int32_t _targetFastR; - int32_t _targetFastG; - int32_t _targetFastB; - int32_t _targetFastCounter; - - void Clear(); - void CopyFrom(MovingTarget* source); -}; +class SoundCapture; class AnimationBaseMusic : public AnimationBase { - Q_OBJECT - public: AnimationBaseMusic(QString name); - static QJsonObject GetArgs(); + ~AnimationBaseMusic(); bool isSoundEffect() override; void store(MovingTarget* source); void restore(MovingTarget* target); +protected: + std::shared_ptr _soundCapture; + private: MovingTarget _myTarget; - + uint32_t _soundHandle; }; diff --git a/include/effectengine/Animation_AtomicSwirl.h b/include/effectengine/Animation_AtomicSwirl.h index 45fe218b8..25c5928e0 100644 --- a/include/effectengine/Animation_AtomicSwirl.h +++ b/include/effectengine/Animation_AtomicSwirl.h @@ -6,12 +6,6 @@ class Animation_AtomicSwirl : public Animation_Swirl { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_AtomicSwirl(QString name = ANIM_ATOMIC_SWIRL); diff --git a/include/effectengine/Animation_BlueMoodBlobs.h b/include/effectengine/Animation_BlueMoodBlobs.h index 5f42767cd..03b7384bf 100644 --- a/include/effectengine/Animation_BlueMoodBlobs.h +++ b/include/effectengine/Animation_BlueMoodBlobs.h @@ -6,12 +6,6 @@ class Animation_BlueMoodBlobs : public Animation_MoodBlobs { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_BlueMoodBlobs(QString name = ANIM_BLUE_MOOD_BLOBS); diff --git a/include/effectengine/Animation_Breath.h b/include/effectengine/Animation_Breath.h index 41bb0dbd6..75d609ca7 100644 --- a/include/effectengine/Animation_Breath.h +++ b/include/effectengine/Animation_Breath.h @@ -6,12 +6,6 @@ class Animation_Breath : public Animation_Fade { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_Breath(QString name = ANIM_BREATH); diff --git a/include/effectengine/Animation_Candle.h b/include/effectengine/Animation_Candle.h index d5e9abb7a..0ca420dfc 100644 --- a/include/effectengine/Animation_Candle.h +++ b/include/effectengine/Animation_Candle.h @@ -6,12 +6,6 @@ class Animation_Candle : public Animation_CandleLight { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_Candle(QString name = ANIM_CANDLE); diff --git a/include/effectengine/Animation_CandleLight.h b/include/effectengine/Animation_CandleLight.h index 89db19afd..74e55f141 100644 --- a/include/effectengine/Animation_CandleLight.h +++ b/include/effectengine/Animation_CandleLight.h @@ -4,8 +4,6 @@ class Animation_CandleLight : public AnimationBase { - Q_OBJECT - public: Animation_CandleLight(QString name); diff --git a/include/effectengine/Animation_CinemaBrightenLights.h b/include/effectengine/Animation_CinemaBrightenLights.h index 9c82c23b8..3fd6fa6f7 100644 --- a/include/effectengine/Animation_CinemaBrightenLights.h +++ b/include/effectengine/Animation_CinemaBrightenLights.h @@ -6,12 +6,6 @@ class Animation_CinemaBrightenLights : public Animation_Fade { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_CinemaBrightenLights(QString name = ANIM_CINEMA_BRIGHTEN_LIGHTS); diff --git a/include/effectengine/Animation_CinemaDimLights.h b/include/effectengine/Animation_CinemaDimLights.h index 9fb7a5769..e48652c0f 100644 --- a/include/effectengine/Animation_CinemaDimLights.h +++ b/include/effectengine/Animation_CinemaDimLights.h @@ -6,12 +6,6 @@ class Animation_CinemaDimLights : public Animation_Fade { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_CinemaDimLights(QString name = ANIM_CINEMA_DIM_LIGHTS); diff --git a/include/effectengine/Animation_ColdMoodBlobs.h b/include/effectengine/Animation_ColdMoodBlobs.h index a44226c98..692d38436 100644 --- a/include/effectengine/Animation_ColdMoodBlobs.h +++ b/include/effectengine/Animation_ColdMoodBlobs.h @@ -6,12 +6,6 @@ class Animation_ColdMoodBlobs : public Animation_MoodBlobs { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_ColdMoodBlobs(QString name = ANIM_COLD_MOOD_BLOBS); diff --git a/include/effectengine/Animation_DoubleSwirl.h b/include/effectengine/Animation_DoubleSwirl.h index 678589db6..72dda0fb9 100644 --- a/include/effectengine/Animation_DoubleSwirl.h +++ b/include/effectengine/Animation_DoubleSwirl.h @@ -6,12 +6,6 @@ class Animation_DoubleSwirl : public Animation_Swirl { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_DoubleSwirl(QString name = ANIM_DOUBLE_SWIRL); diff --git a/include/effectengine/Animation_Fade.h b/include/effectengine/Animation_Fade.h index 55969a28f..be73e113a 100644 --- a/include/effectengine/Animation_Fade.h +++ b/include/effectengine/Animation_Fade.h @@ -3,8 +3,6 @@ #include class Animation_Fade : public AnimationBase { - Q_OBJECT - public: Animation_Fade(QString name); diff --git a/include/effectengine/Animation_FullColorMoodBlobs.h b/include/effectengine/Animation_FullColorMoodBlobs.h index 9321923d9..96740e1f9 100644 --- a/include/effectengine/Animation_FullColorMoodBlobs.h +++ b/include/effectengine/Animation_FullColorMoodBlobs.h @@ -6,12 +6,6 @@ class Animation_FullColorMoodBlobs : public Animation_MoodBlobs { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_FullColorMoodBlobs(QString name = ANIM_FULLCOLOR_MOOD_BLOBS); diff --git a/include/effectengine/Animation_GreenMoodBlobs.h b/include/effectengine/Animation_GreenMoodBlobs.h index a5419c8d7..b1d6321bc 100644 --- a/include/effectengine/Animation_GreenMoodBlobs.h +++ b/include/effectengine/Animation_GreenMoodBlobs.h @@ -6,12 +6,6 @@ class Animation_GreenMoodBlobs : public Animation_MoodBlobs { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_GreenMoodBlobs(QString name = ANIM_GREEN_MOOD_BLOBS); diff --git a/include/effectengine/Animation_KnightRider.h b/include/effectengine/Animation_KnightRider.h index ae87b39fd..81a9eddd0 100644 --- a/include/effectengine/Animation_KnightRider.h +++ b/include/effectengine/Animation_KnightRider.h @@ -8,12 +8,6 @@ class Animation_KnightRider : public AnimationBase { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_KnightRider(); diff --git a/include/effectengine/Animation_MoodBlobs.h b/include/effectengine/Animation_MoodBlobs.h index 17f5b4b82..c93c0e68b 100644 --- a/include/effectengine/Animation_MoodBlobs.h +++ b/include/effectengine/Animation_MoodBlobs.h @@ -3,8 +3,6 @@ #include class Animation_MoodBlobs : public AnimationBase { - Q_OBJECT - public: Animation_MoodBlobs(QString name); diff --git a/include/effectengine/Animation_NotifyBlue.h b/include/effectengine/Animation_NotifyBlue.h index c83ef0590..ddf3763e1 100644 --- a/include/effectengine/Animation_NotifyBlue.h +++ b/include/effectengine/Animation_NotifyBlue.h @@ -6,12 +6,6 @@ class Animation_NotifyBlue : public Animation_Fade { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_NotifyBlue(QString name = ANIM_NOTIFY_BLUE); diff --git a/include/effectengine/Animation_Plasma.h b/include/effectengine/Animation_Plasma.h index 950355d60..071df919d 100644 --- a/include/effectengine/Animation_Plasma.h +++ b/include/effectengine/Animation_Plasma.h @@ -8,12 +8,6 @@ #define PAL_LEN 360 class Animation_Plasma : public AnimationBase { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_Plasma(); diff --git a/include/effectengine/Animation_Police.h b/include/effectengine/Animation_Police.h index 0283cfd39..581d7660c 100644 --- a/include/effectengine/Animation_Police.h +++ b/include/effectengine/Animation_Police.h @@ -4,8 +4,6 @@ class Animation_Police : public AnimationBase { - Q_OBJECT - public: Animation_Police(QString name); diff --git a/include/effectengine/Animation_PoliceLightsSingle.h b/include/effectengine/Animation_PoliceLightsSingle.h index 81e050e4e..da1a17357 100644 --- a/include/effectengine/Animation_PoliceLightsSingle.h +++ b/include/effectengine/Animation_PoliceLightsSingle.h @@ -6,12 +6,6 @@ class Animation_PoliceLightsSingle : public Animation_Police { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_PoliceLightsSingle(QString name = ANIM_POLICELIGHTSSINGLE); diff --git a/include/effectengine/Animation_PoliceLightsSolid.h b/include/effectengine/Animation_PoliceLightsSolid.h index da6e29aaf..4ae889446 100644 --- a/include/effectengine/Animation_PoliceLightsSolid.h +++ b/include/effectengine/Animation_PoliceLightsSolid.h @@ -6,12 +6,6 @@ class Animation_PoliceLightsSolid : public Animation_Police { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_PoliceLightsSolid(QString name = ANIM_POLICELIGHTSSOLID); diff --git a/include/effectengine/Animation_RainbowSwirl.h b/include/effectengine/Animation_RainbowSwirl.h index ef6f3300e..3ccb19716 100644 --- a/include/effectengine/Animation_RainbowSwirl.h +++ b/include/effectengine/Animation_RainbowSwirl.h @@ -6,12 +6,6 @@ class Animation_RainbowSwirl : public Animation_Swirl { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_RainbowSwirl(QString name = ANIM_RAINBOW_SWIRL); diff --git a/include/effectengine/Animation_RainbowWaves.h b/include/effectengine/Animation_RainbowWaves.h index 2216eb16c..b260df47c 100644 --- a/include/effectengine/Animation_RainbowWaves.h +++ b/include/effectengine/Animation_RainbowWaves.h @@ -6,8 +6,6 @@ class Animation_RainbowWaves : public AnimationBase { - Q_OBJECT - public: Animation_RainbowWaves(QString name = ANIM_RAINBOW_WAVES); @@ -24,9 +22,4 @@ class Animation_RainbowWaves : public AnimationBase protected: int hue; - - -private: - - static QJsonObject GetArgs(); }; diff --git a/include/effectengine/Animation_RedMoodBlobs.h b/include/effectengine/Animation_RedMoodBlobs.h index 0d9e908f6..5948ea4b0 100644 --- a/include/effectengine/Animation_RedMoodBlobs.h +++ b/include/effectengine/Animation_RedMoodBlobs.h @@ -6,12 +6,6 @@ class Animation_RedMoodBlobs : public Animation_MoodBlobs { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_RedMoodBlobs(QString name = ANIM_RED_MOOD_BLOBS); diff --git a/include/effectengine/Animation_SeaWaves.h b/include/effectengine/Animation_SeaWaves.h index 53b7948e9..2019f9678 100644 --- a/include/effectengine/Animation_SeaWaves.h +++ b/include/effectengine/Animation_SeaWaves.h @@ -6,12 +6,6 @@ class Animation_SeaWaves : public Animation_Waves { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_SeaWaves(QString name = ANIM_SEAWAVES); diff --git a/include/effectengine/Animation_Sparks.h b/include/effectengine/Animation_Sparks.h index bfab81bef..94ab51011 100644 --- a/include/effectengine/Animation_Sparks.h +++ b/include/effectengine/Animation_Sparks.h @@ -6,12 +6,6 @@ class Animation_Sparks : public AnimationBase { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_Sparks(); diff --git a/include/effectengine/Animation_StrobeRed.h b/include/effectengine/Animation_StrobeRed.h index 5a84d7943..c04faba18 100644 --- a/include/effectengine/Animation_StrobeRed.h +++ b/include/effectengine/Animation_StrobeRed.h @@ -6,12 +6,6 @@ class Animation_StrobeRed : public Animation_Fade { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_StrobeRed(QString name = ANIM_STROBE_RED); diff --git a/include/effectengine/Animation_StrobeWhite.h b/include/effectengine/Animation_StrobeWhite.h index c6071105e..54ca0059b 100644 --- a/include/effectengine/Animation_StrobeWhite.h +++ b/include/effectengine/Animation_StrobeWhite.h @@ -6,12 +6,6 @@ class Animation_StrobeWhite : public Animation_Fade { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_StrobeWhite(QString name = ANIM_STROBE_WHITE); diff --git a/include/effectengine/Animation_Swirl.h b/include/effectengine/Animation_Swirl.h index b4e7d92e6..41ab2fbd2 100644 --- a/include/effectengine/Animation_Swirl.h +++ b/include/effectengine/Animation_Swirl.h @@ -4,8 +4,6 @@ class Animation_Swirl : public AnimationBase { - Q_OBJECT - public: struct SwirlGradient diff --git a/include/effectengine/Animation_SwirlFast.h b/include/effectengine/Animation_SwirlFast.h index 3aefd9a83..df095849d 100644 --- a/include/effectengine/Animation_SwirlFast.h +++ b/include/effectengine/Animation_SwirlFast.h @@ -6,12 +6,6 @@ class Animation_SwirlFast : public Animation_Swirl { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_SwirlFast(QString name = ANIM_SWIRL_FAST); diff --git a/include/effectengine/Animation_SystemShutdown.h b/include/effectengine/Animation_SystemShutdown.h index e99de12fc..59f4381e6 100644 --- a/include/effectengine/Animation_SystemShutdown.h +++ b/include/effectengine/Animation_SystemShutdown.h @@ -8,12 +8,6 @@ class Animation_SystemShutdown : public AnimationBase { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_SystemShutdown(); diff --git a/include/effectengine/Animation_WarmMoodBlobs.h b/include/effectengine/Animation_WarmMoodBlobs.h index 8b4a4c9e2..15da2f5f6 100644 --- a/include/effectengine/Animation_WarmMoodBlobs.h +++ b/include/effectengine/Animation_WarmMoodBlobs.h @@ -6,12 +6,6 @@ class Animation_WarmMoodBlobs : public Animation_MoodBlobs { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_WarmMoodBlobs(QString name = ANIM_WARM_MOOD_BLOBS); diff --git a/include/effectengine/Animation_Waves.h b/include/effectengine/Animation_Waves.h index 79460471e..e92a96196 100644 --- a/include/effectengine/Animation_Waves.h +++ b/include/effectengine/Animation_Waves.h @@ -3,8 +3,6 @@ #include class Animation_Waves : public AnimationBase { - Q_OBJECT - public: Animation_Waves(QString name); diff --git a/include/effectengine/Animation_WavesWithColor.h b/include/effectengine/Animation_WavesWithColor.h index b32df20e4..b6e703e9d 100644 --- a/include/effectengine/Animation_WavesWithColor.h +++ b/include/effectengine/Animation_WavesWithColor.h @@ -6,12 +6,6 @@ class Animation_WavesWithColor : public Animation_Waves { - Q_OBJECT - -private: - - static QJsonObject GetArgs(); - public: Animation_WavesWithColor(QString name = ANIM_WAVESWITHCOLOR); diff --git a/include/effectengine/Effect.h b/include/effectengine/Effect.h index 0aaa93160..d2b45b38b 100644 --- a/include/effectengine/Effect.h +++ b/include/effectengine/Effect.h @@ -1,80 +1,82 @@ #pragma once -// Qt includes -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + + #include +#endif #include #include - -#include #include + class HyperHdrInstance; class Logger; -class Effect : public QThread +class Effect : public QObject { Q_OBJECT -public: - friend class EffectModule; +public slots: + void start(); + void run(); + void stop(); + void visiblePriorityChanged(quint8 priority); + +public: Effect(HyperHdrInstance* hyperhdr, int visiblePriority, int priority, int timeout, - const QString& name, - const QJsonObject& args = QJsonObject(), - const QString& imageData = "" + const EffectDefinition& effect ); ~Effect() override; - void run() override; + QString getDescription() const; int getPriority() const; void requestInterruption(); - bool isInterruptionRequested(); - void visiblePriorityChanged(quint8 priority); void setLedCount(int newCount); QString getName() const; int getTimeout() const; - QJsonObject getArgs() const; - + static std::list getAvailableEffects(); signals: - void setInput(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect); - void setInputImage(int priority, const Image& image, int timeout_ms, bool clearEffect); + void SignalSetLeds(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect); + void SignalSetImage(int priority, const Image& image, int timeout_ms, bool clearEffect); + void SignalEffectFinished(int priority, QString name, bool forced); private: - bool ImageShow(); - bool LedShow(); + void imageShow(int left); + void ledShow(int left); - HyperHdrInstance* _hyperhdr; - std::atomic _visiblePriority; + int _visiblePriority; const int _priority; const int _timeout; - + const int _instanceIndex; const QString _name; - const QJsonObject _args; - const QString _imageData; + AnimationBase* _effect; + int64_t _endTime; QVector _colors; Logger* _log; - std::atomic _interupt{}; + std::atomic _interrupt; - QSize _imageSize; QImage _image; - QPainter* _painter; - AnimationBase* _effect; - QVector _ledBuffer; - uint32_t _soundHandle; + QPainter _painter; + + QTimer _timer; + QVector _ledBuffer; std::atomic _ledCount; }; diff --git a/include/effectengine/EffectDBHandler.h b/include/effectengine/EffectDBHandler.h index aad52837f..13bcc1b33 100644 --- a/include/effectengine/EffectDBHandler.h +++ b/include/effectengine/EffectDBHandler.h @@ -1,36 +1,9 @@ #pragma once -// util -#include #include -#include -class EffectDBHandler : public QObject +class EffectDBHandler { - Q_OBJECT -private: - friend class HyperHdrDaemon; - EffectDBHandler(const QString& rootPath, const QJsonDocument& effectConfig, QObject* parent = nullptr); - static EffectDBHandler* efhInstance; - public: - static EffectDBHandler* getInstance(); - std::list getEffects() const; - -public slots: - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - -signals: - void effectListChanged(); - -private: - void updateEffects(); - -private: - QJsonObject _effectConfig; - Logger* _log; - const QString _rootPath; - - // available effects - std::list _availableEffects; + static std::list getEffects(); }; diff --git a/include/effectengine/EffectDefinition.h b/include/effectengine/EffectDefinition.h index 4c6e726f8..57b50e419 100644 --- a/include/effectengine/EffectDefinition.h +++ b/include/effectengine/EffectDefinition.h @@ -1,12 +1,35 @@ #pragma once -// QT include -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif + +class AnimationBase; struct EffectDefinition { - QString name; - QJsonObject args; - unsigned smoothCfg; + QString name; + bool smoothingDirectMode; + bool smoothingCustomSettings; + int smoothingTime; + int smoothingFrequency; + unsigned smoothingConfig; + AnimationBase* (*factory)(); + + EffectDefinition(AnimationBase* (*effectFactory)(), int _smoothingTime = 0, int _smoothingFrequency = 0) : + EffectDefinition(false, effectFactory, _smoothingTime, _smoothingFrequency) + { + } + + EffectDefinition(bool _directMode, AnimationBase* (*effectFactory)(), int _smoothingTime = 0, int _smoothingFrequency = 0) : + smoothingDirectMode(_directMode), + smoothingCustomSettings(_smoothingFrequency != 0 || _directMode), + smoothingTime(_smoothingTime), + smoothingFrequency(_smoothingFrequency), + smoothingConfig(0), + factory(effectFactory) + { + + } }; diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h index 005845cca..bcb2153d7 100644 --- a/include/effectengine/EffectEngine.h +++ b/include/effectengine/EffectEngine.h @@ -1,21 +1,19 @@ #pragma once -// Qt includes -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include +#endif #include - -// Effect engine includes #include #include #include -// pre-declaration class Effect; class EffectDBHandler; @@ -27,78 +25,27 @@ class EffectEngine : public QObject EffectEngine(HyperHdrInstance* hyperhdr); ~EffectEngine() override; - /// - /// @brief Get all init data of the running effects and stop them - /// - void cacheRunningEffects(); - - /// - /// @brief Start all cached effects, origin and smooth cfg is default - /// - void startCachedEffects(); - - static void handleInitialEffect(HyperHdrInstance* hyperhdr, const QJsonObject& FGEffectConfig); - public slots: - /// Run the specified effect on the given priority channel and optionally specify a timeout int runEffect(const QString& effectName, int priority, int timeout = -1, const QString& origin = "System"); - - /// Run the specified effect on the given priority channel and optionally specify a timeout - int runEffect(const QString& effectName - , const QJsonObject& args - , int priority - , int timeout = -1 - - , const QString& origin = "System" - , unsigned smoothCfg = 0 - , const QString& imageData = "" - ); - - /// Clear any effect running on the provided channel void channelCleared(int priority); - - /// Clear all effects void allChannelsCleared(); - std::list getEffects() const; - std::list getActiveEffects() const; - void visiblePriorityChanged(quint8 priority); - - void gotLedsHandler(int priority, const std::vector& ledColors, int timeout_ms = -1, bool clearEffect = true); + void handlerSetLeds(int priority, const std::vector& ledColors, int timeout_ms = -1, bool clearEffect = true); private slots: - void effectFinished(); + void handlerEffectFinished(int priority, QString name, bool forced); - /// - /// @brief is called whenever the EffectFileHandler emits updated effect list - /// - void handleUpdatedEffectList(); private: - /// Run the specified effect on the given priority channel and optionally specify a timeout - int runEffectScript( - const QString& name - , const QJsonObject& args - , int priority - , int timeout = -1 - , const QString& origin = "System" - , unsigned smoothCfg = 0 - , const QString& imageData = "" - ); + int runEffectScript(const QString& name, int priority, int timeout, const QString& origin); + void createSmoothingConfigs(); -private: HyperHdrInstance* _hyperInstance; - std::list _availableEffects; - - std::list _activeEffects; - - std::list _cachedActiveEffects; + std::list> _activeEffects; Logger* _log; - - // The global effect file handler EffectDBHandler* _effectDBHandler; }; diff --git a/include/flatbufserver/FlatBufferConnection.h b/include/flatbufserver/FlatBufferConnection.h index e9265b731..93180963c 100644 --- a/include/flatbufserver/FlatBufferConnection.h +++ b/include/flatbufserver/FlatBufferConnection.h @@ -22,112 +22,42 @@ namespace hyperhdrnet #define HYPERHDR_DOMAIN_SERVER QStringLiteral("hyperhdr-domain") -/// -/// Connection class to setup an connection to the hyperhdr server and execute commands. -/// class FlatBufferConnection : public QObject { Q_OBJECT public: - /// - /// @brief Constructor - /// @param address The address of the Hyperhdr server (for example "192.168.0.32:19444) - /// @param skipReply If true skip reply - /// - FlatBufferConnection(const QString& origin, const QString& address, int priority, bool skipReply); - - /// - /// @brief Destructor - /// + FlatBufferConnection(QObject* parent, const QString& origin, const QString& address, int priority, bool skipReply); ~FlatBufferConnection() override; - /// @brief Do not read reply messages from Hyperhdr if set to true void setSkipReply(bool skip); - - /// - /// @brief Register a new priority with given origin - /// @param origin The user friendly origin string - /// @param priority The priority to register - /// void setRegister(const QString& origin, int priority); - - /// - /// @brief Set all leds to the specified color - /// @param color The color - /// @param priority The priority - /// @param duration The duration in milliseconds - /// void setColor(const ColorRgb& color, int priority, int duration = 1); - - /// - /// @brief Clear the given priority channel - /// @param priority The priority - /// void clear(int priority); - - /// - /// @brief Clear all priority channels - /// void clearAll(); - - /// - /// @brief Send a command message and receive its reply - /// @param message The message to send - /// void sendMessage(const uint8_t* buffer, uint32_t size); public slots: - /// - /// @brief Set the leds according to the given image - /// @param image The image - /// - void setImage(const Image& image); + void sendImage(const Image& image); private slots: - /// - /// @brief Try to connect to the HyperHDR host - /// void connectToHost(); - - /// - /// @brief Slot called when new data has arrived - /// void readData(); signals: - - /// - /// @brief emits when a new videoMode was requested from flatbuf client - /// - ///void setVideoModeHdr(int hdr); - - void onImage(const Image& image); + void SignalImageToSend(const Image& image); private: - - /// - /// @brief Parse a reply message - /// @param reply The received reply - /// @return true if the reply indicates success - /// bool parseReply(const hyperhdrnet::Reply* reply); -private: - /// The TCP-Socket with the connection to the server QTcpSocket* _socket; QLocalSocket* _domain; QString _origin; int _priority; - - /// Host address QString _host; - - /// Host port uint16_t _port; - /// buffer for reply QByteArray _receiveBuffer; QTimer _timer; QAbstractSocket::SocketState _prevSocketState; diff --git a/include/flatbufserver/FlatBufferServer.h b/include/flatbufserver/FlatBufferServer.h index 49d8c9f0a..53b96cad8 100644 --- a/include/flatbufserver/FlatBufferServer.h +++ b/include/flatbufserver/FlatBufferServer.h @@ -1,12 +1,14 @@ #pragma once -// util -#include -#include -#include +#ifndef PCH_ENABLED + #include -// qt -#include + #include + #include + #include + #include + #include +#endif class BonjourServiceRegister; class QTcpServer; @@ -15,97 +17,54 @@ class FlatBufferClient; class NetOrigin; #define HYPERHDR_DOMAIN_SERVER QStringLiteral("hyperhdr-domain") +#define BASEAPI_FLATBUFFER_USER_LUT_FILE QStringLiteral("BASEAPI_user_lut_file") -/// -/// @brief A TcpServer to receive images of different formats with Google Flatbuffer -/// Images will be forwarded to all HyperHdr instances -/// class FlatBufferServer : public QObject { Q_OBJECT public: - FlatBufferServer(const QJsonDocument& config, const QString& configurationPath, QObject* parent = nullptr); + FlatBufferServer(std::shared_ptr netOrigin, const QJsonDocument& config, const QString& configurationPath, QObject* parent = nullptr); ~FlatBufferServer() override; - static FlatBufferServer* instance; - static FlatBufferServer* getInstance() { return instance; } - signals: - void hdrToneMappingChanged(int mode, uint8_t* lutBuffer); - void HdrChanged(int mode); + void SignalSetNewComponentStateToAllInstances(hyperhdr::Components component, bool enable); + void SignalImportFromProto(int priority, int duration, const Image& image, QString clientDescription); public slots: - /// - /// @brief Handle settings update - /// @param type The type from enum - /// @param config The configuration - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - void initServer(); - - void setUserLut(QString filename); - - void setHdrToneMappingEnabled(int mode); - int getHdrToneMappingEnabled(); - - void importFromProtoHandler(int priority, int duration, const Image& image); + void handlerImportFromProto(int priority, int duration, const Image& image, QString clientDescription); + void handlerImageReceived(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin, QString clientDescription); + void signalRequestSourceHandler(hyperhdr::Components component, int instanceIndex, bool listen); private slots: - /// - /// @brief Is called whenever a new socket wants to connect - /// - void newConnection(); - - /// - /// @brief is called whenever a client disconnected - /// - void clientDisconnected(); + void handlerNewConnection(); + void handlerClientDisconnected(FlatBufferClient* client); private: - /// - /// @brief Start the server with current _port - /// void startServer(); - - /// - /// @brief Stop server - /// void stopServer(); - - - /// - /// @brief Get shared LUT file folder - /// QString GetSharedLut(); - - /// - /// @brief Load LUT file - /// void loadLutFile(); - void setupClient(FlatBufferClient* client); - -private: QTcpServer* _server; QLocalServer* _domain; - NetOrigin* _netOrigin; + std::shared_ptr _netOrigin; Logger* _log; int _timeout; quint16 _port; const QJsonDocument _config; BonjourServiceRegister* _serviceRegister = nullptr; - QVector _openConnections; - // tone mapping int _hdrToneMappingMode; - int _realHdrToneMappingMode; - uint8_t* _lutBuffer; + int _realHdrToneMappingMode; bool _lutBufferInit; QString _configurationPath; QString _userLutFile; + + MemoryBuffer _lut; }; diff --git a/include/grabber/AVFGrabber.h b/include/grabber/AVF/AVFGrabber.h similarity index 85% rename from include/grabber/AVFGrabber.h rename to include/grabber/AVF/AVFGrabber.h index 194aeb16e..05a4e541e 100644 --- a/include/grabber/AVFGrabber.h +++ b/include/grabber/AVF/AVFGrabber.h @@ -15,7 +15,7 @@ // util includes #include #include -#include +#include #include #include #include @@ -51,9 +51,9 @@ public slots: void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override; bool getPermission(); diff --git a/include/grabber/AVFWorker.h b/include/grabber/AVF/AVFWorker.h similarity index 83% rename from include/grabber/AVFWorker.h rename to include/grabber/AVF/AVFWorker.h index b5b939440..bfebd032b 100644 --- a/include/grabber/AVFWorker.h +++ b/include/grabber/AVF/AVFWorker.h @@ -40,7 +40,8 @@ class AVFWorker : public QThread unsigned __cropLeft, unsigned __cropTop, unsigned __cropBottom, unsigned __cropRight, quint64 __currentFrame, qint64 __frameBegin, - int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe); + int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe, + bool __directAccess, QString __deviceName); void startOnThisThread(); void run() override; @@ -52,8 +53,8 @@ class AVFWorker : public QThread ~AVFWorker(); signals: - void newFrame(unsigned int workerIndex, Image data, quint64 sourceCount, qint64 _frameBegin); - void newFrameError(unsigned int workerIndex, QString, quint64 sourceCount); + void SignalNewFrame(unsigned int workerIndex, Image data, quint64 sourceCount, qint64 _frameBegin); + void SignalNewFrameError(unsigned int workerIndex, QString, quint64 sourceCount); private: void runMe(); @@ -63,8 +64,7 @@ class AVFWorker : public QThread QSemaphore _semaphore; unsigned int _workerIndex; PixelFormat _pixelFormat; - uint8_t* _localData; - int _localDataSize; + MemoryBuffer _localBuffer; int _size; int _width; int _height; @@ -79,6 +79,8 @@ class AVFWorker : public QThread uint8_t _hdrToneMappingEnabled; uint8_t* _lutBuffer; bool _qframe; + bool _directAccess; + QString _deviceName; }; class AVFWorkerManager : public QObject diff --git a/include/grabber/AVFWrapper.h b/include/grabber/AVF/AVFWrapper.h similarity index 74% rename from include/grabber/AVFWrapper.h rename to include/grabber/AVF/AVFWrapper.h index 6e26d04de..9c9e2205c 100644 --- a/include/grabber/AVFWrapper.h +++ b/include/grabber/AVF/AVFWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class AVFWrapper : public GrabberWrapper { @@ -9,7 +9,4 @@ class AVFWrapper : public GrabberWrapper public: AVFWrapper(const QString& device, const QString& configurationPath); - -private: - AVFGrabber _grabber; }; diff --git a/include/grabber/DxGrabber.h b/include/grabber/DX/DxGrabber.h similarity index 59% rename from include/grabber/DxGrabber.h rename to include/grabber/DX/DxGrabber.h index 4cddd29c2..b9f3f943d 100644 --- a/include/grabber/DxGrabber.h +++ b/include/grabber/DX/DxGrabber.h @@ -3,8 +3,9 @@ #include #include #include -#include +#include #include +#include // stl includes #include @@ -18,18 +19,12 @@ #include #include #include -#include // util includes #include #include #include -// general JPEG decoder includes -#include -#include - - class DxGrabber : public Grabber { Q_OBJECT @@ -44,23 +39,19 @@ class DxGrabber : public Grabber void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) override; - void alternativeCaching(bool alternative) override; - public slots: void grabFrame(); void cacheHandler(const Image& image); -public slots: - bool start() override; void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override; void restart(); @@ -77,20 +68,31 @@ public slots: void uninit() override; - bool init_device(QString selectedDeviceName); + bool initDirectX(QString selectedDeviceName); + + bool initShaders(); + HRESULT deepScaledCopy(ID3D11Texture2D* source); -private: QString _configurationPath; - QTimer _timer; - QSemaphore _semaphore; + QTimer* _timer; + QTimer* _retryTimer; int _warningCounter; + int _actualDivide; + bool _wideGamut; bool _dxRestartNow; - bool _d3dCache; - bool _alternative; ID3D11Device* _d3dDevice; ID3D11DeviceContext* _d3dContext; - ID3D11Texture2D* _sourceTexture; + ID3D11Buffer* _d3dBuffer; + ID3D11SamplerState* _d3dSampler; + ID3D11InputLayout* _d3dVertexLayout; + ID3D11VertexShader* _d3dVertexShader; + ID3D11PixelShader* _d3dPixelShader; + ID3D11Texture2D* _d3dSourceTexture; + ID3D11Texture2D* _d3dConvertTexture; + ID3D11ShaderResourceView* _d3dConvertTextureView; + ID3D11RenderTargetView* _d3dRenderTargetView; IDXGIOutputDuplication* _d3dDuplicate; + DXGI_OUTDUPL_DESC _surfaceProperties; Image _cacheImage; }; diff --git a/include/grabber/DxWrapper.h b/include/grabber/DX/DxWrapper.h similarity index 86% rename from include/grabber/DxWrapper.h rename to include/grabber/DX/DxWrapper.h index 4ffb06cd3..706bde681 100644 --- a/include/grabber/DxWrapper.h +++ b/include/grabber/DX/DxWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class DxWrapper : public SystemWrapper { diff --git a/include/grabber/DX/PixelShaderHyperHDR.h b/include/grabber/DX/PixelShaderHyperHDR.h new file mode 100644 index 000000000..ae25726d9 --- /dev/null +++ b/include/grabber/DX/PixelShaderHyperHDR.h @@ -0,0 +1,193 @@ +/* PixelShaderHyperHDR.h +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +const BYTE g_PixelShaderHyperHDR[] = +{ + 68, 88, 66, 67, 177, 212, + 244, 113, 119, 191, 171, 199, + 136, 6, 209, 134, 195, 252, + 26, 229, 1, 0, 0, 0, + 208, 3, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 248, 0, 0, 0, 80, 1, + 0, 0, 132, 1, 0, 0, + 52, 3, 0, 0, 82, 68, + 69, 70, 188, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 60, 0, 0, 0, 0, 5, + 255, 255, 0, 1, 0, 0, + 147, 0, 0, 0, 82, 68, + 49, 49, 60, 0, 0, 0, + 24, 0, 0, 0, 32, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 124, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 139, 0, 0, 0, + 2, 0, 0, 0, 5, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 13, 0, 0, 0, 71, 101, + 110, 101, 114, 105, 99, 83, + 97, 109, 112, 108, 101, 114, + 0, 116, 120, 73, 110, 112, + 117, 116, 0, 77, 105, 99, + 114, 111, 115, 111, 102, 116, + 32, 40, 82, 41, 32, 72, + 76, 83, 76, 32, 83, 104, + 97, 100, 101, 114, 32, 67, + 111, 109, 112, 105, 108, 101, + 114, 32, 49, 48, 46, 49, + 0, 171, 73, 83, 71, 78, + 80, 0, 0, 0, 2, 0, + 0, 0, 8, 0, 0, 0, + 56, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 7, 7, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 171, 171, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 84, 97, 114, 103, 101, + 116, 0, 171, 171, 83, 72, + 69, 88, 168, 1, 0, 0, + 80, 0, 0, 0, 106, 0, + 0, 0, 106, 8, 0, 1, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 98, 16, + 0, 3, 114, 16, 16, 0, + 1, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 0, 0, 0, 0, 104, 0, + 0, 2, 3, 0, 0, 0, + 69, 0, 0, 139, 194, 0, + 0, 128, 67, 85, 21, 0, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 16, 16, 0, + 1, 0, 0, 0, 70, 126, + 16, 0, 0, 0, 0, 0, + 0, 96, 16, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 166, 26, + 16, 0, 1, 0, 0, 0, + 47, 0, 0, 5, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 56, 0, 0, 10, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 102, 102, 230, 62, + 102, 102, 230, 62, 102, 102, + 230, 62, 0, 0, 0, 0, + 25, 0, 0, 5, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 15, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 8, 172, 140, 63, + 8, 172, 140, 63, 8, 172, + 140, 63, 0, 0, 0, 0, + 2, 64, 0, 0, 131, 192, + 202, 189, 131, 192, 202, 189, + 131, 192, 202, 189, 0, 0, + 0, 0, 49, 0, 0, 10, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 188, 116, 147, 60, + 188, 116, 147, 60, 188, 116, + 147, 60, 0, 0, 0, 0, + 56, 0, 0, 10, 114, 0, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 144, 64, 0, 0, + 144, 64, 0, 0, 144, 64, + 0, 0, 0, 0, 55, 0, + 0, 9, 114, 32, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 11, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 7, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; diff --git a/include/grabber/DX/VertexShaderHyperHDR.h b/include/grabber/DX/VertexShaderHyperHDR.h new file mode 100644 index 000000000..7b027091f --- /dev/null +++ b/include/grabber/DX/VertexShaderHyperHDR.h @@ -0,0 +1,228 @@ +/* VertexShaderHyperHDR.h +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +const BYTE g_VertexShaderHyperHDR[] = +{ + 68, 88, 66, 67, 145, 11, + 120, 74, 74, 90, 225, 194, + 52, 8, 115, 192, 196, 34, + 123, 175, 1, 0, 0, 0, + 164, 4, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 60, 1, 0, 0, 112, 1, + 0, 0, 200, 1, 0, 0, + 8, 4, 0, 0, 82, 68, + 69, 70, 0, 1, 0, 0, + 1, 0, 0, 0, 100, 0, + 0, 0, 1, 0, 0, 0, + 60, 0, 0, 0, 0, 5, + 254, 255, 0, 1, 0, 0, + 216, 0, 0, 0, 82, 68, + 49, 49, 60, 0, 0, 0, + 24, 0, 0, 0, 32, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 92, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 80, 97, 114, 97, + 109, 115, 0, 171, 92, 0, + 0, 0, 1, 0, 0, 0, + 124, 0, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 164, 0, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 180, 0, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 112, 97, 114, 97, + 109, 115, 0, 102, 108, 111, + 97, 116, 52, 0, 171, 171, + 1, 0, 3, 0, 1, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 171, 0, 0, 0, + 77, 105, 99, 114, 111, 115, + 111, 102, 116, 32, 40, 82, + 41, 32, 72, 76, 83, 76, + 32, 83, 104, 97, 100, 101, + 114, 32, 67, 111, 109, 112, + 105, 108, 101, 114, 32, 49, + 48, 46, 49, 0, 73, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 6, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, + 0, 0, 83, 86, 95, 86, + 69, 82, 84, 69, 88, 73, + 68, 0, 79, 83, 71, 78, + 80, 0, 0, 0, 2, 0, + 0, 0, 8, 0, 0, 0, + 56, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 7, 8, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 171, 171, + 83, 72, 69, 88, 56, 2, + 0, 0, 80, 0, 1, 0, + 142, 0, 0, 0, 106, 8, + 0, 1, 89, 0, 0, 4, + 70, 142, 32, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 96, 0, 0, 4, 18, 16, + 16, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 103, 0, + 0, 4, 242, 32, 16, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 114, 32, 16, 0, 1, 0, + 0, 0, 104, 0, 0, 2, + 1, 0, 0, 0, 54, 0, + 0, 8, 194, 32, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 63, + 1, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 16, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 1, 0, 0, 0, 55, 0, + 0, 9, 18, 32, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 1, 64, 0, 0, + 0, 0, 128, 191, 86, 0, + 0, 5, 18, 32, 16, 0, + 1, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 138, 0, 0, 9, 18, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 1, 0, 0, 0, 10, 16, + 16, 0, 0, 0, 0, 0, + 55, 0, 0, 9, 34, 32, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 191, 1, 64, + 0, 0, 0, 0, 128, 63, + 86, 0, 0, 5, 34, 32, + 16, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 8, + 18, 0, 16, 0, 0, 0, + 0, 0, 26, 128, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 208, 150, 193, 56, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 86, 63, 14, 0, 0, 9, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 49, 0, 0, 7, + 34, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 154, 153, 53, 65, + 49, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 199, 75, + 159, 65, 10, 0, 16, 0, + 0, 0, 0, 0, 60, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 14, 0, 0, 8, + 34, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 160, 66, 10, 128, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 238, 105, + 4, 59, 55, 0, 0, 9, + 66, 32, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 17, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 7, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/include/grabber/MFCallback.h b/include/grabber/MF/MFCallback.h similarity index 100% rename from include/grabber/MFCallback.h rename to include/grabber/MF/MFCallback.h diff --git a/include/grabber/MFGrabber.h b/include/grabber/MF/MFGrabber.h similarity index 64% rename from include/grabber/MFGrabber.h rename to include/grabber/MF/MFGrabber.h index 1d719fe19..908dd8e70 100644 --- a/include/grabber/MFGrabber.h +++ b/include/grabber/MF/MFGrabber.h @@ -1,30 +1,10 @@ #pragma once -#include - -// stl includes -#include -#include -#include - -// Qt includes -#include -#include -#include -#include -#include - -// util includes #include #include -#include +#include #include -// general JPEG decoder includes -#include -#include -#include - class MFCallback; struct IMFSourceReader; @@ -34,13 +14,6 @@ class MFGrabber : public Grabber public: - typedef struct - { - const GUID format_id; - const QString format_name; - PixelFormat pixel; - } VideoFormat; - MFGrabber(const QString& device, const QString& configurationPath); ~MFGrabber(); @@ -57,9 +30,9 @@ public slots: void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override; private: QString GetSharedLut(); diff --git a/include/grabber/MFWorker.h b/include/grabber/MF/MFWorker.h similarity index 68% rename from include/grabber/MFWorker.h rename to include/grabber/MF/MFWorker.h index de6855177..666636ed9 100644 --- a/include/grabber/MFWorker.h +++ b/include/grabber/MF/MFWorker.h @@ -1,33 +1,17 @@ #pragma once -// stl includes -#include -#include -#include - -// Qt includes -#include -#include -#include -#include -#include -#include - -// util includes +#ifndef PCH_ENABLED + #include +#endif + #include #include #include -// general JPEG decoder includes -#include -#include - -// TurboJPEG decoder #include - -/// MT worker for MF devices class MFWorkerManager; + class MFWorker : public QThread { Q_OBJECT @@ -42,7 +26,8 @@ class MFWorker : public QThread unsigned __cropLeft, unsigned __cropTop, unsigned __cropBottom, unsigned __cropRight, quint64 __currentFrame, qint64 __frameBegin, - int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe); + int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe, + bool __directAccess, QString __deviceName); void startOnThisThread(); void run() override; @@ -54,8 +39,8 @@ class MFWorker : public QThread ~MFWorker(); signals: - void newFrame(unsigned int workerIndex, Image data, quint64 sourceCount, qint64 _frameBegin); - void newFrameError(unsigned int workerIndex, QString, quint64 sourceCount); + void SignalNewFrame(unsigned int workerIndex, Image data, quint64 sourceCount, qint64 _frameBegin); + void SignalNewFrameError(unsigned int workerIndex, QString, quint64 sourceCount); private: void runMe(); @@ -65,11 +50,9 @@ class MFWorker : public QThread static std::atomic _isActive; std::atomic _isBusy; - QSemaphore _semaphore; unsigned int _workerIndex; PixelFormat _pixelFormat; - uint8_t* _localData; - int _localDataSize; + MemoryBuffer _localBuffer; int _size; int _width; int _height; @@ -82,8 +65,10 @@ class MFWorker : public QThread quint64 _currentFrame; qint64 _frameBegin; uint8_t _hdrToneMappingEnabled; - uint8_t* _lutBuffer; + uint8_t* _lutBuffer; bool _qframe; + bool _directAccess; + QString _deviceName; }; class MFWorkerManager : public QObject diff --git a/include/grabber/MFWrapper.h b/include/grabber/MF/MFWrapper.h similarity index 74% rename from include/grabber/MFWrapper.h rename to include/grabber/MF/MFWrapper.h index 1c2bb1c16..342a78db9 100644 --- a/include/grabber/MFWrapper.h +++ b/include/grabber/MF/MFWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class MFWrapper : public GrabberWrapper { @@ -9,7 +9,4 @@ class MFWrapper : public GrabberWrapper public: MFWrapper(const QString& device, const QString& configurationPath); - -private: - MFGrabber _grabber; }; diff --git a/include/grabber/SoundCapLinux.h b/include/grabber/SoundCapLinux.h deleted file mode 100644 index d03e59ab1..000000000 --- a/include/grabber/SoundCapLinux.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#define SOUNDCAPLINUX_BUF_LENP 10 -class SoundCapLinux : public SoundCapture -{ - friend class HyperHdrDaemon; - public: - - private: - SoundCapLinux(const QJsonDocument& effectConfig, QObject* parent = nullptr); - ~SoundCapLinux(); - void ListDevices(); - void Start() override; - void Stop() override; - - snd_pcm_t* _handle; - snd_async_handler_t* _pcmCallback; - - private: - static void RecordCallback(snd_async_handler_t* audioHandler); - - static int16_t _soundBuffer[(1< - -#include -#include - -#include - -#define SOUNDCAPMACOS_BUF_LENP 10 -class SoundCapMacOS : public SoundCapture -{ - friend class HyperHdrDaemon; - public: - void RecordCallback(uint32_t frameBytes, uint8_t* rawVideoData); - private: - SoundCapMacOS(const QJsonDocument& effectConfig, QObject* parent = nullptr); - ~SoundCapMacOS(); - void ListDevices(); - void Start() override; - void Stop() override; - bool getPermission(); - - static size_t _soundBufferIndex; - static int16_t _soundBuffer[(1< -#include -#include "mmsystem.h" - -#define SOUNDCAPWINDOWS_BUF_LENP 10 -class SoundCapWindows : public SoundCapture -{ - friend class HyperHdrDaemon; -public: - -private: - SoundCapWindows(const QJsonDocument& effectConfig, QObject* parent = nullptr); - ~SoundCapWindows(); - void ListDevices(); - void Start() override; - void Stop() override; - - static void CALLBACK soundInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); - static int16_t _bufferIndex; - static int16_t _soundBuffer[(1 << SOUNDCAPWINDOWS_BUF_LENP) + 64]; - static WAVEHDR _header; - static HWAVEIN _hWaveIn; -}; - diff --git a/include/grabber/X11Grabber.h b/include/grabber/X11/X11Grabber.h similarity index 83% rename from include/grabber/X11Grabber.h rename to include/grabber/X11/X11Grabber.h index 8c3789e49..40d12b76d 100644 --- a/include/grabber/X11Grabber.h +++ b/include/grabber/X11/X11Grabber.h @@ -50,9 +50,9 @@ public slots: void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; private: QString GetSharedLut(); @@ -69,7 +69,6 @@ public slots: bool init_device(int _display); -private: QString _configurationPath; QTimer _timer; QSemaphore _semaphore; diff --git a/include/grabber/X11Wrapper.h b/include/grabber/X11/X11Wrapper.h similarity index 87% rename from include/grabber/X11Wrapper.h rename to include/grabber/X11/X11Wrapper.h index 294818812..6d333f571 100644 --- a/include/grabber/X11Wrapper.h +++ b/include/grabber/X11/X11Wrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class X11Wrapper : public SystemWrapper { diff --git a/include/grabber/smartX11.h b/include/grabber/X11/smartX11.h similarity index 100% rename from include/grabber/smartX11.h rename to include/grabber/X11/smartX11.h diff --git a/include/grabber/FrameBufGrabber.h b/include/grabber/framebuffer/FrameBufGrabber.h similarity index 83% rename from include/grabber/FrameBufGrabber.h rename to include/grabber/framebuffer/FrameBufGrabber.h index 3185f27e2..2d05e17a8 100644 --- a/include/grabber/FrameBufGrabber.h +++ b/include/grabber/framebuffer/FrameBufGrabber.h @@ -52,9 +52,9 @@ public slots: void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; private: QString GetSharedLut(); diff --git a/include/grabber/FrameBufWrapper.h b/include/grabber/framebuffer/FrameBufWrapper.h similarity index 86% rename from include/grabber/FrameBufWrapper.h rename to include/grabber/framebuffer/FrameBufWrapper.h index a20fadf19..122dff8d2 100644 --- a/include/grabber/FrameBufWrapper.h +++ b/include/grabber/framebuffer/FrameBufWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class FrameBufWrapper : public SystemWrapper { diff --git a/include/grabber/macOsGrabber.h b/include/grabber/macOS/macOsGrabber.h similarity index 83% rename from include/grabber/macOsGrabber.h rename to include/grabber/macOS/macOsGrabber.h index 53d086b38..10990587f 100644 --- a/include/grabber/macOsGrabber.h +++ b/include/grabber/macOS/macOsGrabber.h @@ -48,9 +48,9 @@ public slots: void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; private: QString GetSharedLut(); diff --git a/include/grabber/macOsWrapper.h b/include/grabber/macOS/macOsWrapper.h similarity index 84% rename from include/grabber/macOsWrapper.h rename to include/grabber/macOS/macOsWrapper.h index 877cf23a1..0153e4703 100644 --- a/include/grabber/macOsWrapper.h +++ b/include/grabber/macOS/macOsWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class macOsWrapper : public SystemWrapper { diff --git a/include/grabber/PipewireGrabber.h b/include/grabber/pipewire/PipewireGrabber.h similarity index 81% rename from include/grabber/PipewireGrabber.h rename to include/grabber/pipewire/PipewireGrabber.h index 395c91b58..2be3ada41 100644 --- a/include/grabber/PipewireGrabber.h +++ b/include/grabber/pipewire/PipewireGrabber.h @@ -23,6 +23,8 @@ #include #include +class AccessManager; + class PipewireGrabber : public Grabber { @@ -52,9 +54,9 @@ public slots: void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override {}; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; private: QString GetSharedLut(); @@ -76,11 +78,11 @@ public slots: private: QString _configurationPath; QTimer _timer; - QSemaphore _semaphore; void* _library; int _actualDisplay; bool _isActive; bool _storedToken; bool _versionCheck; + std::shared_ptr _accessManager; }; diff --git a/sources/grabber/pipewire/PipewireHandler.h b/include/grabber/pipewire/PipewireHandler.h similarity index 97% rename from sources/grabber/pipewire/PipewireHandler.h rename to include/grabber/pipewire/PipewireHandler.h index cc6201202..7c52ce60f 100644 --- a/sources/grabber/pipewire/PipewireHandler.h +++ b/include/grabber/pipewire/PipewireHandler.h @@ -5,12 +5,13 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include #include @@ -86,7 +87,7 @@ class PipewireHandler : public QObject uint objectId; uint width, height; QVariantMap properties; - }; + }; public Q_SLOTS: void releaseWorkingFrame(); @@ -106,7 +107,8 @@ public Q_SLOTS: void onProcessFrameSignal(); void onCoreErrorSignal(uint32_t id, int seq, int res, const char *message); -private: +private: + uint8_t* createMemory(int size); void reportError(const QString& input); const QString fourCCtoString(int64_t val); @@ -151,6 +153,8 @@ public Q_SLOTS: int64_t _frameDrmModifier; PipewireImage _image; + MemoryBuffer _memoryCache; + #ifdef ENABLE_PIPEWIRE_EGL eglGetProcAddressFun eglGetProcAddress = nullptr; eglInitializeFun eglInitialize = nullptr; diff --git a/include/grabber/PipewireWrapper.h b/include/grabber/pipewire/PipewireWrapper.h similarity index 89% rename from include/grabber/PipewireWrapper.h rename to include/grabber/pipewire/PipewireWrapper.h index 5b83948ab..eea18bbba 100644 --- a/include/grabber/PipewireWrapper.h +++ b/include/grabber/pipewire/PipewireWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class PipewireWrapper : public SystemWrapper { diff --git a/include/grabber/smartPipewire.h b/include/grabber/pipewire/smartPipewire.h similarity index 95% rename from include/grabber/smartPipewire.h rename to include/grabber/pipewire/smartPipewire.h index 3f7040e91..651000966 100644 --- a/include/grabber/smartPipewire.h +++ b/include/grabber/pipewire/smartPipewire.h @@ -5,7 +5,7 @@ struct PipewireImage { int version; - bool isError; + bool isError = false; int width, height, stride; bool isOrderRgb; uint8_t* data = nullptr; diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/v4l2/V4L2Grabber.h similarity index 74% rename from include/grabber/V4L2Grabber.h rename to include/grabber/v4l2/V4L2Grabber.h index af7c769d2..bd16c693e 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/v4l2/V4L2Grabber.h @@ -1,28 +1,18 @@ #pragma once -// stl includes -#include -#include -#include - -// Qt includes -#include -#include -#include -#include -#include - -// util includes +#ifndef PCH_ENABLED + #include +#endif + #include #include -#include +#include #include -// general JPEG decoder includes -#include -#include #include +class QSocketNotifier; + class V4L2Grabber : public Grabber { Q_OBJECT @@ -48,9 +38,9 @@ public slots: void stop() override; - void newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; + void newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) override; - void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override; + void newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) override; private slots: int read_frame(); @@ -100,12 +90,10 @@ private slots: bool setControl(__u32 controlId, __s32 newValue); -private: - struct buffer { - void* start; - size_t length; + void* start; + size_t length; }; int _fileDescriptor; diff --git a/include/grabber/V4L2Worker.h b/include/grabber/v4l2/V4L2Worker.h similarity index 67% rename from include/grabber/V4L2Worker.h rename to include/grabber/v4l2/V4L2Worker.h index 0943d842b..6fe4c2e7d 100644 --- a/include/grabber/V4L2Worker.h +++ b/include/grabber/v4l2/V4L2Worker.h @@ -1,34 +1,22 @@ #pragma once -// stl includes -#include -#include -#include - -// Qt includes -#include -#include -#include -#include -#include -#include - -// util includes +#ifndef PCH_ENABLED + #include + + #include + #include +#endif + #include #include #include -#include - -// general JPEG decoder includes -#include -#include -// TurboJPEG decoder #include - -/// MT worker for V4L2 devices class V4L2WorkerManager; +struct v4l2_buffer; +class V4L2Worker; + class V4L2Worker : public QThread { Q_OBJECT @@ -38,12 +26,13 @@ class V4L2Worker : public QThread void setup( unsigned int _workerIndex, v4l2_buffer* __v4l2Buf, PixelFormat __pixelFormat, - uint8_t* __sharedData, + uint8_t* __sharedData, int __size, int __width, int __height, int __lineLength, unsigned __cropLeft, unsigned __cropTop, unsigned __cropBottom, unsigned __cropRight, quint64 __currentFrame, qint64 __frameBegin, - int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe); + int __hdrToneMappingEnabled, uint8_t* __lutBuffer, + bool __qframe, bool __directAccess, QString __deviceName); void startOnThisThread(); void run() override; @@ -56,8 +45,8 @@ class V4L2Worker : public QThread ~V4L2Worker(); signals: - void newFrame(unsigned int workerIndex, Image data, quint64 sourceCount, qint64 _frameBegin); - void newFrameError(unsigned int workerIndex, QString, quint64 sourceCount); + void SignalNewFrame(unsigned int workerIndex, Image data, quint64 sourceCount, qint64 _frameBegin); + void SignalNewFrameError(unsigned int workerIndex, QString, quint64 sourceCount); private: void runMe(); @@ -67,7 +56,6 @@ class V4L2Worker : public QThread static std::atomic _isActive; std::atomic _isBusy; - QSemaphore _semaphore; unsigned int _workerIndex; struct v4l2_buffer _v4l2Buf; PixelFormat _pixelFormat; @@ -86,6 +74,8 @@ class V4L2Worker : public QThread uint8_t _hdrToneMappingEnabled; uint8_t* _lutBuffer; bool _qframe; + bool _directAccess; + QString _deviceName; }; class V4L2WorkerManager : public QObject @@ -103,5 +93,5 @@ class V4L2WorkerManager : public QObject // MT workers unsigned int workersCount; - V4L2Worker** workers; + V4L2Worker** workers; }; diff --git a/include/grabber/V4L2Wrapper.h b/include/grabber/v4l2/V4L2Wrapper.h similarity index 73% rename from include/grabber/V4L2Wrapper.h rename to include/grabber/v4l2/V4L2Wrapper.h index fe753950e..d386e6d6d 100644 --- a/include/grabber/V4L2Wrapper.h +++ b/include/grabber/v4l2/V4L2Wrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class V4L2Wrapper : public GrabberWrapper { @@ -9,7 +9,4 @@ class V4L2Wrapper : public GrabberWrapper public: V4L2Wrapper(const QString& device, const QString& configurationPath); - -private: - V4L2Grabber _grabber; }; diff --git a/include/jsonserver/JsonServer.h b/include/jsonserver/JsonServer.h index e74c279c5..217f0aa95 100644 --- a/include/jsonserver/JsonServer.h +++ b/include/jsonserver/JsonServer.h @@ -1,7 +1,8 @@ #pragma once -// Qt includes -#include +#ifndef PCH_ENABLED + #include +#endif #include #include @@ -12,65 +13,33 @@ class QTcpSocket; class JsonClientConnection; class BonjourServiceRegister; class NetOrigin; +class HyperHdrManager; -/// -/// This class creates a TCP server which accepts connections wich can then send -/// in JSON encoded commands. This interface to HyperHdr is used by hyperhdr-remote -/// to control the leds -/// class JsonServer : public QObject { Q_OBJECT public: - /// - /// JsonServer constructor - /// @param The configuration - /// - JsonServer(const QJsonDocument& config); + JsonServer(std::shared_ptr netOrigin, const QJsonDocument& config); ~JsonServer() override; - /// - /// @return the port number on which this TCP listens for incoming connections - /// uint16_t getPort() const; - private slots: - /// - /// Slot which is called when a client tries to create a new connection - /// void newConnection(); - - /// - /// Slot which is called when a client closes a connection - /// - void closedConnection(); + void signalClientConnectionClosedHandler(JsonClientConnection* client); public slots: - /// - /// @brief Handle settings update from Hyperhdr Settingsmanager emit or this constructor - /// @param type settings type from enum - /// @param config configuration object - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); private: - /// The TCP server object QTcpServer* _server; - - /// List with open connections QSet _openConnections; - - /// the logger instance Logger* _log; - NetOrigin* _netOrigin; - - /// port - uint16_t _port = 0; + std::shared_ptr _netOrigin; - BonjourServiceRegister* _serviceRegister = nullptr; + uint16_t _port; void start(); void stop(); diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h index 5284b4bbe..2c8b127bb 100644 --- a/include/leddevice/LedDevice.h +++ b/include/leddevice/LedDevice.h @@ -1,446 +1,158 @@ -#ifndef LEDEVICE_H -#define LEDEVICE_H +#pragma once + +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + + #include + #include + #include + #include +#endif -// qt includes -#include -#include -#include -#include -#include -#include -#include - -// STL includes -#include -#include -#include -#include - -// Utility includes -#include #include -#include -#include -#include #include class LedDevice; +class DiscoveryWrapper; typedef LedDevice* (*LedDeviceCreateFuncType) (const QJsonObject&); typedef std::map LedDeviceRegistry; -/// -/// @brief Interface (pure virtual base class) for LED-devices. -/// class LedDevice : public QObject { Q_OBJECT public: - /// - /// @brief Constructs LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// @param parent QT parent - /// LedDevice(const QJsonObject& deviceConfig = QJsonObject(), QObject* parent = nullptr); + virtual ~LedDevice(); - /// - /// @brief Destructor of the LED-device - /// - ~LedDevice() override; - - /// - /// @brief Set the current active LED-device type. - /// - /// @param deviceType Device's type - /// void setActiveDeviceType(const QString& deviceType); - - /// - /// @brief Set the number of LEDs supported by the device. - /// - /// @param[in] ledCount Number of device LEDs, 0 = unknown number - /// void setLedCount(int ledCount); - - /// - /// @brief Set a device's refresh time. - /// - /// Refresh time is the time frame a devices requires to be refreshed, if no updated happened in the meantime. - /// - /// @param[in] refreshTime_ms refresh time in milliseconds - /// - void setRefreshTime(int refreshTime_ms); - - /// - /// @brief Discover devices of this type available (for configuration). - /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways. - /// - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// + void setRefreshTime(int userInterval); virtual QJsonObject discover(const QJsonObject& params); - - /// - /// @brief Discover first device of this type available (for configuration). - /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways. - /// - /// @return A string of the device found - /// virtual QString discoverFirst(); - - /// - /// @brief Get the device's properties - /// - /// Used in context of a set of devices of the same type. - /// - /// @param[in] params Parameters to address device - /// @return A JSON structure holding the device's properties - /// virtual QJsonObject getProperties(const QJsonObject& params); - - /// - /// @brief Send an update to the device to identify it. - /// - /// Used in context of a set of devices of the same type. - /// - /// @param[in] params Parameters to address device - /// virtual void identify(const QJsonObject& /*params*/) {} - virtual void identifyLed(const QJsonObject& /*params*/); - - /// - /// @brief Check, if device is properly initialised - /// - /// i.e. initialisation and configuration were successful. - /// - /// @return True, if device is initialised - /// bool isInitialised() const; - - /// - /// @brief Check, if device is ready to be used. - /// - /// i.e. initialisation and opening were successful. - /// - /// @return True, if device is ready - /// bool isReady() const; - - /// - /// @brief Check, if device is in error state. - /// - /// @return True, if device is in error - /// bool isInError() const; + void setInstanceIndex(int instanceIndex); - /// - /// @brief Prints the color values to stdout. - /// - /// @param[in] ledValues The color per led - /// static void printLedValues(const std::vector& ledValues); - static void signalTerminateTriggered(); public slots: - - /// - /// @brief Is called on thread start, all construction tasks and init should run here. - /// virtual void start(); - - /// - /// @brief Stops the device. - /// - /// Includes switching-off the device and stopping refreshes. - /// virtual void stop(); - - /// - /// @brief Update the color values of the device's LEDs. - /// - /// Handles refreshing of LEDs. - /// - /// @param[in] ledValues The color per LED - /// @return Zero on success else negative (i.e. device is not ready) - /// - virtual int updateLeds(std::vector ledValues); - - /// - /// @brief Get the currently defined RefreshTime. - /// - /// @return Rewrite time in milliseconds - /// + void updateLeds(std::vector ledValues); int getRefreshTime() const; - - /// - /// @brief Get the number of LEDs supported by the device. - /// - /// @return Number of device's LEDs, 0 = unknown number - /// int getLedCount() const; - - /// - /// @brief Get the current active LED-device type. - /// QString getActiveDeviceType() const; - - /// - /// @brief Get the LED-Device component's state. - /// - /// @return True, if enabled - /// bool componentState() const; - - /// - /// @brief Enables the device for output. - /// - /// If the device is not ready, it will not be enabled. - /// void enable(); - - /// - /// @brief Disables the device for output. - /// void disable(); - - /// - /// @brief Switch the LEDs on. - /// - /// Takes care that the device is opened and powered-on. - /// Depending on the configuration, the device may store its current state for later restore. - /// @see powerOn, storeState - /// - /// @return True, if success - /// virtual bool switchOn(); - - /// - /// @brief Switch the LEDs off. - /// - /// Takes care that the LEDs and device are switched-off and device is closed. - /// Depending on the configuration, the device may be powered-off or restored to its previous state. - /// @see powerOff, restoreState - /// - /// @return True, if success - /// virtual bool switchOff(); - void blinking(QJsonObject params); + void smoothingRestarted(int newSmoothingInterval); + int hasLedClock(); signals: - /// - /// @brief Emits whenever the LED-Device switches between on/off. - /// - /// @param[in] newState The new state of the device - /// - void enableStateChanged(bool newState); - - void manualUpdate(); - - void newCounter(PerformanceReport pr); + void SignalEnableStateChanged(bool newState); + void SignalManualUpdate(); + void SignalSmoothingClockTick(); protected: - /// - /// @brief Initialise the device's configuration. - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// virtual bool init(const QJsonObject& deviceConfig); - - /// - /// @brief Opens the output device. - /// - /// @return Zero, on success (i.e. device is ready), else negative - /// virtual int open(); - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// virtual int close(); - - /// - /// @brief 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) = 0; - - /// - /// @brief Writes "BLACK" to the output stream, - /// even if the device is not in enabled state (allowing to have a defined state during device power-off). - /// @note: latch-time is considered between each write - /// - /// @param[in] numberOfWrites Write Black given number of times - /// @return Zero on success else negative - /// virtual int writeBlack(int numberOfBlack = 2); - - /// - /// @brief Power-/turn on the LED-device. - /// - /// Powers-/Turns on the LED hardware, if supported. - /// - /// @return True, if success - /// virtual bool powerOn(); - - /// - /// @brief Power-/turn off the LED-device. - /// - /// Depending on the device's capability, the device is powered-/turned off or - /// an off state is simulated by writing "Black to LED" (default). - /// - /// @return True, if success - /// virtual bool powerOff(); - - /// - /// @brief Store the device's original state. - /// - /// Save the device's state before hyperhdr color streaming starts allowing to restore state during switchOff(). - /// - /// @return True, if success - /// virtual bool storeState(); - - /// - /// @brief Restore the device's original state. - /// - /// Restore the device's state as before hyperhdr color streaming started. - /// This includes the on/off state of the device. - /// - /// @return True, if success - /// virtual bool restoreState(); - /// - /// @brief Converts an uint8_t array to hex string. - /// - /// @param data uint8_t array - /// @param size of the array - /// @param number Number of array items to be converted. - /// @return array as string of hex values - QString uint8_t_to_hex_string(const uint8_t* data, const int size, int number = -1) const; + void enableDevice(bool toEmit); + void disableDevice(bool toEmit); + void startRefreshTimer(); + void setupRetry(int interval); - /// - /// @brief Converts a ByteArray to hex string. - /// - /// @param data ByteArray - /// @param number Number of array items to be converted. - /// @return array as string of hex values + QString uint8_t_to_hex_string(const uint8_t* data, const int size, int number = -1) const; QString toHex(const QByteArray& data, int number = -1) const; - /// Current device's type QString _activeDeviceType; - - /// Helper to pipe device configuration from constructor to start() QJsonObject _devConfig; - - /// The common Logger instance for all LED-devices Logger* _log; - /// The buffer containing the packed RGB values std::vector _ledBuffer; + std::unique_ptr _refreshTimer; - /// Timer object which makes sure that LED data is written at a minimum rate - /// e.g. some devices will switch off when they do not receive data at least every 15 seconds - QTimer* _refreshTimer; - - // Device configuration parameters - - /// Refresh interval in milliseconds - int _refreshTimerInterval_ms; - - /// Number of hardware LEDs supported by device. + int _currentInterval; + int _defaultInterval; + int _forcedInterval; + int _smoothingInterval; + uint _ledCount; uint _ledRGBCount; uint _ledRGBWCount; - /// Does the device allow restoring the original state? bool _isRestoreOrigState; - - /// Device, lights state before streaming via hyperhdr QJsonObject _orignalStateValues; - - // Device states - /// Is the device enabled? bool _isEnabled; - - /// Is the device initialised? bool _isDeviceInitialised; - - /// Is the device ready for processing? bool _isDeviceReady; - - /// Is the device switched on? bool _isOn; - - /// Is the device in error state and stopped? bool _isDeviceInError; - bool _retryMode; int _maxRetry; int _currentRetry; + QString _customInfo; + std::unique_ptr _retryTimer; static std::atomic _signalTerminate; -protected slots: + std::weak_ptr _discoveryWrapper; - /// - /// @brief Write the last data to the LEDs again. - /// - /// @return Zero on success else negative - /// +protected slots: int rewriteLEDs(); - - /// - /// @brief Set device in error state - /// - /// @param[in] errorMsg The error message to be logged - /// virtual void setInError(const QString& errorMsg); -protected: - void enableDevice(bool toEmit); - void disableDevice(bool toEmit); - void startRefreshTimer(); - private: - - /// @brief Stop refresh cycle void stopRefreshTimer(); + void stopRetryTimer(); - /// Is last write refreshing enabled? - bool _isRefreshEnabled; - - bool _newFrame2Send; - int64_t _newFrame2SendTime; - - /// Last LED values written + std::atomic_bool _isRefreshEnabled; + std::atomic_bool _newFrame2Send; std::vector _lastLedValues; - struct + struct LedStats { qint64 token = 0; qint64 statBegin = 0; qint64 frames = 0; qint64 incomingframes = 0; qint64 droppedFrames = 0; + + void reset(int64_t now); } _computeStats; int _blinkIndex; + qint64 _blinkTime; + int _instanceIndex; }; - -#endif // LEDEVICE_H diff --git a/include/leddevice/LedDeviceFactory.h b/include/leddevice/LedDeviceFactory.h index 17f62dcc3..b6aca4190 100644 --- a/include/leddevice/LedDeviceFactory.h +++ b/include/leddevice/LedDeviceFactory.h @@ -1,24 +1,10 @@ -#ifndef LEDEVICEFACTORY_H -#define LEDEVICEFACTORY_H +#pragma once class LedDevice; class QJsonObject; -/// -/// The LedDeviceFactory is responsible for constructing 'LedDevices' -/// class LedDeviceFactory { public: - /// - /// Constructs a LedDevice based on the given configuration - /// - /// @param deviceConfig The configuration of the led-device - /// - /// @return The constructed LedDevice or nullptr if configuration is invalid. The ownership of - /// the constructed LedDevice is transferred to the caller - /// static LedDevice* construct(const QJsonObject& deviceConfig); }; - -#endif // LEDEVICEFACTORY_H diff --git a/include/leddevice/LedDeviceWrapper.h b/include/leddevice/LedDeviceWrapper.h index a5d4c692a..50b8555d8 100644 --- a/include/leddevice/LedDeviceWrapper.h +++ b/include/leddevice/LedDeviceWrapper.h @@ -1,116 +1,47 @@ -#ifndef LEDEVICEWRAPPER_H -#define LEDEVICEWRAPPER_H +#pragma once + +#ifndef PCH_ENABLED + #include +#endif // util #include #include #include -#include - class LedDevice; class HyperHdrInstance; typedef LedDevice* (*LedDeviceCreateFuncType) (const QJsonObject&); typedef std::map LedDeviceRegistry; -/// -/// @brief Creates and destroys LedDevice instances with LedDeviceFactory and moves the device to a thread. Pipes all signal/slots and methods to LedDevice instance -/// class LedDeviceWrapper : public QObject { Q_OBJECT public: - explicit LedDeviceWrapper(HyperHdrInstance* hyperhdr); - ~LedDeviceWrapper() override; - /// - /// @brief Constructs a new LedDevice, moves to thread and starts - /// @param config With the given configuration - /// - void createLedDevice(const QJsonObject& config); + explicit LedDeviceWrapper(HyperHdrInstance* ownerInstance); + virtual ~LedDeviceWrapper(); - /// - /// @brief Get all available device schemas - /// @return device schemas - /// + void createLedDevice(QJsonObject config, int smoothingInterval); static QJsonObject getLedDeviceSchemas(); - - /// - /// @brief add all device constructors to the map - /// static int addToDeviceMap(QString name, LedDeviceCreateFuncType funcPtr); - - /// - /// @brief Return all available device constructors - /// @return device constructors - /// static const LedDeviceRegistry& getDeviceMap(); - - /// - /// @brief Get the current active ledDevice type - /// QString getActiveDeviceType() const; - - /// - /// @brief Return the last enable state - /// bool enabled() const; - - /// - /// @brief Get the number of LEDs from device - /// unsigned int getLedCount() const; - void identifyLed(const QJsonObject& params); + int hasLedClock(); public slots: - /// - /// @brief Handle new component state request - /// @param component The comp from enum - /// @param state The new state - /// void handleComponentState(hyperhdr::Components component, bool state); - -signals: - /// - /// PIPER signal for Hyperhdr -> LedDevice - /// - /// @param[in] ledValues The RGB-color per led - /// - /// @return Zero on success else negative - /// - int updateLeds(std::vector ledValues); - - void stopLedDevice(); - -private slots: - /// - /// @brief Is called whenever the led device switches between on/off. The led device can disable it's component state - /// The signal comes from the LedDevice - /// @param newState The new state of the device - /// void handleInternalEnableState(bool newState); - protected: - /// contains all available led device constructors static LedDeviceRegistry _ledDeviceMap; - static QMutex _ledDeviceMapLock; private: - /// - /// @brief switchOff() the device and Stops the device thread - /// - void stopDeviceThread(); - -private: - // parent Hyperhdr - HyperHdrInstance* _hyperhdr; - // Pointer of current led device - LedDevice* _ledDevice; - // the enable state + HyperHdrInstance* _ownerInstance; + std::unique_ptr _ledDevice; bool _enabled; }; - -#endif // LEDEVICEWRAPPER_H diff --git a/include/mqtt/mqtt.h b/include/mqtt/mqtt.h index b08c7a682..65d0b52e0 100644 --- a/include/mqtt/mqtt.h +++ b/include/mqtt/mqtt.h @@ -1,14 +1,15 @@ #pragma once -// Qt includes -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif #include #include - -// settings #include + #include class mqtt : public QObject @@ -16,12 +17,13 @@ class mqtt : public QObject Q_OBJECT public: - mqtt(QObject* _parent); + mqtt(const QJsonDocument& mqttConfig); ~mqtt(); public slots: - void start(QString host, int port, QString username, QString password, bool is_ssl, bool ignore_ssl_errors); + void begin(); + void start(QString host, int port, QString username, QString password, bool is_ssl, bool ignore_ssl_errors, QString customTopic); void stop(); void handleSettingsUpdate(settings::type type, const QJsonDocument& config); @@ -30,14 +32,31 @@ private slots: void connected(); void error(const QMQTT::ClientError error); void received(const QMQTT::Message& message); - -signals: - void jsonCommander(QString command); + void disconnected(); private: + void executeJson(QString origin, const QJsonDocument& input, QJsonDocument& result); + void initRetry(); + + // HyperHDR MQTT topic & reponse path + QString HYPERHDRAPI; + QString HYPERHDRAPI_RESPONSE; + + QString _customTopic; + + bool _enabled; + QString _host; + int _port; + QString _username; + QString _password; + bool _is_ssl; + bool _ignore_ssl_errors; + int _maxRetry; + int _currentRetry; + QTimer* _retryTimer; + bool _initialized; + QJsonArray _resultArray; - /// Logger instance - int _jsonPort; Logger* _log; - QMQTT::Client* _clientInstance; + QMQTT::Client* _clientInstance; }; diff --git a/include/proto-nano-server/ProtoNanoClientConnection.h b/include/proto-nano-server/ProtoNanoClientConnection.h index 39a82a522..d3e5b80b0 100644 --- a/include/proto-nano-server/ProtoNanoClientConnection.h +++ b/include/proto-nano-server/ProtoNanoClientConnection.h @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -35,90 +35,38 @@ #include #include - class QTcpSocket; class QTimer; - -/// -/// The Connection object created by a ProtoServer when a new connection is established -/// class ProtoNanoClientConnection : 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 ProtoNanoClientConnection(QTcpSocket* socket, int timeout, QObject* parent); signals: - /// - /// @brief forward register data to HyperHDRDaemon - /// - void registerGlobalInput(int priority, hyperhdr::Components component, const QString& origin = "ProtoBuffer", const QString& owner = "", unsigned smooth_cfg = 0); - - /// - /// @brief Forward clear command to HyperHDRDaemon - /// - void clearGlobalInput(int priority, bool forceClearAll = false); - - /// - /// @brief forward prepared image to HyperHDRDaemon - /// - bool setGlobalInputImage(int priority, const Image& image, int timeout_ms, bool clearEffect = false); - - /// - /// @brief Forward requested color - /// - void setGlobalInputColor(int priority, const std::vector& ledColor, int timeout_ms, const QString& origin = "ProtoBuffer", bool clearEffects = true); - - /// - /// @brief Emits whenever the client disconnected - /// - void clientDisconnected(); + void SignalClearGlobalInput(int priority, bool forceClearAll); + void SignalImportFromProto(int priority, int duration, const Image& image, QString clientDescription); + void SignalSetGlobalColor(int priority, const std::vector& ledColor, int timeout_ms, hyperhdr::Components origin, QString clientDescription); + void SignalClientConnectionClosed(ProtoNanoClientConnection* client); public slots: - /// - /// @brief Requests a registration from the client - /// - void registationRequired(int priority) { if (_priority == priority) _priority = -1; }; - - /// - /// @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: void handleImageCommand(const proto_ImageRequest& message, Image& image); - void handleClearCommand(const proto_ClearRequest& message); - void sendMessage(const proto_HyperhdrReply& message); - void sendSuccessReply(); - void sendErrorReply(const std::string& error); - void processData(const uint8_t* buffer, uint32_t messageSize); static bool readImage(pb_istream_t* stream, const pb_field_t* field, void** arg); - static bool writeError(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg); private: @@ -129,5 +77,6 @@ private slots: QTimer* _timeoutTimer; int _timeout; int _priority; + QString _clientDescription; QByteArray _receiveBuffer; }; diff --git a/include/proto-nano-server/ProtoServer.h b/include/proto-nano-server/ProtoServer.h index 1a9d92237..e2f472529 100644 --- a/include/proto-nano-server/ProtoServer.h +++ b/include/proto-nano-server/ProtoServer.h @@ -1,70 +1,47 @@ #pragma once -// util +#ifndef PCH_ENABLED + #include +#endif + #include #include -// qt -#include class QTcpServer; class ProtoNanoClientConnection; class NetOrigin; -/// -/// @brief This class creates a TCP server which accepts connections wich can then send -/// in Protocol Buffer encoded commands. This interface to Hyperhdr is used by various -/// third-party applications -/// class ProtoServer : public QObject { Q_OBJECT public: - ProtoServer(const QJsonDocument& config, QObject* parent = nullptr); + ProtoServer(std::shared_ptr netOrigin, const QJsonDocument& config, QObject* parent = nullptr); ~ProtoServer() override; public slots: - /// - /// @brief Handle settings update - /// @param type The type from enum - /// @param config The configuration - /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - void initServer(); private slots: - /// - /// @brief Is called whenever a new socket wants to connect - /// void newConnection(); - - /// - /// @brief is called whenever a client disconnected - /// - void clientDisconnected(); + void signalClientConnectionClosedHandler(ProtoNanoClientConnection* client); private: - /// - /// @brief Start the server with current _port - /// void startServer(); - - /// - /// @brief Stop server - /// void stopServer(); +signals: + void SignalImportFromProto(int priority, int duration, const Image& image, QString clientDescription); private: QTcpServer* _server; - NetOrigin* _netOrigin; + std::shared_ptr _netOrigin; Logger* _log; int _timeout; quint16 _port; const QJsonDocument _config; - QVector _openConnections; }; diff --git a/include/sound-capture/linux/SoundCaptureLinux.h b/include/sound-capture/linux/SoundCaptureLinux.h new file mode 100644 index 000000000..5e853d184 --- /dev/null +++ b/include/sound-capture/linux/SoundCaptureLinux.h @@ -0,0 +1,39 @@ +#pragma once + +#ifndef PCH_ENABLED + #include +#endif + +#include + +class AlsaWorkerThread : public QThread +{ + Q_OBJECT + + std::atomic _exitNow{ false }; + Logger* _logger; + QString _device; + SoundCapture* _parent; + + void run() override; + + public: + AlsaWorkerThread(Logger* logger, QString device, SoundCapture* parent); + void exitNow(); +}; + +class SoundCaptureLinux : public SoundCapture +{ + Q_OBJECT + + public: + SoundCaptureLinux(const QJsonDocument& effectConfig, QObject* parent = nullptr); + ~SoundCaptureLinux(); + void start() override; + void stop() override; + private: + void listDevices(); + AlsaWorkerThread* _thread; +}; + +typedef SoundCaptureLinux SoundGrabber; diff --git a/include/sound-capture/macos/SoundCaptureMacOS.h b/include/sound-capture/macos/SoundCaptureMacOS.h new file mode 100644 index 000000000..a70d72849 --- /dev/null +++ b/include/sound-capture/macos/SoundCaptureMacOS.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#define SOUNDCAPMACOS_BUF_LENP 10 + +class SoundCaptureMacOS : public SoundCapture +{ + public: + SoundCaptureMacOS(const QJsonDocument& effectConfig, QObject* parent = nullptr); + ~SoundCaptureMacOS(); + + void recordCallback(uint32_t frameBytes, uint8_t* rawAudioData); + void start() override; + void stop() override; + + private: + void listDevices(); + bool getPermission(); + + size_t _soundBufferIndex = 0; + int16_t _soundBuffer[(1< +#endif + +#include + +class WindowsSoundThread : public QThread +{ + Q_OBJECT + + std::atomic _exitNow{ false }; + Logger* _logger; + QString _device; + SoundCapture* _parent; + + void run() override; + +public: + WindowsSoundThread(Logger* logger, QString device, SoundCapture* parent); + void exitNow(); +}; + +class SoundCaptureWindows : public SoundCapture +{ + Q_OBJECT + +public: + SoundCaptureWindows(const QJsonDocument& effectConfig, QObject* parent = nullptr); + ~SoundCaptureWindows(); + void start() override; + void stop() override; +private: + void listDevices(); + WindowsSoundThread* _thread; +}; + +typedef SoundCaptureWindows SoundGrabber; diff --git a/include/ssdp/SSDPDiscover.h b/include/ssdp/SSDPDiscover.h index a3e17b805..192e11dba 100644 --- a/include/ssdp/SSDPDiscover.h +++ b/include/ssdp/SSDPDiscover.h @@ -1,12 +1,13 @@ -#ifndef SSDPDISCOVER_H -#define SSDPDISCOVER_H +#pragma once -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include -#include + #include +#endif class Logger; class QUdpSocket; @@ -207,4 +208,3 @@ private slots: bool _skipDupKeys; }; -#endif // SSDPDISCOVER_H diff --git a/include/ssdp/SSDPHandler.h b/include/ssdp/SSDPHandler.h index 87a50565e..f435fbdf9 100644 --- a/include/ssdp/SSDPHandler.h +++ b/include/ssdp/SSDPHandler.h @@ -1,13 +1,11 @@ #pragma once #include -#include #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #include #endif -// utils #include class WebServer; @@ -23,7 +21,7 @@ class SSDPHandler : public SSDPServer { Q_OBJECT public: - SSDPHandler(WebServer* webserver, quint16 flatBufPort, quint16 protoBufPort, quint16 jsonServerPort, quint16 sslPort, const QString& name, QObject* parent = nullptr); + SSDPHandler(QString uuid, quint16 flatBufPort, quint16 protoBufPort, quint16 jsonServerPort, quint16 sslPort, quint16 webPort, const QString& name, QObject* parent = nullptr); ~SSDPHandler() override; /// @@ -88,31 +86,13 @@ private slots: /// void handleMSearchRequest(const QString& target, const QString& mx, const QString address, quint16 port); - /// - /// @brief Handle changes in the network configuration - /// @param conig New config - /// -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - // yes, we know it depracated and can handle it -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - void handleNetworkConfigurationChanged(const QNetworkConfiguration& config); -#pragma GCC diagnostic pop -#endif +signals: + void newSsdpXmlDesc(QString newSsdpDesc); private: - WebServer* _webserver; - QString _localAddress; - -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - // yes, we know it depracated and can handle it -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - std::unique_ptr _NCA; -#pragma GCC diagnostic pop -#endif - - QString _uuid; - /// Targets for announcement + Logger* _log; + QString _localAddress; + QString _uuid; + int _retry; std::vector _deviceList; }; diff --git a/include/ssdp/SSDPServer.h b/include/ssdp/SSDPServer.h index 7b7b220e0..c89737fc2 100644 --- a/include/ssdp/SSDPServer.h +++ b/include/ssdp/SSDPServer.h @@ -17,7 +17,7 @@ class SSDPServer : public QObject /// @brief Construct the server, listen on default ssdp address/port with multicast /// @param parent The parent object /// - SSDPServer(QObject* parent = nullptr); + SSDPServer(QObject* parent); ~SSDPServer() override; /// @@ -110,6 +110,9 @@ class SSDPServer : public QObject /// quint16 getSSLServerPort() const; + void setWebServerPort(quint16 port); + quint16 getWebServerPort() const; + /// /// @brief set new hyperhdr name /// @@ -137,7 +140,7 @@ class SSDPServer : public QObject Logger* _log; QUdpSocket* _udpSocket; - QString _serverHeader, _uuid, _fbsPort, _pbsPort, _jssPort, _sslPort, _name, _descAddress; + QString _serverHeader, _uuid, _fbsPort, _pbsPort, _jssPort, _sslPort, _webPort, _name, _descAddress; bool _running; private slots: diff --git a/include/utils/ChannelCalibration.h b/include/utils/ChannelCalibration.h new file mode 100644 index 000000000..eef421d01 --- /dev/null +++ b/include/utils/ChannelCalibration.h @@ -0,0 +1,68 @@ +#pragma once + +/* ChannelCalibration.h +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include + #include + + #include + + #include + #include +#endif + +class ChannelCalibration +{ + +public: + ChannelCalibration(quint8 instance, QString channelName, const QJsonObject& colorConfig, int defaultR, int defaultG, int defaultB); + + void apply(uint8_t input, uint8_t brightness, uint8_t& red, uint8_t& green, uint8_t& blue); + + ColorRgb getAdjustment() const; + void setAdjustment(const QJsonArray& value); + + uint8_t adjustmentR(uint8_t inputR) const; + uint8_t adjustmentG(uint8_t inputG) const; + uint8_t adjustmentB(uint8_t inputB) const; + + uint8_t getCorrection() const; + void setCorrection(int correction); + uint8_t correction(uint8_t input) const; + bool isEnabled() const; + QString getChannelName(); + +private: + ColorRgb _targetCalibration; + int _correction; + QString _channelName; + Logger* _log; + bool _enabled; +}; diff --git a/include/utils/ColorRgb.h b/include/utils/ColorRgb.h index bdedd4788..74701e8f3 100644 --- a/include/utils/ColorRgb.h +++ b/include/utils/ColorRgb.h @@ -1,36 +1,24 @@ #pragma once -// STL includes -#include -#include +#ifndef PCH_ENABLED + #include + #include -#include -#include + #include + #include +#endif -/// -/// Plain-Old-Data structure containing the red-green-blue color specification. Size of the -/// structure is exactly 3-bytes for easy writing to led-device -/// struct ColorRgb { - /// The red color channel uint8_t red; - /// The green color channel uint8_t green; - /// The blue color channel uint8_t blue; - /// 'Black' RgbColor (0, 0, 0) static const ColorRgb BLACK; - /// 'Red' RgbColor (255, 0, 0) static const ColorRgb RED; - /// 'Green' RgbColor (0, 255, 0) static const ColorRgb GREEN; - /// 'Blue' RgbColor (0, 0, 255) static const ColorRgb BLUE; - /// 'Yellow' RgbColor (255, 255, 0) static const ColorRgb YELLOW; - /// 'White' RgbColor (255, 255, 255) static const ColorRgb WHITE; ColorRgb() = default; @@ -52,22 +40,19 @@ struct ColorRgb return a; } + inline bool hasColor() const + { + return red || green || blue; + } + QString toQString() { return QString("(%1,%2,%3)").arg(red).arg(green).arg(blue); } }; -/// Assert to ensure that the size of the structure is 'only' 3 bytes static_assert(sizeof(ColorRgb) == 3, "Incorrect size of ColorRgb"); -/// -/// Stream operator to write ColorRgb to an outputstream (format "'{'[red]','[green]','[blue]'}'") -/// -/// @param os The output stream -/// @param color The color to write -/// @return The output stream (with the color written to it) -/// inline std::ostream& operator<<(std::ostream& os, const ColorRgb& color) { os << "{" @@ -79,13 +64,6 @@ inline std::ostream& operator<<(std::ostream& os, const ColorRgb& color) return os; } -/// -/// Stream operator to write ColorRgb to a QTextStream (format "'{'[red]','[green]','[blue]'}'") -/// -/// @param os The output stream -/// @param color The color to write -/// @return The output stream (with the color written to it) -/// inline QTextStream& operator<<(QTextStream& os, const ColorRgb& color) { os << "{" @@ -97,7 +75,6 @@ inline QTextStream& operator<<(QTextStream& os, const ColorRgb& color) return os; } -/// Compare operator to check if a color is 'equal' to another color inline bool operator==(const ColorRgb& lhs, const ColorRgb& rhs) { return lhs.red == rhs.red && @@ -105,7 +82,6 @@ inline bool operator==(const ColorRgb& lhs, const ColorRgb& rhs) lhs.blue == rhs.blue; } -/// Compare operator to check if a color is 'smaller' than another color inline bool operator<(const ColorRgb& lhs, const ColorRgb& rhs) { return lhs.red < rhs.red&& @@ -113,13 +89,11 @@ inline bool operator<(const ColorRgb& lhs, const ColorRgb& rhs) lhs.blue < rhs.blue; } -/// Compare operator to check if a color is 'not equal' to another color inline bool operator!=(const ColorRgb& lhs, const ColorRgb& rhs) { return !(lhs == rhs); } -/// Compare operator to check if a color is 'smaller' than or 'equal' to another color inline bool operator<=(const ColorRgb& lhs, const ColorRgb& rhs) { return lhs.red <= rhs.red && @@ -127,7 +101,6 @@ inline bool operator<=(const ColorRgb& lhs, const ColorRgb& rhs) lhs.blue <= rhs.blue; } -/// Compare operator to check if a color is 'greater' to another color inline bool operator>(const ColorRgb& lhs, const ColorRgb& rhs) { return lhs.red > rhs.red && @@ -135,7 +108,6 @@ inline bool operator>(const ColorRgb& lhs, const ColorRgb& rhs) lhs.blue > rhs.blue; } -/// Compare operator to check if a color is 'greater' than or 'equal' to another color inline bool operator>=(const ColorRgb& lhs, const ColorRgb& rhs) { return lhs.red >= rhs.red && diff --git a/include/utils/ColorRgbw.h b/include/utils/ColorRgbw.h index 4b67ae571..ed14e1629 100644 --- a/include/utils/ColorRgbw.h +++ b/include/utils/ColorRgbw.h @@ -1,50 +1,31 @@ #pragma once -// STL includes -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + + #include + #include + + #include +#endif -/// -/// Plain-Old-Data structure containing the red-green-blue color specification. Size of the -/// structure is exactly 3-bytes for easy writing to led-device -/// struct ColorRgbw { - /// The red color channel uint8_t red; - /// The green color channel uint8_t green; - /// The blue color channel uint8_t blue; - /// The white color channel uint8_t white; - /// 'Black' RgbColor (0, 0, 0, 0) static const ColorRgbw BLACK; - /// 'Red' RgbColor (255, 0, 0, 0) static const ColorRgbw RED; - /// 'Green' RgbColor (0, 255, 0, 0) static const ColorRgbw GREEN; - /// 'Blue' RgbColor (0, 0, 255, 0) static const ColorRgbw BLUE; - /// 'Yellow' RgbColor (255, 255, 0, 0) static const ColorRgbw YELLOW; - /// 'White' RgbColor (0, 0, 0, 255) static const ColorRgbw WHITE; }; -/// Assert to ensure that the size of the structure is 'only' 4 bytes static_assert(sizeof(ColorRgbw) == 4, "Incorrect size of ColorRgbw"); -/// -/// Stream operator to write ColorRgb to an outputstream (format "'{'[red]','[green]','[blue]'}'") -/// -/// @param os The output stream -/// @param color The color to write -/// @return The output stream (with the color written to it) -/// inline std::ostream& operator<<(std::ostream& os, const ColorRgbw& color) { os << "{" @@ -57,7 +38,6 @@ inline std::ostream& operator<<(std::ostream& os, const ColorRgbw& color) return os; } -/// Compare operator to check if a color is 'equal' than another color inline bool operator==(const ColorRgbw& lhs, const ColorRgbw& rhs) { return lhs.red == rhs.red && @@ -66,7 +46,6 @@ inline bool operator==(const ColorRgbw& lhs, const ColorRgbw& rhs) lhs.white == rhs.white; } -/// Compare operator to check if a color is 'smaller' than another color inline bool operator<(const ColorRgbw& lhs, const ColorRgbw& rhs) { return lhs.red < rhs.red&& @@ -75,7 +54,6 @@ inline bool operator<(const ColorRgbw& lhs, const ColorRgbw& rhs) lhs.white < rhs.white; } -/// Compare operator to check if a color is 'smaller' than or 'equal' to another color inline bool operator<=(const ColorRgbw& lhs, const ColorRgbw& rhs) { return lhs < rhs || lhs == rhs; diff --git a/include/utils/ColorSpaceCalibration.h b/include/utils/ColorSpaceCalibration.h new file mode 100644 index 000000000..41d1bf139 --- /dev/null +++ b/include/utils/ColorSpaceCalibration.h @@ -0,0 +1,80 @@ +#pragma once + +#ifndef PCH_ENABLED + #include + #include + + #include + + #include +#endif + +class ColorSpaceCalibration +{ +public: + ColorSpaceCalibration(uint8_t instance, const QJsonObject& colorConfig); + + double getGammaR() const; + double getGammaG() const; + double getGammaB() const; + void setGamma(double gammaR, double gammaG = -1, double gammaB = -1); + void setSaturationGain(double saturationGain); + void setLuminanceGain(double luminanceGain); + double getSaturationGain() const; + double getLuminanceGain() const; + bool getClassicConfig() const; + void setClassicConfig(bool classic_config); + + int getBacklightThreshold() const; + void setBacklightThreshold(int backlightThreshold); + bool getBacklightColored() const; + void setBacklightColored(bool backlightColored); + bool getBackLightEnabled() const; + void setBackLightEnabled(bool enable); + uint8_t getBrightness() const; + void setBrightness(int brightness); + uint8_t getBrightnessCompensation() const; + void setBrightnessCompensation(int brightnessCompensation); + void getBrightnessComponents(uint8_t& rgb, uint8_t& cmy, uint8_t& w) const; + void applyGamma(uint8_t& red, uint8_t& green, uint8_t& blue) const; + void applyBacklight(uint8_t& red, uint8_t& green, uint8_t& blue) const; + void applySaturationLuminance(uint8_t& red, uint8_t& green, uint8_t& blue) const; + + static void rgb2hsl_d(double r, double g, double b, double& hue, double& saturation, double& luminance); + static void hsl2rgb_d(double hue, double saturation, double luminance, double& r, double& g, double& b); + +private: + void init( + bool classic_config, + double saturationGain, + double luminanceGain, + double gammaR, double gammaG, double gammaB, + int backlightThreshold, bool backlightColored, uint8_t brightness, uint8_t brightnessCompensation); + + void initializeMapping(); + + void updateBrightnessComponents(); + + static void rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t& hue, float& saturation, float& luminance); + static void hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t& red, uint8_t& green, uint8_t& blue); + + bool _backLightEnabled, _backlightColored; + int _backlightThreshold; + + uint8_t _sumBrightnessRGBLow; + + double _gammaR, _gammaG, _gammaB; + + uint8_t _mappingR[256], _mappingG[256], _mappingB[256]; + + uint8_t _brightness, _brightnessCompensation, _brightness_rgb, _brightness_cmy, _brightness_w; + + Logger* _log; + +public: + + bool _classic_config; + double _saturationGain; + double _luminanceGain; + double _luminanceMinimum; +}; diff --git a/include/utils/ColorSys.h b/include/utils/ColorSys.h index 85b0c4322..09d3533b9 100644 --- a/include/utils/ColorSys.h +++ b/include/utils/ColorSys.h @@ -1,81 +1,16 @@ #pragma once -// STL includes -#include +#ifndef PCH_ENABLED + #include +#endif -/// -/// Color transformation to adjust the saturation and luminance of a RGB color value -/// class ColorSys { public: - /// - /// Translates an RGB (red, green, blue) color to an HSL (hue, saturation, luminance) color - /// - /// @param[in] red The red RGB-component - /// @param[in] green The green RGB-component - /// @param[in] blue The blue RGB-component - /// @param[out] hue The hue HSL-component - /// @param[out] saturation The saturation HSL-component - /// @param[out] luminance The luminance HSL-component - /// - static void rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t& hue, float& saturation, float& luminance); - - /// - /// Translates an HSL (hue, saturation, luminance) color to an RGB (red, green, blue) color - /// - /// @param[in] hue The hue HSL-component - /// @param[in] saturation The saturation HSL-component - /// @param[in] luminance The luminance HSL-component - /// @param[out] red The red RGB-component - /// @param[out] green The green RGB-component - /// @param[out] blue The blue RGB-component - /// - static void hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t& red, uint8_t& green, uint8_t& blue); - /// - /// Translates an RGB (red, green, blue) color to an HSV (hue, saturation, value) color - /// - /// @param[in] red The red RGB-component - /// @param[in] green The green RGB-component - /// @param[in] blue The blue RGB-component - /// @param[out] hue The hue HSV-component - /// @param[out] saturation The saturation HSV-component - /// @param[out] value The value HSV-component - /// - /// @note Integer version of the conversion are faster, but a little less accurate all values - /// are unsigned 8 bit values and scaled between 0 and 255 except for the hue which is a 16 bit - /// number and scaled between 0 and 360 - /// static void rgb2hsv(uint8_t red, uint8_t green, uint8_t blue, uint16_t& hue, uint8_t& saturation, uint8_t& value); - - /// - /// Translates an HSV (hue, saturation, value) color to an RGB (red, green, blue) color - /// - /// @param[in] hue The hue HSV-component - /// @param[in] saturation The saturation HSV-component - /// @param[in] value The value HSV-component - /// @param[out] red The red RGB-component - /// @param[out] green The green RGB-component - /// @param[out] blue The blue RGB-component - /// - /// @note Integer version of the conversion are faster, but a little less accurate all values - /// are unsigned 8 bit values and scaled between 0 and 255 except for the hue which is a 16 bit - /// number and scaled between 0 and 360 - /// static void hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value, uint8_t& red, uint8_t& green, uint8_t& blue); - - /// - /// Translates a YUV (luminance, chrominance, chrominance) color to an RGB (red, green, blue) color - /// - /// @param[in] y The luminance YUV-component - /// @param[in] u The chrominance YUV-component - /// @param[in] v The chrominance YUV-component - /// @param[out] red The red RGB-component - /// @param[out] green The green RGB-component - /// @param[out] blue The blue RGB-component static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t& r, uint8_t& g, uint8_t& b); - static void rgb2yuv(int r, int g, int b, uint8_t& y, uint8_t& u, uint8_t& v); }; diff --git a/include/utils/Components.h b/include/utils/Components.h index 9c5618111..6605e4bf9 100644 --- a/include/utils/Components.h +++ b/include/utils/Components.h @@ -1,12 +1,11 @@ #pragma once -#include + +#ifndef PCH_ENABLED + #include +#endif namespace hyperhdr { - - /** - * Enumeration of components in HyperHDR. - */ enum Components { COMP_INVALID, @@ -24,7 +23,8 @@ namespace hyperhdr COMP_LEDDEVICE, COMP_FLATBUFSERVER, COMP_RAWUDPSERVER, - COMP_PROTOSERVER + COMP_CEC, + COMP_PROTOSERVER, }; inline const char* componentToString(Components c) @@ -45,6 +45,7 @@ namespace hyperhdr case COMP_LEDDEVICE: return "LED device"; case COMP_FLATBUFSERVER: return "Image Receiver"; case COMP_RAWUDPSERVER: return "Raw RGB UDP Server"; + case COMP_CEC: return "CEC"; case COMP_PROTOSERVER: return "Proto Server"; default: return ""; } @@ -68,6 +69,7 @@ namespace hyperhdr case COMP_LEDDEVICE: return "LEDDEVICE"; case COMP_FLATBUFSERVER: return "FLATBUFSERVER"; case COMP_RAWUDPSERVER: return "RAWUDPSERVER"; + case COMP_CEC: return "CEC"; case COMP_PROTOSERVER: return "PROTOSERVER"; default: return ""; } @@ -90,8 +92,8 @@ namespace hyperhdr if (cmp == "LEDDEVICE") return COMP_LEDDEVICE; if (cmp == "FLATBUFSERVER") return COMP_FLATBUFSERVER; if (cmp == "RAWUDPSERVER") return COMP_RAWUDPSERVER; + if (cmp == "CEC") return COMP_CEC; if (cmp == "PROTOSERVER") return COMP_PROTOSERVER; return COMP_INVALID; } - -} // end of namespace +} diff --git a/include/utils/FileUtils.h b/include/utils/FileUtils.h index d903fd252..72ac0cfde 100644 --- a/include/utils/FileUtils.h +++ b/include/utils/FileUtils.h @@ -1,64 +1,19 @@ #pragma once -// qt includes -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include -// util includes -#include "Logger.h" + #include +#endif namespace FileUtils { QString getBaseName(const QString& sourceFile); - QString getDirName(const QString& sourceFile); - /// - /// @brief remove directory recursive given by path - /// @param[in] path Path to directory - bool removeDir(const QString& path, Logger* log); - - /// - /// @brief check if the file exists - /// @param[in] path The file path to check - /// @param[in] log The logger of the caller to print errors - /// @param[in] ignError Ignore errors during file read (no log output) - /// @return true on success else false - /// bool fileExists(const QString& path, Logger* log, bool ignError = false); - - /// - /// @brief read a file given by path. - /// @param[in] path The file path to read - /// @param[out] data The read data o success - /// @param[in] log The logger of the caller to print errors - /// @param[in] ignError Ignore errors during file read (no log output) - /// @return true on success else false - /// bool readFile(const QString& path, QString& data, Logger* log, bool ignError = false); - - /// - /// write a file given by path. - /// @param[in] path The file path to read - /// @param[in] data The data to write - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool writeFile(const QString& path, const QByteArray& data, Logger* log); - - /// - /// @brief delete a file by given path - /// @param[in] path The file path to delete - /// @param[in] log The logger of the caller to print errors - /// @param[in] ignError Ignore errors during file delete (no log output) - /// @return true on success else false - /// - bool removeFile(const QString& path, Logger* log, bool ignError = false); - - /// - /// @brief resolve the file error and print a message - /// @param[in] file The file which caused the error - /// @param[in] log The logger of the caller - /// void resolveFileError(const QFile& file, Logger* log); } diff --git a/include/utils/FrameDecoder.h b/include/utils/FrameDecoder.h index 9e1f51753..465d5d3a4 100644 --- a/include/utils/FrameDecoder.h +++ b/include/utils/FrameDecoder.h @@ -1,9 +1,12 @@ #pragma once -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif +#include // some stuff for HDR tone mapping #define LUT_INDEX(y,u,v) ((y + (u<<8) + (v<<16))*3) @@ -11,6 +14,7 @@ class FrameDecoder { public: + static void encodeJpeg(MemoryBuffer& buffer, Image& inputImage, bool scaleDown); static void processImage( int _cropLeft, int _cropRight, int _cropTop, int _cropBottom, const uint8_t* data, int width, int height, int lineLength, @@ -40,6 +44,11 @@ class FrameDecoder uint8_t* source, int _actualWidth, int _actualHeight, int division, uint8_t* _lutBuffer, int lineSize = 0); + static void processSystemImagePQ10(Image& image, int targetSizeX, int targetSizeY, + int startX, int startY, + uint8_t* source, int _actualWidth, int _actualHeight, + int division, uint8_t* _lutBuffer, int lineSize = 0); + static void applyLUT(uint8_t* _source, unsigned int width, unsigned int height, const uint8_t* lutBuffer, const int _hdrToneMappingEnabled); }; diff --git a/include/utils/GlobalSignals.h b/include/utils/GlobalSignals.h index da398b4bf..2f62ffaaa 100644 --- a/include/utils/GlobalSignals.h +++ b/include/utils/GlobalSignals.h @@ -1,16 +1,49 @@ +/* GlobalSignals.h +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + #pragma once -// util -#include -#include -#include +#ifndef PCH_ENABLED + #include + + #include + #include + #include +#endif + +#include +#include -// qt -#include +class HyperHdrManager; +class AccessManager; +class SoundCapture; +class GrabberHelper; +class DiscoveryWrapper; -/// -/// Singleton instance for simple signal sharing across threads, should be never used with Qt:DirectConnection! -/// class GlobalSignals : public QObject { Q_OBJECT @@ -29,76 +62,37 @@ class GlobalSignals : public QObject void operator=(GlobalSignals const&) = delete; signals: - /////////////////////////////////////// - ////////////////// TO ///////////////// - /////////////////////////////////////// - - /// - /// @brief PIPE SystemCapture images from GrabberWrapper to HyperHDR class - /// @param name The name of the platform capture that is currently active - /// @param image The prepared image - /// - void setSystemImage(const QString& name, const Image& image); - - /// - /// @brief PIPE video images from video grabber over HyperHDRDaemon to HyperHDR class - /// @param name The name of the grabber capture (path) that is currently active - /// @param image The prepared image - /// - void setVideoImage(const QString& name, const Image& image); - - /// - /// @brief PIPE the register command for a new global input over HyperHDRDaemon to HyperHDR class - /// @param[in] priority The priority of the channel - /// @param[in] component The component of the channel - /// @param[in] origin Who set the channel (CustomString@IP) - /// @param[in] owner Specific owner string, might be empty - /// @param[in] smooth_cfg The smooth id to use - /// - void registerGlobalInput(int priority, hyperhdr::Components component, const QString& origin = "External", const QString& owner = "", unsigned smooth_cfg = 0); - - /// - /// @brief PIPE the clear command for the global priority channel over HyperHDRDaemon to HyperHDR class - /// @param[in] priority The priority channel (-1 to clear all possible priorities) - /// @param[in] forceclearAll Force the clear - /// - void clearGlobalInput(int priority, bool forceClearAll = false); - - /// - /// @brief PIPE external images over HyperHDRDaemon to HyperHDR class - /// @param[in] priority The priority of the channel - /// @param image The prepared image - /// @param[in] timeout_ms The timeout in milliseconds - /// @param clearEffect Should be true when NOT called from an effect - /// - void setGlobalImage(int priority, const Image& image, int timeout_ms, bool clearEffect = true); - - /// - /// @brief PIPE external color message over HyperHDRDaemon to HyperHDR class - /// @param[in] priority The priority of the channel - /// @param image The prepared color - /// @param[in] timeout_ms The timeout in milliseconds - /// @param[in] origin The setter - /// @param clearEffect Should be true when NOT called from an effect - /// - void setGlobalColor(int priority, const std::vector& ledColor, int timeout_ms, const QString& origin = "External", bool clearEffects = true); - - /////////////////////////////////////// - ///////////////// FROM //////////////// - /////////////////////////////////////// - - /// - /// @brief PIPE a registration request from the HyperHDR class to the priority channel - /// @param[in] priority The priority channel - /// - void globalRegRequired(int priority); - - /// - /// @brief Tell v4l2/screen capture the listener state - /// @param component The component to handle - /// @param hyperhdrInd The HyperHDR instance index as identifier - /// @param listen True when listening, else false - /// - void requestSource(hyperhdr::Components component, int hyperHdrInd, bool listen); + void SignalGetInstanceManager(std::shared_ptr& instanceManager); + + void SignalGetAccessManager(std::shared_ptr& accessManager); + + void SignalGetSoundCapture(std::shared_ptr& soundCapture); + + void SignalGetSystemGrabber(std::shared_ptr& systemGrabber); + + void SignalGetVideoGrabber(std::shared_ptr& videoGrabber); + + void SignalGetPerformanceCounters(std::shared_ptr& performanceCounters); + + void SignalGetDiscoveryWrapper(std::shared_ptr& discoveryWrapper); + + void SignalNewSystemImage(const QString& name, const Image& image); + + void SignalNewVideoImage(const QString& name, const Image& image); + + void SignalClearGlobalInput(int priority, bool forceClearAll); + + void SignalSetGlobalImage(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin, QString clientDescription); + + void SignalSetGlobalColor(int priority, const std::vector& ledColor, int timeout_ms, hyperhdr::Components origin, QString clientDescription); + + void SignalRequestComponent(hyperhdr::Components component, int hyperHdrInd, bool listen); + + void SignalPerformanceNewReport(PerformanceReport pr); + + void SignalPerformanceStateChanged(bool enabled, hyperhdr::PerformanceReportType type, int id, QString name = ""); + + void SignalDiscoveryRequestToScan(DiscoveryRecord::Service type); + void SignalDiscoveryEvent(DiscoveryRecord message); }; diff --git a/include/utils/Image.h b/include/utils/Image.h index bcd993342..46e1e7d18 100644 --- a/include/utils/Image.h +++ b/include/utils/Image.h @@ -1,6 +1,9 @@ #pragma once -#include +#ifndef PCH_ENABLED + #include +#endif + #include template @@ -11,13 +14,11 @@ class Image Image(unsigned width, unsigned height); - /// - /// Copy constructor for an image - /// @param other The image which will be copied - /// Image(const Image& other); - Image& operator=(Image other); + Image& operator=(const Image& other); + + Image& operator=(Image&& other) noexcept; static QString adjustCache(); @@ -31,61 +32,24 @@ class Image void gradientVBox(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t r, uint8_t g, uint8_t b); - /// - /// Returns the width of the image - /// - /// @return The width of the image - /// unsigned width() const; - /// - /// Returns the height of the image - /// - /// @return The height of the image - /// unsigned height() const; - /// Returns a reference to a specified pixel in the image - /// - /// @param x The x index - /// @param y The y index const ColorSpace& operator()(unsigned x, unsigned y) const; - /// - /// @return reference to specified pixel - /// ColorSpace& operator()(unsigned x, unsigned y); - /// Resize the image - /// @param width The width of the image - /// @param height The height of the image void resize(unsigned width, unsigned height); - /// - /// Returns a memory pointer to the first pixel in the image - /// @return The memory pointer to the first pixel - /// uint8_t* rawMem(); - /// - /// Returns a const memory pointer to the first pixel in the image - /// @return The const memory pointer to the first pixel - /// const uint8_t* rawMem() const; - /// - /// Get size of buffer - /// size_t size() const; - /// - /// Clear the image - /// void clear(); private: - QExplicitlySharedDataPointer> _d_ptr; + std::shared_ptr> _sharedData; }; - -Q_DECLARE_TYPEINFO(Image, Q_MOVABLE_TYPE); - diff --git a/include/utils/ImageData.h b/include/utils/ImageData.h index 1fb213909..f664861ad 100644 --- a/include/utils/ImageData.h +++ b/include/utils/ImageData.h @@ -1,29 +1,22 @@ #pragma once -// Includes -#include -#include - -// QT includes -#include - -#if defined(_MSC_VER) - #include - typedef SSIZE_T ssize_t; +#ifndef PCH_ENABLED + #include + #include #endif +#include +#include template -class ImageData : public QSharedData +class ImageData { public: ImageData(unsigned width, unsigned height); - ImageData(const ImageData& other); - bool setBufferCacheSize(); - ~ImageData(); + ~ImageData(); unsigned width() const; @@ -58,22 +51,17 @@ class ImageData : public QSharedData private: inline unsigned toIndex(unsigned x, unsigned y) const; - inline uint8_t* getMemory(size_t width, size_t height); + inline void getMemory(size_t width, size_t height); inline void freeMemory(); private: - /// The width of the image unsigned _width; - /// The height of the image unsigned _height; - uint64_t _initData; + std::unique_ptr> _memoryBuffer; - /// The pixels of the image uint8_t* _pixels; - size_t _bufferSize; - static VideoMemoryManager videoCache; }; diff --git a/include/utils/InternalClock.h b/include/utils/InternalClock.h index a2c1ec456..31694755d 100644 --- a/include/utils/InternalClock.h +++ b/include/utils/InternalClock.h @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,8 +26,11 @@ */ #pragma once -#include -#include + +#ifndef PCH_ENABLED + #include + #include +#endif class InternalClock { diff --git a/include/utils/JsonUtils.h b/include/utils/JsonUtils.h index dde8bab1c..d7fa3b2b3 100644 --- a/include/utils/JsonUtils.h +++ b/include/utils/JsonUtils.h @@ -1,95 +1,21 @@ #pragma once -#include +#ifndef PCH_ENABLED + #include + + #include +#endif -#include -#include +#include namespace JsonUtils { - /// - /// @brief read a json file and get the parsed result on success - /// @param[in] path The file path to read - /// @param[out] obj Returns the parsed QJsonObject - /// @param[in] log The logger of the caller to print errors - /// @param[in] ignError Ignore errors during file read (no log output) - /// @return true on success else false - /// bool readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError = false); - - /// - /// @brief read a schema file and resolve $refs - /// @param[in] path The file path to read - /// @param[out] obj Returns the parsed QJsonObject - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool readSchema(const QString& path, QJsonObject& obj, Logger* log); - - /// - /// @brief parse a json QString and get a QJsonObject. Overloaded funtion - /// @param[in] path The file path/name just used for log messages - /// @param[in] data Data to parse - /// @param[out] obj Retuns the parsed QJsonObject - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log); - - /// - /// @brief parse a json QString and get a QJsonArray. Overloaded function - /// @param[in] path The file path/name just used for log messages - /// @param[in] data Data to parse - /// @param[out] arr Retuns the parsed QJsonArray - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log); - - /// - /// @brief parse a json QString and get a QJsonDocument - /// @param[in] path The file path/name just used for log messages - /// @param[in] data Data to parse - /// @param[out] doc Retuns the parsed QJsonDocument - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log); - - /// - /// @brief Validate json data against a schema - /// @param[in] file The path/name of json file just used for log messages - /// @param[in] json The json data - /// @param[in] schemaP The schema path - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool validate(const QString& file, const QJsonObject& json, const QString& schemaPath, Logger* log); - - /// - /// @brief Validate json data against a schema - /// @param[in] file The path/name of json file just used for log messages - /// @param[in] json The json data - /// @param[in] schema The schema object - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool validate(const QString& file, const QJsonObject& json, const QJsonObject& schema, Logger* log); - - /// - /// @brief Write json data to file - /// @param[in] filenameThe file path to write - /// @param[in] json The json data to write - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool write(const QString& filename, const QJsonObject& json, Logger* log); - - /// - /// @brief resolve schema $ref attribute - /// @param[in] schema the schema to iterate - /// @param[out] obj the resolved object - /// @param[in] log The logger of the caller to print errors - /// @return true on success else false - /// bool resolveRefs(const QJsonObject& schema, QJsonObject& obj, Logger* log); } diff --git a/include/utils/Logger.h b/include/utils/Logger.h index d6b4ed757..a6842cbdf 100644 --- a/include/utils/Logger.h +++ b/include/utils/Logger.h @@ -1,41 +1,36 @@ #pragma once +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + + #include + #include +#endif + #include #include -// QT includes -#include -#include -#include -#include -#include -#include - -// stl includes -#include -#include - -#ifdef _WIN32 - #include -#endif #define THREAD_ID QSTRING_CSTR(QString().asprintf("%p", QThread::currentThreadId())) -#define QSTRING_CSTR(str) str.toLocal8Bit().constData() #define LOG_MESSAGE(severity, logger, ...) (logger)->Message(severity, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) -// standard log messages #define Debug(logger, ...) LOG_MESSAGE(Logger::DEBUG , logger, __VA_ARGS__) #define Info(logger, ...) LOG_MESSAGE(Logger::INFO , logger, __VA_ARGS__) #define Warning(logger, ...) LOG_MESSAGE(Logger::WARNING, logger, __VA_ARGS__) #define Error(logger, ...) LOG_MESSAGE(Logger::ERRORR , logger, __VA_ARGS__) -// conditional log messages #define DebugIf(condition, logger, ...) if (condition) Debug(logger, __VA_ARGS__) #define InfoIf(condition, logger, ...) if (condition) Info(logger, __VA_ARGS__) #define WarningIf(condition, logger, ...) if (condition) Warning(logger, __VA_ARGS__) #define ErrorIf(condition, logger, ...) if (condition) Error(logger, __VA_ARGS__) -// ================================================================ +class LoggerManager; class Logger : public QObject { @@ -53,7 +48,6 @@ class Logger : public QObject struct T_LOG_MESSAGE { - QString appName; QString loggerName; QString function; unsigned int line; @@ -69,6 +63,8 @@ class Logger : public QObject utime = 0; level = LogLevel::INFO; } + + T_LOG_MESSAGE(const T_LOG_MESSAGE&) = default; }; static Logger* getInstance(const QString& name = "", LogLevel minLevel = Logger::INFO); @@ -81,9 +77,9 @@ class Logger : public QObject void setMinLevel(LogLevel level); LogLevel getMinLevel() const; QString getName() const; - QString getAppName() const; void disable(); void enable(); + void setInstanceEnable(bool enabled); signals: void newLogMessage(Logger::T_LOG_MESSAGE); @@ -102,11 +98,12 @@ class Logger : public QObject static QString _lastError; static bool _hasConsole; const QString _name; - const QString _appname; const bool _syslogEnabled; const unsigned _loggerId; + bool _instanceEnabled; + + std::shared_ptr _logsManager; - /* Only non-const member, hence the atomic */ QAtomicInteger _minLevel; }; @@ -115,22 +112,24 @@ class LoggerManager : public QObject Q_OBJECT public: - static LoggerManager* getInstance(); - const QList* getLogMessageBuffer() const; + LoggerManager(); + ~LoggerManager(); + + static std::shared_ptr getInstance(); public slots: void handleNewLogMessage(const Logger::T_LOG_MESSAGE&); void handleNewState(bool state); + QJsonArray getLogMessageBuffer(); signals: void newLogMessage(const Logger::T_LOG_MESSAGE&); protected: - LoggerManager(); - QList _logMessageBuffer; - const int _loggerMaxMsgBufferSize; - bool _enable; + QList _logs; + const int _loggerMaxMsgBufferSize; + bool _enable; }; Q_DECLARE_METATYPE(Logger::T_LOG_MESSAGE) diff --git a/include/utils/LutCalibrator.h b/include/utils/LutCalibrator.h index d53cd25e9..43515db56 100644 --- a/include/utils/LutCalibrator.h +++ b/include/utils/LutCalibrator.h @@ -4,7 +4,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -27,18 +27,22 @@ * SOFTWARE. */ -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include -#include + #include -#include -#include + #include + #include + #include +#endif class Logger; +class GrabberWrapper; class LutCalibrator : public QObject { @@ -113,19 +117,16 @@ class LutCalibrator : public QObject public: LutCalibrator(); ~LutCalibrator(); - static LutCalibrator* getInstance(); signals: - void assign(hyperhdr::Components defaultComp, int checksum, ColorRgb startColor, ColorRgb endColor, bool limitedRange, double saturation, double luminance, double gammaR, double gammaG, double gammaB, int coef); - void stop(); - void lutCalibrationUpdate(const QJsonObject& data); + void SignalLutCalibrationUpdated(const QJsonObject& data); public slots: - void assignHandler(hyperhdr::Components defaultComp, int checksum, ColorRgb startColor, ColorRgb endColor, bool limitedRange, double saturation, double luminance, double gammaR, double gammaG, double gammaB, int coef); + void incomingCommand(QString rootpath, GrabberWrapper* grabberWrapper, hyperhdr::Components defaultComp, int checksum, ColorRgb startColor, ColorRgb endColor, bool limitedRange, double saturation, double luminance, double gammaR, double gammaG, double gammaB, int coef); void stopHandler(); void setVideoImage(const QString& name, const Image& image); void setSystemImage(const QString& name, const Image& image); - void setGlobalInputImage(int priority, const Image& image, int timeout_ms, bool clearEffect = true); + void signalSetGlobalImageHandler(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin); private: void handleImage(const Image& image); @@ -133,7 +134,6 @@ public slots: void storeColor(const ColorRgb& inputColor, const ColorRgb& color); bool finalize(bool fastTrack = false); bool correctionEnd(); - bool findNext(int r, int g, int b, int range); inline int clampInt(int val, int min, int max) { return qMin(qMax(val, min), max);} inline double clampDouble(double val, double min, double max) { return qMin(qMax(val, min), max); } @@ -155,7 +155,6 @@ public slots: void displayPreCalibrationInfo(); void displayPostCalibrationInfo(); double fineTune(double& optimalRange, double& optimalScale, int& optimalWhite, int& optimalStrategy); - double calcMax(ColorStat input, bool doBT2020, ColorStat whiteBalance, double floor, double scale, double range); double getError(ColorRgb first, ColorStat second); void applyFilter(); @@ -179,7 +178,8 @@ public slots: ColorRgb _minColor; ColorRgb _maxColor; ColorStat _colorBalance[26]; - uint8_t* _lutBuffer; + MemoryBuffer _lut; + QString _rootPath; static ColorRgb primeColors[]; diff --git a/include/utils/Macros.h b/include/utils/Macros.h index f28f7f234..56c577c2f 100644 --- a/include/utils/Macros.h +++ b/include/utils/Macros.h @@ -1,10 +1,14 @@ #pragma once +#ifndef PCH_ENABLED + #include +#endif + /* Macros.h * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -32,7 +36,7 @@ inline void SAFE_CALL_TEST_FUN() {}; #define SAFE_CALL_0_RET(target, method, returnType, result, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_RETURN_ARG(returnType, result)); \ else \ result = target->method(); \ @@ -41,7 +45,7 @@ inline void SAFE_CALL_TEST_FUN() {}; #define SAFE_CALL_1_RET(target, method, returnType, result, p1type, p1value, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_RETURN_ARG(returnType, result), Q_ARG(p1type, p1value)); \ else \ result = target->method(p1value); \ @@ -50,7 +54,7 @@ inline void SAFE_CALL_TEST_FUN() {}; #define SAFE_CALL_2_RET(target, method, returnType, result, p1type, p1value, p2type, p2value, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_RETURN_ARG(returnType, result), Q_ARG(p1type, p1value), Q_ARG(p2type, p2value)); \ else \ result = target->method(p1value, p2value); \ @@ -59,7 +63,7 @@ inline void SAFE_CALL_TEST_FUN() {}; #define SAFE_CALL_3_RET(target, method, returnType, result, p1type, p1value, p2type, p2value, p3type, p3value, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_RETURN_ARG(returnType, result), Q_ARG(p1type, p1value), Q_ARG(p2type, p2value), Q_ARG(p3type, p3value)); \ else \ result = target->method(p1value, p2value, p3value); \ @@ -68,7 +72,7 @@ inline void SAFE_CALL_TEST_FUN() {}; #define SAFE_CALL_4_RET(target, method, returnType, result, p1type, p1value, p2type, p2value, p3type, p3value, p4type, p4value , ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_RETURN_ARG(returnType, result), Q_ARG(p1type, p1value), Q_ARG(p2type, p2value), Q_ARG(p3type, p3value), Q_ARG(p4type, p4value)); \ else \ result = target->method(p1value, p2value, p3value, p4value); \ @@ -89,7 +93,7 @@ inline void SAFE_CALL_TEST_FUN() {}; if (true) \ QMetaObject::invokeMethod(target, #method, Qt::QueuedConnection, Q_ARG(p1type, p1value)); \ else \ - target->method(p1value); \ + target->method((p1type) p1value); \ } #define QUEUE_CALL_2(target, method, p1type, p1value, p2type, p2value, ...) \ @@ -98,7 +102,7 @@ inline void SAFE_CALL_TEST_FUN() {}; if (true) \ QMetaObject::invokeMethod(target, #method, Qt::QueuedConnection, Q_ARG(p1type, p1value), Q_ARG(p2type, p2value)); \ else \ - target->method(p1value, p2value); \ + target->method((p1type) p1value, (p2type) p2value); \ } #define QUEUE_CALL_3(target, method, p1type, p1value, p2type, p2value, p3type, p3value, ...) \ @@ -107,7 +111,7 @@ inline void SAFE_CALL_TEST_FUN() {}; if (true) \ QMetaObject::invokeMethod(target, #method, Qt::QueuedConnection, Q_ARG(p1type, p1value), Q_ARG(p2type, p2value), Q_ARG(p3type, p3value)); \ else \ - target->method(p1value, p2value, p3value); \ + target->method((p1type) p1value, (p2type) p2value, (p3type) p3value); \ } #define QUEUE_CALL_4(target, method, p1type, p1value, p2type, p2value, p3type, p3value, p4type, p4value , ...) \ @@ -116,13 +120,13 @@ inline void SAFE_CALL_TEST_FUN() {}; if (true) \ QMetaObject::invokeMethod(target, #method, Qt::QueuedConnection, Q_ARG(p1type, p1value), Q_ARG(p2type, p2value), Q_ARG(p3type, p3value), Q_ARG(p4type, p4value)); \ else \ - target->method(p1value, p2value, p3value, p4value); \ + target->method((p1type) p1value, (p2type) p2value, (p3type) p3value, (p4type) p4value); \ } #define BLOCK_CALL_0(target, method, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection); \ else \ target->method(); \ @@ -131,37 +135,46 @@ inline void SAFE_CALL_TEST_FUN() {}; #define BLOCK_CALL_1(target, method, p1type, p1value, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_ARG(p1type, p1value)); \ else \ - target->method(p1value); \ + target->method((p1type) p1value); \ } #define BLOCK_CALL_2(target, method, p1type, p1value, p2type, p2value, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_ARG(p1type, p1value), Q_ARG(p2type, p2value)); \ else \ - target->method(p1value, p2value); \ + target->method((p1type) p1value, (p2type) p2value); \ } #define BLOCK_CALL_3(target, method, p1type, p1value, p2type, p2value, p3type, p3value, ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_ARG(p1type, p1value), Q_ARG(p2type, p2value), Q_ARG(p3type, p3value)); \ else \ - target->method(p1value, p2value, p3value); \ + target->method((p1type) p1value, (p2type) p2value, (p3type) p3value); \ } #define BLOCK_CALL_4(target, method, p1type, p1value, p2type, p2value, p3type, p3value, p4type, p4value , ...) \ { \ SAFE_CALL_TEST_FUN(__VA_ARGS__); \ - if (target->thread() != this->thread()) \ + if (target->thread() != QThread::currentThread()) \ QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_ARG(p1type, p1value), Q_ARG(p2type, p2value), Q_ARG(p3type, p3value), Q_ARG(p4type, p4value)); \ else \ - target->method(p1value, p2value, p3value, p4value); \ + target->method((p1type) p1value, (p2type) p2value, (p3type) p3value, (p4type) p4value); \ +} + +#define BLOCK_CALL_5(target, method, p1type, p1value, p2type, p2value, p3type, p3value, p4type, p4value, p5type, p5value, ...) \ +{ \ + SAFE_CALL_TEST_FUN(__VA_ARGS__); \ + if (target->thread() != QThread::currentThread()) \ + QMetaObject::invokeMethod(target, #method, Qt::BlockingQueuedConnection, Q_ARG(p1type, p1value), Q_ARG(p2type, p2value), Q_ARG(p3type, p3value), Q_ARG(p4type, p4value), Q_ARG(p5type, p5value)); \ + else \ + target->method((p1type) p1value, (p2type) p2value, (p3type) p3value, (p4type) p4value, (p5type) p5value); \ } #define AUTO_CALL_0(target, method, ...) \ @@ -208,3 +221,11 @@ inline void SAFE_CALL_TEST_FUN() {}; else \ target->method(p1value, p2value, p3value, p4value); \ } + +#define QSTRING_CSTR(str) str.toLocal8Bit().constData() + +namespace hyperhdr { + void THREAD_REMOVER(QString message, QThread* parentThread, QObject* client); + void THREAD_MULTI_REMOVER(QString message, QThread* parentThread, std::vector clients); + void SMARTPOINTER_MESSAGE(QString message); +}; diff --git a/include/utils/MemoryBuffer.h b/include/utils/MemoryBuffer.h new file mode 100644 index 000000000..6fadcdb18 --- /dev/null +++ b/include/utils/MemoryBuffer.h @@ -0,0 +1,96 @@ +#pragma once + +/* MemoryBuffer.h +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include +#endif + +template +class MemoryBuffer +{ +public: + MemoryBuffer() : + _buffer(nullptr), + _size(0) + { + } + + MemoryBuffer(size_t size) : + _buffer( (size > 0) ? reinterpret_cast(malloc(size)) : nullptr), + _size(size) + { + } + + MemoryBuffer(const MemoryBuffer& t) = delete; + MemoryBuffer& operator=(const MemoryBuffer& x) = delete; + + ~MemoryBuffer() + { + releaseMemory(); + } + + void releaseMemory() + { + if (_buffer != nullptr) + free(_buffer); + _buffer = nullptr; + _size = 0; + } + + void resize(size_t size) + { + if (size == 0) + { + releaseMemory(); + } + else if (_size != size) + { + auto newBuffer = reinterpret_cast(realloc(_buffer, size)); + _buffer = newBuffer; + _size = size; + } + } + + inline T* data() + { + return _buffer; + } + + inline size_t size() + { + return _size; + } + +private: + T* _buffer; + size_t _size; +}; + +template class MemoryBuffer; diff --git a/include/utils/NetOrigin.h b/include/utils/NetOrigin.h index a532f72f5..4067053b5 100644 --- a/include/utils/NetOrigin.h +++ b/include/utils/NetOrigin.h @@ -1,54 +1,30 @@ #pragma once -// qt -#include -#include +#ifndef PCH_ENABLED + #include + #include -// utils -#include -#include + #include + #include +#endif -/// -/// @brief Checks the origin ip addresses for access allowed -/// class NetOrigin : public QObject { Q_OBJECT -private: - friend class HyperHdrDaemon; - NetOrigin(QObject* parent = nullptr, Logger* log = Logger::getInstance("NETWORK")); public: - /// - /// @brief Check if address is allowed to connect. The local address is the network adapter ip this connection comes from - /// @param address The peer address - /// @param local The local address of the socket (Differs based on NetworkAdapter IP or localhost) - /// @return True when allowed, else false - /// - bool accessAllowed(const QHostAddress& address, const QHostAddress& local) const; - - /// - /// @brief Check if address is in subnet of local - /// @return True or false - /// - bool isLocalAddress(const QHostAddress& address, const QHostAddress& local) const; - - static NetOrigin* getInstance() { return instance; } - static NetOrigin* instance; - + bool accessAllowed(const QHostAddress& address, const QHostAddress& local); + static bool isLocalAddress(const QHostAddress& address, const QHostAddress& local); + private slots: - /// - /// @brief Handle settings update from SettingsManager - /// @param type settingyType from enum - /// @param config configuration object - /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + void settingsChangedHandler(settings::type type, const QJsonDocument& config); private: + friend class HyperHdrDaemon; + NetOrigin(QObject* parent = nullptr, Logger* log = Logger::getInstance("NETWORK")); + Logger* _log; - /// True when internet access is allowed bool _internetAccessAllowed; - /// Whitelisted ip addresses QList _ipWhitelist; - + QMutex _mutex; }; diff --git a/include/utils/PerformanceCounters.h b/include/utils/PerformanceCounters.h index 4a5a920f3..93d74439d 100644 --- a/include/utils/PerformanceCounters.h +++ b/include/utils/PerformanceCounters.h @@ -1,37 +1,44 @@ #pragma once -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include +#endif class Logger; -enum class PerformanceReportType { VIDEO_GRABBER = 1, INSTANCE = 2, LED = 3, CPU_USAGE = 4, RAM_USAGE = 5, CPU_TEMPERATURE = 6, SYSTEM_UNDERVOLTAGE = 7, UNKNOWN = 8 }; +namespace hyperhdr +{ + enum PerformanceReportType { VIDEO_GRABBER = 1, INSTANCE = 2, LED = 3, CPU_USAGE = 4, RAM_USAGE = 5, CPU_TEMPERATURE = 6, SYSTEM_UNDERVOLTAGE = 7, UNKNOWN = 8 }; +} struct PerformanceReport { - int type = (int)PerformanceReportType::UNKNOWN; - int id = -1; + int type; + int id; QString name; - double param1 = 0; - qint64 param2 = 0; - qint64 param3 = 0; - qint64 param4 = 0; - qint64 timeStamp = 0; - qint64 token = 0; + double param1; + qint64 param2; + qint64 param3; + qint64 param4; + qint64 timeStamp; + qint64 token; + + PerformanceReport(); PerformanceReport(int _type, qint64 _token, QString _name, double _param1, qint64 _param2, qint64 _param3, qint64 _param4, int _id = -1); PerformanceReport(const PerformanceReport&) = default; - - PerformanceReport() = default; }; Q_DECLARE_METATYPE(PerformanceReport); +class SystemPerformanceCounters; + class PerformanceCounters : public QObject { Q_OBJECT @@ -40,35 +47,31 @@ class PerformanceCounters : public QObject void consoleReport(int type, int token); void createUpdate(PerformanceReport pr); void deleteUpdate(int type, int id); - void broadcast(); - - static std::unique_ptr _instance; Logger* _log; QList _reports; - SystemPerformanceCounters _system; + std::unique_ptr _system; qint64 _lastRead; qint64 _lastNetworkScan; public: PerformanceCounters(); + ~PerformanceCounters(); - static PerformanceCounters* getInstance(); static qint64 currentToken(); private slots: - void receive(PerformanceReport pr); - void remove(int type, int id); - void request(bool all); - -signals: + void signalPerformanceNewReportHandler(PerformanceReport pr); + void signalPerformanceStateChangedHandler(bool state, hyperhdr::PerformanceReportType type, int id, QString name = ""); + - void newCounter(PerformanceReport pr); - void removeCounter(int type, int id); - void performanceUpdate(QJsonObject report); +public slots: void triggerBroadcast(); void performanceInfoRequest(bool all); + +signals: + void SignalPerformanceStatisticsUpdated(QJsonObject report); }; diff --git a/include/utils/PixelFormat.h b/include/utils/PixelFormat.h index f874fff60..5e808b6c4 100644 --- a/include/utils/PixelFormat.h +++ b/include/utils/PixelFormat.h @@ -1,10 +1,9 @@ #pragma once -#include +#ifndef PCH_ENABLED + #include +#endif -/** - * Enumeration of the possible pixel formats the grabber can be set to - */ enum class PixelFormat { YUYV, RGB24, @@ -17,7 +16,6 @@ enum class PixelFormat { inline PixelFormat parsePixelFormat(const QString& pixelFormat) { - // convert to lower case QString format = pixelFormat.toLower(); if (format.compare("yuyv") == 0) @@ -45,7 +43,6 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat) return PixelFormat::MJPEG; } - // return the default NO_CHANGE return PixelFormat::NO_CHANGE; } @@ -77,6 +74,5 @@ inline QString pixelFormatToString(const PixelFormat& pixelFormat) return "mjpeg"; } - // return the default NO_CHANGE return "NO_CHANGE"; } diff --git a/include/utils/QStringUtils.h b/include/utils/QStringUtils.h index 95c2cad57..16dcaa7d1 100644 --- a/include/utils/QStringUtils.h +++ b/include/utils/QStringUtils.h @@ -1,14 +1,9 @@ -#ifndef QSTRINGUTILS_H -#define QSTRINGUTILS_H +#pragma once -#include -#include -#include - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -#include -#else -#include +#ifndef PCH_ENABLED + #include + #include + #include #endif namespace QStringUtils { @@ -24,24 +19,4 @@ namespace QStringUtils { return string.split(sep, QString::SkipEmptyParts); } #endif - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - inline QList REFSPLITTER(const QString& string, QChar sep) - { - return QStringView{ string }.split(sep, Qt::SplitBehaviorFlags::SkipEmptyParts); - } -#elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - inline QVector REFSPLITTER(const QString& string, QChar sep) - { - return string.splitRef(sep, Qt::SkipEmptyParts); - } -#else - inline QVector REFSPLITTER(const QString& string, QChar sep) - { - return string.splitRef(sep, QString::SkipEmptyParts); - } -#endif - } - -#endif // QSTRINGUTILS_H diff --git a/include/utils/RgbChannelAdjustment.h b/include/utils/RgbChannelAdjustment.h deleted file mode 100644 index 4ce8eda7b..000000000 --- a/include/utils/RgbChannelAdjustment.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -// STL includes -#include -#include -#include -#include - -/// Correction for a single color byte value -/// All configuration values are unsigned int and assume the color value to be between 0 and 255 -class RgbChannelAdjustment -{ -public: - /// Default constructor - RgbChannelAdjustment(QString channelName = ""); - - /// Constructor - /// @param adjustR - /// @param adjustG - /// @param adjustB - RgbChannelAdjustment(quint8 instance, uint8_t adjustR, uint8_t adjustG, uint8_t adjustB, QString channelName, bool enabled); - - /// - /// Transform the given array value - /// - /// @param input The input color bytes - /// @param brightness The current brightness value - /// @param red The red color component - /// @param green The green color component - /// @param blue The blue color component - /// - /// @note The values are updated in place. - /// - void apply(uint8_t input, uint8_t brightness, uint8_t& red, uint8_t& green, uint8_t& blue); - - /// - /// setAdjustment RGB - /// - /// @param adjustR - /// @param adjustG - /// @param adjustB - /// - void setAdjustment(uint8_t adjustR, uint8_t adjustG, uint8_t adjustB); - - /// @return The current adjustR value - uint8_t getAdjustmentR() const; - - /// @return The current adjustG value - uint8_t getAdjustmentG() const; - - /// @return The current adjustB value - uint8_t getAdjustmentB() const; - - uint8_t adjustmentR(uint8_t inputR) const; - - uint8_t adjustmentG(uint8_t inputG) const; - - uint8_t adjustmentB(uint8_t inputB) const; - - void setCorrection(uint8_t correction); - - uint8_t getCorrection() const; - - uint8_t correction(uint8_t input) const; - - bool isEnabled(); - - static RgbChannelAdjustment createRgbChannelAdjustment(quint8 instance, const QJsonObject& colorConfig, const QString& channelName, int defaultR, int defaultG, int defaultB); - -private: - /// color channels - enum ColorChannel { RED = 0, GREEN = 1, BLUE = 2 }; - - /// reset init of color mapping - void resetInitialized(); - - /// (re)-initilize the color mapping - void initializeAdjustMapping(uint8_t _adjustR, uint8_t _adjustG, uint8_t _adjustB); - void initializeCorrectionMapping(uint8_t correction); - - int _mappingAdjR[256]; - int _mappingAdjG[256]; - int _mappingAdjB[256]; - - /// The correction of R channel - int _correction; - - /// The mapping from input color to output color - int _mappingCorection[256]; - - - /// The adjustment of RGB channel - uint8_t _adjust[3]; - - /// The mapping from input color to output color - uint8_t _mapping[3][256]; - - /// Name of this channel, usefull for debug messages - QString _channelName; - - /// Logger instance - Logger* _log; - - /// bitfield to determine white value is alreade initialized - bool _initialized[256]; - - /// current brightness value - uint8_t _brightness; - - bool _enabled; -}; diff --git a/include/utils/RgbTransform.h b/include/utils/RgbTransform.h deleted file mode 100644 index 992aa5d55..000000000 --- a/include/utils/RgbTransform.h +++ /dev/null @@ -1,168 +0,0 @@ -#pragma once - -// STL includes -#include -#include -#include -#include - -/// -/// Color transformation to adjust the saturation and value of a RGB color value -/// -class RgbTransform -{ -public: - /// - /// Default constructor - /// - RgbTransform(); - - /// - /// Constructor - /// - /// @param gammaR The used red gamma - /// @param gammaG The used green gamma - /// @param gammab The used blue gamma - /// @param backlightThreshold The used lower brightness - /// @param backlightColored use color in backlight - /// @param brightnessHigh The used higher brightness - /// - RgbTransform( - quint8 instance, - bool classic_config, - double saturationGain, - double luminanceGain, - double gammaR, double gammaG, double gammaB, - double backlightThreshold, bool backlightColored, - uint8_t brightness, uint8_t brightnessCompensation); - - /// @return The current red gamma value - double getGammaR() const; - - /// @return The current green gamma value - double getGammaG() const; - - /// @return The current blue gamma value - double getGammaB() const; - - /// @param gammaR New red gamma value - /// @param gammaG New green gamma value - /// @param gammaB New blue gamma value - void setGamma(double gammaR, double gammaG = -1, double gammaB = -1); - - void setSaturationGain(double saturationGain); - void setLuminanceGain(double luminanceGain); - double getSaturationGain() const; - double getLuminanceGain() const; - bool getClassicConfig() const; - void setClassicConfig(bool classic_config); - - /// @return The current lower brightness - int getBacklightThreshold() const; - - /// @param backlightThreshold New lower brightness - void setBacklightThreshold(int backlightThreshold); - - /// @return The current state - bool getBacklightColored() const; - - /// @param backlightColored en/disable colored backlight - void setBacklightColored(bool backlightColored); - - /// @return return state of backlight - bool getBackLightEnabled() const; - - /// @param enable en/disable backlight - void setBackLightEnabled(bool enable); - - /// @return The current brightness - uint8_t getBrightness() const; - - /// @param brightness New brightness - void setBrightness(uint8_t brightness); - - /// @return The current brightnessCompensation value - uint8_t getBrightnessCompensation() const; - - /// @param brightnessCompensation new brightnessCompensation - void setBrightnessCompensation(uint8_t brightnessCompensation); - - /// - /// get component values of brightness for compensated brightness - /// - /// @param rgb the rgb component - /// @param cmy the cyan magenta yellow component - /// @param w the white component - /// - /// @note The values are updated in place. - /// - void getBrightnessComponents(uint8_t& rgb, uint8_t& cmy, uint8_t& w) const; - - /// - /// Apply the transform the the given RGB values. - /// - /// @param red The red color component - /// @param green The green color component - /// @param blue The blue color component - /// - /// @note The values are updated in place. - /// - void transform(uint8_t& red, uint8_t& green, uint8_t& blue); - void transformSatLum(uint8_t& red, uint8_t& green, uint8_t& blue); - static RgbTransform createRgbTransform(quint8 instance, const QJsonObject& colorConfig); - static void rgb2hsl_d(double r, double g, double b, double& hue, double& saturation, double& luminance); - static void hsl2rgb_d(double hue, double saturation, double luminance, double& r, double& g, double& b); -private: - /// - /// init - /// - /// @param gammaR The used red gamma - /// @param gammaG The used green gamma - /// @param gammab The used blue gamma - /// @param backlightThreshold The used lower brightness - /// @param backlightColored en/disable color in backlight - /// @param brightness The used brightness - /// @param brightnessCompensation The used brightness compensation - /// - void init( - bool classic_config, - double saturationGain, - double luminanceGain, - double gammaR, double gammaG, double gammaB, double backlightThreshold, bool backlightColored, uint8_t brightness, uint8_t brightnessCompensation, bool _silent = false); - - /// (re)-initilize the color mapping - void initializeMapping(); /// The saturation gain - - void updateBrightnessComponents(); - - void rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t& hue, float& saturation, float& luminance); - void hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t& red, uint8_t& green, uint8_t& blue); - - /// backlight variables - bool _backLightEnabled, _backlightColored; - double _backlightThreshold; - - uint8_t _sumBrightnessRGBLow; - - /// gamma variables - double _gammaR, _gammaG, _gammaB; - - /// The mapping from input color to output color - uint8_t _mappingR[256], _mappingG[256], _mappingB[256]; - - /// brightness variables - uint8_t _brightness - , _brightnessCompensation - , _brightness_rgb - , _brightness_cmy - , _brightness_w; - - /// Logger instance - Logger* _log; - -public: - bool _classic_config; - double _saturationGain; - double _luminanceGain; - double _luminanceMinimum; -}; diff --git a/include/utils/SysInfo.h b/include/utils/SysInfo.h deleted file mode 100644 index 68d4d1fef..000000000 --- a/include/utils/SysInfo.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include -#include - -class SysInfo : public QObject -{ - Q_OBJECT - -public: - struct HyperhdrSysInfo - { - QString kernelType; - QString kernelVersion; - QString architecture; - QString cpuModelName; - QString cpuModelType; - QString cpuRevision; - QString cpuHardware; - QString wordSize; - QString productType; - QString productVersion; - QString prettyName; - QString hostName; - QString domainName; - QString qtVersion; - QString pyVersion; - }; - - static HyperhdrSysInfo get(); - -private: - SysInfo(); - void getCPUInfo(); - - static std::unique_ptr _instance; - - HyperhdrSysInfo _sysinfo; -}; diff --git a/include/utils/SystemPerformanceCounters.h b/include/utils/SystemPerformanceCounters.h index fb30cacd7..d9b3d4649 100644 --- a/include/utils/SystemPerformanceCounters.h +++ b/include/utils/SystemPerformanceCounters.h @@ -1,12 +1,16 @@ #pragma once -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include +#endif -#include #ifdef __linux__ #define MAXPERFCPU 16 @@ -17,15 +21,7 @@ class SystemPerformanceCounters { private: bool isInitialized = false; - void init(); - QString getChar(double val) - { - auto scale = qMax(qMin(qRound(val * 20), 20), 1); - - QString color = (val <= 0.5) ? "cpu_low_usage" : ((val <= 0.8) ? "cpu_medium_usage" : "cpu_high_usage"); //"ForestGreen" : ((val <= 0.8) ? "orange" : "red"); - QString box = QString("").arg(20 - scale).arg(scale); - return QString("%2").arg(color).arg(box); - }; + void init(); #ifdef __linux__ int underVoltage = -1; diff --git a/include/utils/VideoMemoryManager.h b/include/utils/VideoMemoryManager.h index 9feaf0fb0..bbaab5fc6 100644 --- a/include/utils/VideoMemoryManager.h +++ b/include/utils/VideoMemoryManager.h @@ -1,11 +1,17 @@ #pragma once -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include + #include + #include + + #include +#endif #define VideoMemoryManagerBufferSize 8 @@ -16,23 +22,19 @@ class VideoMemoryManager ~VideoMemoryManager(); bool setFrameSize(size_t size); - void release(size_t size, uint8_t* buffer); + void release(std::unique_ptr>& frame); QString adjustCache(); - uint8_t* request(size_t size); - - static void enableCache(bool frameCache); + std::unique_ptr> request(size_t size); private: void releaseBuffer(); - QStack _stack; + std::list>> _stack; size_t _currentSize; - int _prevSize; + size_t _prevSize; int _hits; bool _needed; bool _prevNeeded; - int _bufferLimit; + size_t _bufferLimit; QMutex _locker; - - static bool _enabled, _dirty; }; diff --git a/include/utils/jsonschema/QJsonSchemaChecker.h b/include/utils/jsonschema/QJsonSchemaChecker.h index c7cc42383..afc1ba6ba 100644 --- a/include/utils/jsonschema/QJsonSchemaChecker.h +++ b/include/utils/jsonschema/QJsonSchemaChecker.h @@ -1,11 +1,13 @@ #pragma once -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include +#endif /// JsonSchemaChecker is a very basic implementation of json schema. /// The json schema definition draft can be found at diff --git a/include/utils/jsonschema/QJsonUtils.h b/include/utils/jsonschema/QJsonUtils.h index 9e8a18315..076e44466 100644 --- a/include/utils/jsonschema/QJsonUtils.h +++ b/include/utils/jsonschema/QJsonUtils.h @@ -1,29 +1,29 @@ #pragma once -#include - -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include +#endif -#include -#include -#include -#include -#include -#include -#include -#include +#include class QJsonUtils { public: static int load(const QString& schema, const QString& config, QJsonObject& json) { - // Load the schema and the config trees QJsonObject schemaTree = readSchema(schema); QJsonObject configTree = readConfig(config); - // create the validator QJsonSchemaChecker schemaChecker; schemaChecker.setSchema(schemaTree); @@ -200,24 +200,25 @@ class QJsonUtils return createValue(schema, ignoreRequired); } - static QString getDefaultValue(const QJsonValue& value) + static QString outputNode(const QJsonValue& value) { - QString ret; switch (value.type()) { case QJsonValue::Array: { + QString ret = "["; for (const QJsonValueRef v : value.toArray()) { - ret = getDefaultValue(v); - if (!ret.isEmpty()) - break; + ret += ((ret.length() > 1) ? ", ": "") + outputNode(v); } + return ret + "]"; break; } case QJsonValue::Object: - ret = getDefaultValue(value.toObject().find("default").value()); + { + return QJsonDocument(value.toObject()).toJson(QJsonDocument::Compact); break; + } case QJsonValue::Bool: return value.toBool() ? "True" : "False"; case QJsonValue::Double: @@ -228,7 +229,7 @@ class QJsonUtils case QJsonValue::Undefined: break; } - return ret; + return "???"; } private: @@ -270,38 +271,38 @@ class QJsonUtils for (QJsonObject::const_iterator i = obj.begin(); i != obj.end(); ++i) { QString attribute = i.key(); - const QJsonValue& attributeValue = *i; + const QJsonObject& attributeValue = i.value().toObject(); QJsonValue subValue = obj[attribute]; - if (attributeValue.toObject().find("type") != attributeValue.toObject().end()) + if (attributeValue.find("type") != attributeValue.end()) { - if (attributeValue.toObject().find("type").value().toString() == "object" && (attributeValue.toObject().find("required").value().toBool() || ignoreRequired)) + if (attributeValue.find("type").value().toString() == "object" && (attributeValue.find("required").value().toBool() || ignoreRequired)) { if (obj.contains("properties")) result[attribute] = createValue(obj["properties"], ignoreRequired); else result[attribute] = createValue(subValue, ignoreRequired); } - else if (attributeValue.toObject().find("type").value().toString() == "array" && (attributeValue.toObject().find("required").value().toBool() || ignoreRequired)) + else if (attributeValue.find("type").value().toString() == "array" && (attributeValue.find("required").value().toBool() || ignoreRequired)) { QJsonArray array; - if (attributeValue.toObject().find("default") != attributeValue.toObject().end()) - result[attribute] = attributeValue.toObject().find("default").value(); + if (attributeValue.find("default") != attributeValue.end()) + result[attribute] = attributeValue.find("default").value(); else { QJsonValue retEmpty; - retEmpty = createValue(attributeValue.toObject()["items"], ignoreRequired); + retEmpty = createValue(attributeValue["items"], ignoreRequired); if (!retEmpty.toObject().isEmpty()) array.append(retEmpty); result[attribute] = array; } } - else if (attributeValue.toObject().find("required").value().toBool() || ignoreRequired) + else if ((attributeValue.find("required") != attributeValue.end() && attributeValue.find("required").value().toBool()) || ignoreRequired) { - if (attributeValue.toObject().find("default") != attributeValue.toObject().end()) - result[attribute] = attributeValue.toObject().find("default").value(); + if (attributeValue.find("default") != attributeValue.end()) + result[attribute] = attributeValue.find("default").value(); else result[attribute] = QJsonValue::Null; } diff --git a/include/utils/settings.h b/include/utils/settings.h index 51470b5fe..b30a53b43 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -1,12 +1,12 @@ #pragma once -#include -#include -/// -/// @brief Provide util methods to work with SettingsManager class -/// +#ifndef PCH_ENABLED + #include + #include +#endif + namespace settings { - // all available settings sections + enum class type { SNDEFFECT = 1, BGEFFECT, @@ -37,11 +37,6 @@ namespace settings { INVALID }; - /// - /// @brief Convert settings::type to string representation - /// @param type The settings::type from enum - /// @return The settings type as string - /// inline QString typeToString(type type) { switch (type) @@ -76,11 +71,6 @@ namespace settings { } } - /// - /// @brief Convert string to settings::type representation - /// @param type The string to convert - /// @return The settings type from enum - /// inline type stringToType(const QString& type) { if (type == "soundEffect") return type::SNDEFFECT; diff --git a/include/webserver/WebServer.h b/include/webserver/WebServer.h index 8e71e56f3..ae12e0455 100644 --- a/include/webserver/WebServer.h +++ b/include/webserver/WebServer.h @@ -1,70 +1,44 @@ -#ifndef WEBSERVER_H -#define WEBSERVER_H +#pragma once -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include -// hyperhdr / utils -#include - -// settings -#include + #include + #include +#endif class BonjourServiceRegister; class StaticFileServing; class QtHttpServer; +class HyperHdrManager; +class NetOrigin; class WebServer : public QObject { Q_OBJECT public: - WebServer(const QJsonDocument& config, bool useSsl, QObject* parent = nullptr); - + WebServer(std::shared_ptr netOrigin, const QJsonDocument& config, bool useSsl, QObject* parent = nullptr); ~WebServer() override; void start(); void stop(); - static bool portAvailable(quint16& port, Logger* log); + static bool portAvailable(quint16& port, Logger* log); signals: - /// - /// @emits whenever server is started or stopped (to sync with SSDPHandler) - /// @param newState True when started, false when stopped - /// void stateChange(bool newState); - - /// - /// @brief Emits whenever the port changes (doesn't compare prev <> now) - /// void portChanged(quint16 port); public slots: - /// - /// @brief Init server after thread start - /// void initServer(); - void onServerStopped(); void onServerStarted(quint16 port); void onServerError(QString msg); - - /// - /// @brief Handle settings update from HyperHDR Settingsmanager emit or this constructor - /// @param type settingyType from enum - /// @param config configuration object - /// void handleSettingsUpdate(settings::type type, QJsonDocument config); - - /// - /// @brief Set a new description, if empty the description is NotFound for clients - /// - void setSSDPDescription(const QString& desc); - - /// check if server has been inited - bool isInited() const { return _inited; } + void setSsdpXmlDesc(const QString& desc); quint16 getPort() const { return _port; } @@ -76,7 +50,7 @@ public slots: QString _baseUrl; StaticFileServing* _staticFileServing; QtHttpServer* _server; - bool _inited = false; + std::shared_ptr _netOrigin; const QString WEBSERVER_DEFAULT_PATH = ":/www"; const QString WEBSERVER_DEFAULT_CRT_PATH = ":/hyperhdrcrt.pem"; @@ -85,5 +59,3 @@ public slots: BonjourServiceRegister* _serviceRegister = nullptr; }; - -#endif // WEBSERVER_H diff --git a/sources/CMakeLists.txt b/sources/CMakeLists.txt index 5d8bb7b8f..d2c7790e1 100644 --- a/sources/CMakeLists.txt +++ b/sources/CMakeLists.txt @@ -3,32 +3,35 @@ if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) add_library(precompiled_hyperhdr_headers ${CMAKE_CURRENT_BINARY_DIR}/precompiled_hyperhdr_headers.cpp) target_link_libraries(precompiled_hyperhdr_headers Qt${Qt_VERSION}::Gui Qt${Qt_VERSION}::Network) - target_precompile_headers(precompiled_hyperhdr_headers PRIVATE ${CMAKE_SOURCE_DIR}/include/base/AllHeaders_pch.h) + target_precompile_headers(precompiled_hyperhdr_headers PRIVATE ${CMAKE_SOURCE_DIR}/include/base/AllHeaders_pch.h ${CMAKE_SOURCE_DIR}/include/base/AllHdrHeaders_pch.h ) endif() # Define the current source locations SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources) +add_subdirectory(api) add_subdirectory(base) -add_subdirectory(commandline) add_subdirectory(blackborder) -add_subdirectory(jsonserver) +add_subdirectory(commandline) +add_subdirectory(db) +add_subdirectory(effectengine) add_subdirectory(flatbufserver) +add_subdirectory(grabber) +add_subdirectory(jsonserver) +add_subdirectory(leddevice) +add_subdirectory(sound-capture) +add_subdirectory(ssdp) +add_subdirectory(utils) +add_subdirectory(webserver) + if (ENABLE_PROTOBUF) add_subdirectory(proto-nano-server) endif() -add_subdirectory(ssdp) + if(ENABLE_BOBLIGHT) add_subdirectory(boblightserver) endif() -add_subdirectory(leddevice) -add_subdirectory(utils) -add_subdirectory(effectengine) -add_subdirectory(grabber) -add_subdirectory(webserver) -add_subdirectory(db) -add_subdirectory(api) if (ENABLE_CEC) add_subdirectory(cec) diff --git a/sources/api/API.cpp b/sources/api/API.cpp deleted file mode 100644 index 084f91645..000000000 --- a/sources/api/API.cpp +++ /dev/null @@ -1,667 +0,0 @@ -// project includes -#include -#include -#include - - -#include -#include - -// stl includes -#include -#include - -// Qt includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// bonjour wrapper -#include - -// ledmapping int <> string transform methods -#include - -// api includes -#include - -#ifdef ENABLE_XZ - // decoder - #include -#endif - -using namespace hyperhdr; - -API::API(Logger* log, bool localConnection, QObject* parent) - : QObject(parent) -{ - qRegisterMetaType("int64_t"); - qRegisterMetaType>("std::map"); - - // Init - _log = log; - _authManager = AuthManager::getInstance(); - _instanceManager = HyperHdrIManager::getInstance(); - _localConnection = localConnection; - - _authorized = false; - _adminAuthorized = false; - - _hyperhdr = _instanceManager->getHyperHdrInstance(0); - _currInstanceIndex = 0; - // TODO FIXME - // report back current registers when a Hyperhdr instance request it - //connect(ApiSync::getInstance(), &ApiSync::requestActiveRegister, this, &API::requestActiveRegister, Qt::QueuedConnection); - - // connect to possible token responses that has been requested - connect(_authManager, &AuthManager::tokenResponse, [=](bool success, QObject* caller, const QString& token, const QString& comment, const QString& id, const int& tan) - { - if (this == caller) - emit onTokenResponse(success, token, comment, id, tan); - }); - - // connect to possible startInstance responses that has been requested - connect(_instanceManager, &HyperHdrIManager::startInstanceResponse, [=](QObject* caller, const int& tan) - { - if (this == caller) - emit onStartInstanceResponse(tan); - }); -} - -void API::init() -{ - bool apiAuthRequired = _authManager->isAuthRequired(); - - // For security we block external connections if default PW is set - if (!_localConnection && API::hasHyperhdrDefaultPw()) - { - emit forceClose(); - } - // if this is localConnection and network allows unauth locals, set authorized flag - if (apiAuthRequired && _localConnection) - _authorized = !_authManager->isLocalAuthRequired(); - - // admin access is allowed, when the connection is local and the option for local admin isn't set. Con: All local connections get full access - if (_localConnection) - { - _adminAuthorized = !_authManager->isLocalAdminAuthRequired(); - // just in positive direction - if (_adminAuthorized) - _authorized = true; - } -} - -bool API::setHyperhdrInstance(quint8 inst) -{ - bool isRunning; - - if (_currInstanceIndex == inst) - return true; - - SAFE_CALL_1_RET(_instanceManager, IsInstanceRunning, bool, isRunning, quint8, inst); - if (!isRunning) - return false; - - SAFE_CALL_1_RET(_instanceManager, IsInstanceRunning, bool, isRunning, quint8, _currInstanceIndex); - if (isRunning) - disconnect(_hyperhdr, 0, this, 0); - - SAFE_CALL_1_RET(_instanceManager, getHyperHdrInstance, HyperHdrInstance*, _hyperhdr, quint8, inst); - _currInstanceIndex = inst; - - return true; -} - -bool API::startInstance(quint8 index, int tan) -{ - bool res; - - SAFE_CALL_4_RET(_instanceManager, startInstance, bool, res, quint8, index, bool, false, QObject*, this, int, tan); - - return res; -} - -void API::stopInstance(quint8 index) -{ - emit _instanceManager->instanceStateChanged(InstanceState::H_PRE_DELETE, index); - QUEUE_CALL_1(_instanceManager, stopInstance, quint8, index); -} - -bool API::deleteInstance(quint8 index, QString& replyMsg) -{ - if (_adminAuthorized) - { - emit _instanceManager->instanceStateChanged(InstanceState::H_PRE_DELETE, index); - QUEUE_CALL_1(_instanceManager, deleteInstance, quint8, index); - return true; - } - replyMsg = NO_AUTH; - return false; -} - -QString API::createInstance(const QString& name) -{ - if (_adminAuthorized) - { - bool success = false; - - SAFE_CALL_1_RET(_instanceManager, createInstance, bool, success, QString, name); - - if (!success) - return QString("Instance name '%1' is already in use").arg(name); - - return ""; - } - return NO_AUTH; -} - -QString API::setInstanceName(quint8 index, const QString& name) -{ - if (_adminAuthorized) - { - QUEUE_CALL_2(_instanceManager, saveName, quint8, index, QString, name); - return ""; - } - return NO_AUTH; -} - -void API::setColor(int priority, const std::vector& ledColors, int timeout_ms, const QString& origin, hyperhdr::Components callerComp) -{ - std::vector fledColors; - if (ledColors.size() % 3 == 0) - { - for (uint64_t i = 0; i < ledColors.size(); i += 3) - { - fledColors.emplace_back(ColorRgb{ ledColors[i], ledColors[i + 1], ledColors[i + 2] }); - } - QUEUE_CALL_4(_hyperhdr, setColor, int, priority, std::vector, fledColors, int, timeout_ms, QString, origin); - } -} - -bool API::setImage(ImageCmdData& data, hyperhdr::Components comp, QString& replyMsg, hyperhdr::Components callerComp) -{ - // truncate name length - data.imgName.truncate(16); - - if (data.format == "auto") - { - QImage img = QImage::fromData(data.data); - if (img.isNull()) - { - replyMsg = "Failed to parse picture, the file might be corrupted"; - return false; - } - - // check for requested scale - if (data.scale > 24) - { - if (img.height() > data.scale) - { - img = img.scaledToHeight(data.scale); - } - if (img.width() > data.scale) - { - img = img.scaledToWidth(data.scale); - } - } - - // check if we need to force a scale - if (img.width() > 2000 || img.height() > 2000) - { - data.scale = 2000; - if (img.height() > data.scale) - { - img = img.scaledToHeight(data.scale); - } - if (img.width() > data.scale) - { - img = img.scaledToWidth(data.scale); - } - } - - data.width = img.width(); - data.height = img.height(); - - // extract image - img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); - data.data.clear(); - data.data.reserve(img.width() * img.height() * 3); - for (int i = 0; i < img.height(); ++i) - { - const QRgb* scanline = reinterpret_cast(img.scanLine(i)); - for (int j = 0; j < img.width(); ++j) - { - data.data.append((char)qRed(scanline[j])); - data.data.append((char)qGreen(scanline[j])); - data.data.append((char)qBlue(scanline[j])); - } - } - } - else - { - // check consistency of the size of the received data - if (data.data.size() != data.width * data.height * 3) - { - replyMsg = "Size of image data does not match with the width and height"; - return false; - } - } - - // copy image - Image image(data.width, data.height); - memcpy(image.rawMem(), data.data.data(), data.data.size()); - - - QUEUE_CALL_4(_hyperhdr, registerInput, int, data.priority, hyperhdr::Components, comp, QString, data.origin, QString, data.imgName); - QUEUE_CALL_3(_hyperhdr, setInputImage, int, data.priority, Image, image, int64_t, data.duration); - - return true; -} - -bool API::clearPriority(int priority, QString& replyMsg, hyperhdr::Components callerComp) -{ - if (priority < 0 || (priority > 0 && priority < PriorityMuxer::LOWEST_EFFECT_PRIORITY)) - { - QUEUE_CALL_1(_hyperhdr, clear, int, priority); - } - else - { - replyMsg = QString("Priority %1 is not allowed to be cleared").arg(priority); - return false; - } - return true; -} - -bool API::setComponentState(const QString& comp, bool& compState, QString& replyMsg, hyperhdr::Components callerComp) -{ - QString input(comp); - if (input == "GRABBER") - input = "SYSTEMGRABBER"; - if (input == "V4L") - input = "VIDEOGRABBER"; - Components component = stringToComponent(input); - if (component == COMP_ALL) - { - QUEUE_CALL_1(HyperHdrIManager::getInstance(), toggleStateAllInstances, bool, compState); - - return true; - } - else if (component == COMP_HDR) - { - setVideoModeHdr((compState) ? 1 : 0, component); - return true; - } - else if (component != COMP_INVALID) - { - QUEUE_CALL_2(_hyperhdr, compStateChangeRequest, hyperhdr::Components, component, bool, compState); - return true; - } - replyMsg = QString("Unknown component name: %1").arg(comp); - return false; -} - -void API::setLedMappingType(int type, hyperhdr::Components callerComp) -{ - QUEUE_CALL_1(_hyperhdr, setLedMappingType, int, type); -} - -void API::setVideoModeHdr(int hdr, hyperhdr::Components callerComp) -{ - if (GrabberWrapper::getInstance() != nullptr) - QUEUE_CALL_1(GrabberWrapper::getInstance(), setHdrToneMappingEnabled, int, hdr); - if (FlatBufferServer::getInstance() != nullptr) - QUEUE_CALL_1(FlatBufferServer::getInstance(), setHdrToneMappingEnabled, int, hdr); -} - -void API::setFlatbufferUserLUT(QString userLUTfile) -{ - if (FlatBufferServer::getInstance() != nullptr) - QUEUE_CALL_1(FlatBufferServer::getInstance(), setUserLut, QString, userLUTfile); -} - -bool API::setEffect(const EffectCmdData& dat, hyperhdr::Components callerComp) -{ - int res = -1; - - if (dat.args.isEmpty()) - { - SAFE_CALL_4_RET(_hyperhdr, setEffect, int, res, QString, dat.effectName, int, dat.priority, int, dat.duration, QString, dat.origin); - } - - return res >= 0; -} - -void API::setSourceAutoSelect(bool state, hyperhdr::Components callerComp) -{ - QUEUE_CALL_1(_hyperhdr, setSourceAutoSelect, bool, state); -} - -void API::setVisiblePriority(int priority, hyperhdr::Components callerComp) -{ - QUEUE_CALL_1(_hyperhdr, setVisiblePriority, int, priority); -} - -void API::registerInput(int priority, hyperhdr::Components component, const QString& origin, const QString& owner, hyperhdr::Components callerComp) -{ - if (_activeRegisters.count(priority)) - _activeRegisters.erase(priority); - - _activeRegisters.insert({ priority, registerData{component, origin, owner, callerComp} }); - - QUEUE_CALL_4(_hyperhdr, registerInput, int, priority, hyperhdr::Components, component, QString, origin, QString, owner); -} - -void API::unregisterInput(int priority) -{ - if (_activeRegisters.count(priority)) - _activeRegisters.erase(priority); -} - -std::map API::getAllComponents() -{ - std::map comps; - //QMetaObject::invokeMethod(_hyperhdr, "getAllComponents", Qt::BlockingQueuedConnection, Q_RETURN_ARG(std::map, comps)); - return comps; -} - -bool API::isHyperhdrEnabled() -{ - int res = false; - - SAFE_CALL_1_RET(_hyperhdr, isComponentEnabled, int, res, hyperhdr::Components, hyperhdr::COMP_ALL); - - return res > 0; -} - -QVector API::getAllInstanceData() -{ - QVector vec; - - SAFE_CALL_0_RET(_instanceManager, getInstanceData, QVector, vec); - - return vec; -} - -QJsonObject API::getAverageColor(quint8 index) -{ - QJsonObject res; - - SAFE_CALL_1_RET(_instanceManager, getAverageColor, QJsonObject, res, quint8, index); - - return res; -} - -void API::requestActiveRegister(QObject* callerInstance) -{ - // TODO FIXME - //if (_activeRegisters.size()) - // QMetaObject::invokeMethod(ApiSync::getInstance(), "answerActiveRegister", Qt::QueuedConnection, Q_ARG(QObject *, callerInstance), Q_ARG(MapRegister, _activeRegisters)); -} - -bool API::saveSettings(const QJsonObject& data) -{ - bool rc = false; - if (_adminAuthorized) - { - SAFE_CALL_2_RET(_hyperhdr, saveSettings, bool, rc, QJsonObject, data, bool, true); - } - return rc; -} - -QString API::installLut(QNetworkReply* reply, QString fileName, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time) -{ -#ifdef ENABLE_XZ - QString error = nullptr; - - if (reply->error() == QNetworkReply::NetworkError::NoError) - { - QByteArray downloadedData = reply->readAll(); - - QFile file(fileName); - if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) - { - size_t outSize = 67174456; - uint8_t* outBuf = reinterpret_cast(malloc(outSize)); - - if (outBuf == nullptr) - { - error = "Could not allocate buffer"; - } - else - { - const uint32_t flags = LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED; - lzma_stream strm = LZMA_STREAM_INIT; - strm.next_in = reinterpret_cast(downloadedData.data()); - strm.avail_in = downloadedData.size(); - lzma_ret lzmaRet = lzma_stream_decoder(&strm, outSize, flags); - if (lzmaRet == LZMA_OK) - { - do { - strm.next_out = outBuf; - strm.avail_out = outSize; - lzmaRet = lzma_code(&strm, LZMA_FINISH); - if (lzmaRet == LZMA_MEMLIMIT_ERROR) - { - outSize = lzma_memusage(&strm); - free(outBuf); - outBuf = reinterpret_cast(malloc(outSize)); - if (outBuf == nullptr) - { - error = QString("Could not increase buffer size"); - break; - } - lzma_memlimit_set(&strm, outSize); - strm.avail_out = 0; - } - else if (lzmaRet != LZMA_OK && lzmaRet != LZMA_STREAM_END) - { - // error - error = QString("LZMA decoder return error: %1").arg(lzmaRet); - break; - } - else - { - size_t toWrite = outSize - strm.avail_out; - file.write(QByteArray((char*)outBuf, toWrite)); - } - } while (strm.avail_out == 0 && lzmaRet != LZMA_STREAM_END); - file.flush(); - } - else - { - error = "Could not initialize LZMA decoder"; - } - - if (time != 0) - file.setFileTime(QDateTime::fromMSecsSinceEpoch(time), QFileDevice::FileModificationTime); - - file.close(); - if (error != nullptr) - file.remove(); - - lzma_end(&strm); - free(outBuf); - } - } - else - error = QString("Could not open %1 for writing").arg(fileName); - } - else - error = "Could not download LUT file"; - - return error; -#else - return "XZ support was disabled in the build configuration"; -#endif -} - -quint8 API::getCurrentInstanceIndex() -{ - return _currInstanceIndex; -} - -bool API::isAuthorized() -{ - return _authorized; -}; - -bool API::isAdminAuthorized() -{ - return _adminAuthorized; -}; - -bool API::updateHyperhdrPassword(const QString& password, const QString& newPassword) -{ - if (!_adminAuthorized) - return false; - bool res; - SAFE_CALL_3_RET(_authManager, updateUserPassword, bool, res, QString, DEFAULT_CONFIG_USER, QString, password, QString, newPassword); - return res; -} - -QString API::createToken(const QString& comment, AuthManager::AuthDefinition& def) -{ - if (!_adminAuthorized) - return NO_AUTH; - if (comment.isEmpty()) - return "comment is empty"; - SAFE_CALL_1_RET(_authManager, createToken, AuthManager::AuthDefinition, def, QString, comment); - return ""; -} - -QString API::renameToken(const QString& id, const QString& comment) -{ - if (!_adminAuthorized) - return NO_AUTH; - if (comment.isEmpty() || id.isEmpty()) - return "Empty comment or id"; - - QUEUE_CALL_2(_authManager, renameToken, QString, id, QString, comment); - return ""; -} - -QString API::deleteToken(const QString& id) -{ - if (!_adminAuthorized) - return NO_AUTH; - if (id.isEmpty()) - return "Empty id"; - - QUEUE_CALL_1(_authManager, deleteToken, QString, id); - return ""; -} - -void API::setNewTokenRequest(const QString& comment, const QString& id, const int& tan) -{ - QUEUE_CALL_4(_authManager, setNewTokenRequest, QObject*, this, QString, comment, QString, id, int, tan); -} - -void API::cancelNewTokenRequest(const QString& comment, const QString& id) -{ - QUEUE_CALL_3(_authManager, cancelNewTokenRequest, QObject*, this, QString, comment, QString, id); -} - -bool API::handlePendingTokenRequest(const QString& id, bool accept) -{ - if (!_adminAuthorized) - return false; - QUEUE_CALL_2(_authManager, handlePendingTokenRequest, QString, id, bool, accept); - return true; -} - -bool API::getTokenList(QVector& def) -{ - if (!_adminAuthorized) - return false; - SAFE_CALL_0_RET(_authManager, getTokenList, QVector, def); - return true; -} - -bool API::getPendingTokenRequests(QVector& map) -{ - if (!_adminAuthorized) - return false; - SAFE_CALL_0_RET(_authManager, getPendingRequests, QVector, map); - return true; -} - -bool API::isUserTokenAuthorized(const QString& userToken) -{ - bool res; - SAFE_CALL_2_RET(_authManager, isUserTokenAuthorized, bool, res, QString, DEFAULT_CONFIG_USER, QString, userToken); - if (res) - { - _authorized = true; - _adminAuthorized = true; - // Listen for ADMIN ACCESS protected signals - connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest, Qt::UniqueConnection); - } - return res; -} - -bool API::getUserToken(QString& userToken) -{ - if (!_adminAuthorized) - return false; - SAFE_CALL_0_RET(_authManager, getUserToken, QString, userToken); - return true; -} - -bool API::isTokenAuthorized(const QString& token) -{ - SAFE_CALL_1_RET(_authManager, isTokenAuthorized, bool, _authorized, QString, token); - return _authorized; -} - -bool API::isUserAuthorized(const QString& password) -{ - bool res = false; - SAFE_CALL_2_RET(_authManager, isUserAuthorized, bool, res, QString, DEFAULT_CONFIG_USER, QString, password); - if (res) - { - _authorized = true; - _adminAuthorized = true; - // Listen for ADMIN ACCESS protected signals - connect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest, Qt::UniqueConnection); - } - return res; -} - -bool API::isUserBlocked() -{ - bool res; - SAFE_CALL_0_RET(_authManager, isUserAuthBlocked, bool, res); - return res; -} - -bool API::hasHyperhdrDefaultPw() -{ - bool res = false; - SAFE_CALL_2_RET(_authManager, isUserAuthorized, bool, res, QString, DEFAULT_CONFIG_USER, QString, DEFAULT_CONFIG_PASSWORD); - return res; -} - -void API::logout() -{ - _authorized = false; - _adminAuthorized = false; - // Stop listenig for ADMIN ACCESS protected signals - disconnect(_authManager, &AuthManager::newPendingTokenRequest, this, &API::onPendingTokenRequest); - stopDataConnectionss(); -} - -void API::stopDataConnectionss() -{ -} diff --git a/sources/api/BaseAPI.cpp b/sources/api/BaseAPI.cpp new file mode 100644 index 000000000..593db25c8 --- /dev/null +++ b/sources/api/BaseAPI.cpp @@ -0,0 +1,784 @@ +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + + #include + #include + #include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_XZ + #include +#endif + +#ifdef _WIN32 + #include +#endif + +using namespace hyperhdr; + +BaseAPI::BaseAPI(Logger* log, bool localConnection, QObject* parent) + : QObject(parent), + _adminAuthorized(false), + _log(log), + _instanceManager(nullptr), + _accessManager(nullptr), + _soundCapture(nullptr), + _videoGrabber(nullptr), + _systemGrabber(nullptr), + _performanceCounters(nullptr), + _discoveryWrapper(nullptr), + _authorized(false), + _localConnection(localConnection), + _currentInstanceIndex(0) +{ + qRegisterMetaType("int64_t"); + qRegisterMetaType>("std::map"); + + emit GlobalSignals::getInstance()->SignalGetInstanceManager(_instanceManager); + if (_instanceManager == nullptr) + { + Error(_log, "Instance manager is already removed"); + return; + } + + emit GlobalSignals::getInstance()->SignalGetAccessManager(_accessManager); + if (_accessManager == nullptr) + { + Error(_log, "Access manager is already removed"); + return; + } + + emit GlobalSignals::getInstance()->SignalGetPerformanceCounters(_performanceCounters); + if (_performanceCounters == nullptr) + { + Error(_log, "PerformanceCounters is already removed"); + return; + } + + emit GlobalSignals::getInstance()->SignalGetSoundCapture(_soundCapture); + emit GlobalSignals::getInstance()->SignalGetVideoGrabber(_videoGrabber); + emit GlobalSignals::getInstance()->SignalGetSystemGrabber(_systemGrabber); + emit GlobalSignals::getInstance()->SignalGetDiscoveryWrapper(_discoveryWrapper); + + SAFE_CALL_1_RET(_instanceManager.get(), getHyperHdrInstance, std::shared_ptr, _hyperhdr, quint8, 0); + if (_hyperhdr == nullptr) + { + Error(_log, "Could not get HyperHDR first instance"); + return; + } + + // connect to possible token responses that has been requested + connect(_accessManager.get(), &AccessManager::SignalTokenNotifyClient, this, [this](bool success, QObject* caller, const QString& token, const QString& comment, const QString& id, const int& tan) + { + if (this == caller) + emit SignalTokenClientNotification(success, token, comment, id, tan); + }); + + // connect to possible startInstance responses that has been requested + connect(_instanceManager.get(), &HyperHdrManager::SignalStartInstanceResponse, this, [this](QObject* caller, const int& tan) + { + if (this == caller) + emit SignalInstanceStartedClientNotification(tan); + }); +} + +void BaseAPI::init() +{ + bool apiAuthRequired = _accessManager->isAuthRequired(); + + // For security we block external connections if default PW is set + if (!_localConnection && BaseAPI::hasHyperhdrDefaultPw()) + { + emit SignalPerformClientDisconnection(); + } + + // if this is localConnection and network allows unauth locals, set authorized flag + if (apiAuthRequired && _localConnection) + _authorized = !_accessManager->isLocalAuthRequired(); + + // admin access is allowed, when the connection is local and the option for local admin isn't set. Con: All local connections get full access + if (_localConnection) + { + _adminAuthorized = !_accessManager->isLocalAdminAuthRequired(); + // just in positive direction + if (_adminAuthorized) + _authorized = true; + } +} + +bool BaseAPI::setHyperhdrInstance(quint8 inst) +{ + if (_currentInstanceIndex == inst) + return true; + + if (_hyperhdr != nullptr) + disconnect(_hyperhdr.get(), nullptr, this, nullptr); + + removeSubscriptions(); + + SAFE_CALL_1_RET(_instanceManager.get(), getHyperHdrInstance, std::shared_ptr, _hyperhdr, quint8, inst); + _currentInstanceIndex = inst; + + addSubscriptions(); + + return true; +} + +bool BaseAPI::startInstance(quint8 index, int tan) +{ + bool res; + + SAFE_CALL_3_RET(_instanceManager.get(), startInstance, bool, res, quint8, index, QObject*, this, int, tan); + + return res; +} + +void BaseAPI::stopInstance(quint8 index) +{ + QUEUE_CALL_1(_instanceManager.get(), stopInstance, quint8, index); +} + +bool BaseAPI::deleteInstance(quint8 index, QString& replyMsg) +{ + if (_adminAuthorized) + { + QUEUE_CALL_1(_instanceManager.get(), deleteInstance, quint8, index); + return true; + } + replyMsg = NO_AUTH; + return false; +} + +QString BaseAPI::createInstance(const QString& name) +{ + if (_adminAuthorized) + { + bool success = false; + + SAFE_CALL_1_RET(_instanceManager.get(), createInstance, bool, success, QString, name); + + if (!success) + return QString("Instance name '%1' is already in use").arg(name); + + return ""; + } + return NO_AUTH; +} + +QString BaseAPI::setInstanceName(quint8 index, const QString& name) +{ + if (_adminAuthorized) + { + QUEUE_CALL_2(_instanceManager.get(), saveName, quint8, index, QString, name); + return ""; + } + return NO_AUTH; +} + +void BaseAPI::setColor(int priority, const std::vector& ledColors, int timeout_ms, const QString& origin, hyperhdr::Components callerComp) +{ + std::vector fledColors; + if (ledColors.size() % 3 == 0) + { + for (uint64_t i = 0; i < ledColors.size(); i += 3) + { + fledColors.emplace_back(ColorRgb{ ledColors[i], ledColors[i + 1], ledColors[i + 2] }); + } + QUEUE_CALL_4(_hyperhdr.get(), setColor, int, priority, std::vector, fledColors, int, timeout_ms, QString, origin); + } +} + +bool BaseAPI::setImage(ImageCmdData& data, hyperhdr::Components comp, QString& replyMsg, hyperhdr::Components callerComp) +{ + // truncate name length + data.imgName.truncate(16); + + if (data.format == "auto") + { + QImage img = QImage::fromData(data.data); + if (img.isNull()) + { + replyMsg = "Failed to parse picture, the file might be corrupted"; + return false; + } + + // check for requested scale + if (data.scale > 24) + { + if (img.height() > data.scale) + { + img = img.scaledToHeight(data.scale); + } + if (img.width() > data.scale) + { + img = img.scaledToWidth(data.scale); + } + } + + // check if we need to force a scale + if (img.width() > 2000 || img.height() > 2000) + { + data.scale = 2000; + if (img.height() > data.scale) + { + img = img.scaledToHeight(data.scale); + } + if (img.width() > data.scale) + { + img = img.scaledToWidth(data.scale); + } + } + + data.width = img.width(); + data.height = img.height(); + + // extract image + img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); + data.data.clear(); + data.data.reserve(img.width() * img.height() * 3); + for (int i = 0; i < img.height(); ++i) + { + const QRgb* scanline = reinterpret_cast(img.scanLine(i)); + for (int j = 0; j < img.width(); ++j) + { + data.data.append((char)qRed(scanline[j])); + data.data.append((char)qGreen(scanline[j])); + data.data.append((char)qBlue(scanline[j])); + } + } + } + else + { + // check consistency of the size of the received data + if (data.data.size() != data.width * data.height * 3) + { + replyMsg = "Size of image data does not match with the width and height"; + return false; + } + } + + // copy image + Image image(data.width, data.height); + memcpy(image.rawMem(), data.data.data(), data.data.size()); + + + QUEUE_CALL_4(_hyperhdr.get(), registerInput, int, data.priority, hyperhdr::Components, comp, QString, data.origin, QString, data.imgName); + QUEUE_CALL_3(_hyperhdr.get(), setInputImage, int, data.priority, Image, image, int64_t, data.duration); + + return true; +} + +bool BaseAPI::clearPriority(int priority, QString& replyMsg, hyperhdr::Components callerComp) +{ + if (priority < 0 || (priority > 0 && priority < Muxer::LOWEST_EFFECT_PRIORITY)) + { + QUEUE_CALL_1(_hyperhdr.get(), clear, int, priority); + } + else + { + replyMsg = QString("Priority %1 is not allowed to be cleared").arg(priority); + return false; + } + return true; +} + +bool BaseAPI::setComponentState(const QString& comp, bool& compState, QString& replyMsg, hyperhdr::Components callerComp) +{ + QString input(comp); + if (input == "GRABBER") + input = "SYSTEMGRABBER"; + if (input == "V4L") + input = "VIDEOGRABBER"; + Components component = stringToComponent(input); + if (component == COMP_ALL) + { + QUEUE_CALL_1(_instanceManager.get(), toggleStateAllInstances, bool, compState); + + return true; + } + else if (component == COMP_HDR) + { + setVideoModeHdr((compState) ? 1 : 0, component); + return true; + } + else if (component != COMP_INVALID) + { + QUEUE_CALL_2(_hyperhdr.get(), SignalRequestComponent, hyperhdr::Components, component, bool, compState); + return true; + } + replyMsg = QString("Unknown component name: %1").arg(comp); + return false; +} + +void BaseAPI::setLedMappingType(int type, hyperhdr::Components callerComp) +{ + QUEUE_CALL_1(_hyperhdr.get(), setLedMappingType, int, type); +} + +void BaseAPI::setVideoModeHdr(int hdr, hyperhdr::Components callerComp) +{ + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::Components::COMP_HDR, -1, hdr); +} + +void BaseAPI::setFlatbufferUserLUT(QString userLUTfile) +{ + QJsonObject obj; + obj[BASEAPI_FLATBUFFER_USER_LUT_FILE] = userLUTfile; + QJsonDocument updateSettings(obj); + emit _instanceManager->SignalSettingsChanged(settings::type::FLATBUFSERVER, updateSettings); +} + +bool BaseAPI::setEffect(const EffectCmdData& dat, hyperhdr::Components callerComp) +{ + int res = -1; + + if (dat.args.isEmpty()) + { + SAFE_CALL_4_RET(_hyperhdr.get(), setEffect, int, res, QString, dat.effectName, int, dat.priority, int, dat.duration, QString, dat.origin); + } + + return res >= 0; +} + +void BaseAPI::setSourceAutoSelect(bool state, hyperhdr::Components callerComp) +{ + QUEUE_CALL_1(_hyperhdr.get(), setSourceAutoSelect, bool, state); +} + +void BaseAPI::setVisiblePriority(int priority, hyperhdr::Components callerComp) +{ + QUEUE_CALL_1(_hyperhdr.get(), setVisiblePriority, int, priority); +} + +void BaseAPI::registerInput(int priority, hyperhdr::Components component, const QString& origin, const QString& owner, hyperhdr::Components callerComp) +{ + if (_activeRegisters.count(priority)) + _activeRegisters.erase(priority); + + _activeRegisters.insert({ priority, registerData{component, origin, owner, callerComp} }); + + QUEUE_CALL_4(_hyperhdr.get(), registerInput, int, priority, hyperhdr::Components, component, QString, origin, QString, owner); +} + +void BaseAPI::unregisterInput(int priority) +{ + if (_activeRegisters.count(priority)) + _activeRegisters.erase(priority); +} + +std::map BaseAPI::getAllComponents() +{ + std::map comps; + //QMetaObject::invokeMethod(_hyperhdr, "getAllComponents", Qt::BlockingQueuedConnection, Q_RETURN_ARG(std::map, comps)); + return comps; +} + +bool BaseAPI::isHyperhdrEnabled() +{ + int res = false; + + SAFE_CALL_1_RET(_hyperhdr.get(), isComponentEnabled, int, res, hyperhdr::Components, hyperhdr::COMP_ALL); + + return res > 0; +} + +QVector BaseAPI::getAllInstanceData() +{ + QVector vec; + + SAFE_CALL_0_RET(_instanceManager.get(), getInstanceData, QVector, vec); + + return vec; +} + +QJsonObject BaseAPI::getAverageColor(quint8 index) +{ + QJsonObject res; + + SAFE_CALL_1_RET(_instanceManager.get(), getAverageColor, QJsonObject, res, quint8, index); + + return res; +} + +void BaseAPI::requestActiveRegister(QObject* callerInstance) +{ + // TODO FIXME + //if (_activeRegisters.size()) + // QMetaObject::invokeMethod(ApiSync::getInstance(), "answerActiveRegister", Qt::QueuedConnection, Q_ARG(QObject *, callerInstance), Q_ARG(MapRegister, _activeRegisters)); +} + +bool BaseAPI::saveSettings(const QJsonObject& data) +{ + bool rc = false; + if (_adminAuthorized) + { + SAFE_CALL_2_RET(_hyperhdr.get(), saveSettings, bool, rc, QJsonObject, data, bool, true); + } + return rc; +} + +QString BaseAPI::installLut(QNetworkReply* reply, QString fileName, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time) +{ +#ifdef ENABLE_XZ + QString error = nullptr; + + if (reply->error() == QNetworkReply::NetworkError::NoError) + { + QByteArray downloadedData = reply->readAll(); + + QFile file(fileName); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + size_t outSize = 67174456; + MemoryBuffer outBuffer(outSize); + + if (outBuffer.data() == nullptr) + { + error = "Could not allocate buffer"; + } + else + { + const uint32_t flags = LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED; + lzma_stream strm = LZMA_STREAM_INIT; + strm.next_in = reinterpret_cast(downloadedData.data()); + strm.avail_in = downloadedData.size(); + lzma_ret lzmaRet = lzma_stream_decoder(&strm, outSize, flags); + if (lzmaRet == LZMA_OK) + { + do { + strm.next_out = outBuffer.data(); + strm.avail_out = outSize; + lzmaRet = lzma_code(&strm, LZMA_FINISH); + if (lzmaRet == LZMA_MEMLIMIT_ERROR) + { + outSize = lzma_memusage(&strm); + outBuffer.resize(outSize); + if (outBuffer.data() == nullptr) + { + error = QString("Could not increase buffer size"); + break; + } + lzma_memlimit_set(&strm, outSize); + strm.avail_out = 0; + } + else if (lzmaRet != LZMA_OK && lzmaRet != LZMA_STREAM_END) + { + // error + error = QString("LZMA decoder return error: %1").arg(lzmaRet); + break; + } + else + { + qint64 toWrite = static_cast(outSize - strm.avail_out); + file.write(reinterpret_cast(outBuffer.data()), toWrite); + } + } while (strm.avail_out == 0 && lzmaRet != LZMA_STREAM_END); + file.flush(); + } + else + { + error = "Could not initialize LZMA decoder"; + } + + if (time != 0) + file.setFileTime(QDateTime::fromMSecsSinceEpoch(time), QFileDevice::FileModificationTime); + + file.close(); + if (error != nullptr) + file.remove(); + + lzma_end(&strm); + } + } + else + error = QString("Could not open %1 for writing").arg(fileName); + } + else + error = "Could not download LUT file"; + + return error; +#else + return "XZ support was disabled in the build configuration"; +#endif +} + +quint8 BaseAPI::getCurrentInstanceIndex() +{ + return _currentInstanceIndex; +} + +bool BaseAPI::isAuthorized() +{ + return _authorized; +}; + +bool BaseAPI::isAdminAuthorized() +{ + return _adminAuthorized; +}; + +bool BaseAPI::updateHyperhdrPassword(const QString& password, const QString& newPassword) +{ + if (!_adminAuthorized) + return false; + bool res; + SAFE_CALL_3_RET(_accessManager.get(), updateUserPassword, bool, res, QString, DEFAULT_CONFIG_USER, QString, password, QString, newPassword); + return res; +} + +QString BaseAPI::createToken(const QString& comment, AccessManager::AuthDefinition& def) +{ + if (!_adminAuthorized) + return NO_AUTH; + if (comment.isEmpty()) + return "comment is empty"; + SAFE_CALL_1_RET(_accessManager.get(), createToken, AccessManager::AuthDefinition, def, QString, comment); + return ""; +} + +QString BaseAPI::renameToken(const QString& id, const QString& comment) +{ + if (!_adminAuthorized) + return NO_AUTH; + if (comment.isEmpty() || id.isEmpty()) + return "Empty comment or id"; + + QUEUE_CALL_2(_accessManager.get(), renameToken, QString, id, QString, comment); + return ""; +} + +QString BaseAPI::deleteToken(const QString& id) +{ + if (!_adminAuthorized) + return NO_AUTH; + if (id.isEmpty()) + return "Empty id"; + + QUEUE_CALL_1(_accessManager.get(), deleteToken, QString, id); + return ""; +} + +void BaseAPI::setNewTokenRequest(const QString& comment, const QString& id, const int& tan) +{ + QUEUE_CALL_4(_accessManager.get(), setNewTokenRequest, QObject*, this, QString, comment, QString, id, int, tan); +} + +void BaseAPI::cancelNewTokenRequest(const QString& comment, const QString& id) +{ + QUEUE_CALL_3(_accessManager.get(), cancelNewTokenRequest, QObject*, this, QString, comment, QString, id); +} + +bool BaseAPI::handlePendingTokenRequest(const QString& id, bool accept) +{ + if (!_adminAuthorized) + return false; + QUEUE_CALL_2(_accessManager.get(), handlePendingTokenRequest, QString, id, bool, accept); + return true; +} + +bool BaseAPI::getTokenList(QVector& def) +{ + if (!_adminAuthorized) + return false; + SAFE_CALL_0_RET(_accessManager.get(), getTokenList, QVector, def); + return true; +} + +bool BaseAPI::getPendingTokenRequests(QVector& map) +{ + if (!_adminAuthorized) + return false; + SAFE_CALL_0_RET(_accessManager.get(), getPendingRequests, QVector, map); + return true; +} + +bool BaseAPI::isUserTokenAuthorized(const QString& userToken) +{ + bool res; + SAFE_CALL_2_RET(_accessManager.get(), isUserTokenAuthorized, bool, res, QString, DEFAULT_CONFIG_USER, QString, userToken); + if (res) + { + _authorized = true; + _adminAuthorized = true; + // Listen for ADMIN ACCESS protected signals + connect(_accessManager.get(), &AccessManager::newPendingTokenRequest, this, &BaseAPI::SignalPendingTokenClientNotification, Qt::UniqueConnection); + } + return res; +} + +bool BaseAPI::getUserToken(QString& userToken) +{ + if (!_adminAuthorized) + return false; + SAFE_CALL_0_RET(_accessManager.get(), getUserToken, QString, userToken); + return true; +} + +bool BaseAPI::isTokenAuthorized(const QString& token) +{ + SAFE_CALL_1_RET(_accessManager.get(), isTokenAuthorized, bool, _authorized, QString, token); + return _authorized; +} + +bool BaseAPI::isUserAuthorized(const QString& password) +{ + bool res = false; + SAFE_CALL_2_RET(_accessManager.get(), isUserAuthorized, bool, res, QString, DEFAULT_CONFIG_USER, QString, password); + if (res) + { + _authorized = true; + _adminAuthorized = true; + // Listen for ADMIN ACCESS protected signals + connect(_accessManager.get(), &AccessManager::newPendingTokenRequest, this, &BaseAPI::SignalPendingTokenClientNotification, Qt::UniqueConnection); + } + return res; +} + +bool BaseAPI::isUserBlocked() +{ + bool res; + SAFE_CALL_0_RET(_accessManager.get(), isUserAuthBlocked, bool, res); + return res; +} + +bool BaseAPI::hasHyperhdrDefaultPw() +{ + bool res = false; + SAFE_CALL_2_RET(_accessManager.get(), isUserAuthorized, bool, res, QString, DEFAULT_CONFIG_USER, QString, DEFAULT_CONFIG_PASSWORD); + return res; +} + +void BaseAPI::logout() +{ + _authorized = false; + _adminAuthorized = false; + // Stop listenig for ADMIN ACCESS protected signals + disconnect(_accessManager.get(), &AccessManager::newPendingTokenRequest, this, &BaseAPI::SignalPendingTokenClientNotification); + stopDataConnections(); +} + +void BaseAPI::putSystemInfo(QJsonObject& system) +{ + if (!_sysInfo.init) + { + _sysInfo.init = true; + + _sysInfo.kernelType = QSysInfo::kernelType(); + _sysInfo.kernelVersion = QSysInfo::kernelVersion(); + _sysInfo.architecture = QSysInfo::currentCpuArchitecture(); + _sysInfo.wordSize = QString::number(QSysInfo::WordSize); + _sysInfo.productType = QSysInfo::productType(); + _sysInfo.productVersion = QSysInfo::productVersion(); + _sysInfo.prettyName = QSysInfo::prettyProductName(); + _sysInfo.hostName = QHostInfo::localHostName(); + _sysInfo.domainName = QHostInfo::localDomainName(); + + #ifdef _WIN32 + QString cpucorp = "wmic cpu get name"; + QProcess windowscpu; + windowscpu.startCommand(cpucorp); + windowscpu.waitForFinished(); + QString result = windowscpu.readAllStandardOutput().trimmed(); + if (result.startsWith("Name", Qt::CaseInsensitive)) + result = result.right(result.size()-4).trimmed(); + _sysInfo.cpuModelName = result; + #else + QString cpuString; + QFile file("/proc/cpuinfo"); + + if (file.exists() && file.open(QFile::ReadOnly | QFile::Text)) + { + QTextStream in(&file); + + while (in.readLineInto(&cpuString)) + { + bool more = false; + + if (_sysInfo.cpuModelType.isEmpty()) + { + QString match = "model\t"; + if (cpuString.startsWith(match, Qt::CaseInsensitive)) + _sysInfo.cpuModelType = cpuString.right(cpuString.length() - match.length() - 2).trimmed(); + else + more = true; + } + + if (_sysInfo.cpuModelName.isEmpty()) + { + QString match = "model name\t"; + + if (cpuString.startsWith(match, Qt::CaseInsensitive)) + _sysInfo.cpuModelName = cpuString.right(cpuString.length() - match.length() - 2).trimmed(); + else + more = true; + } + + if (_sysInfo.cpuHardware.isEmpty()) + { + QString match = "hardware\t"; + + if (cpuString.startsWith(match, Qt::CaseInsensitive)) + _sysInfo.cpuHardware = cpuString.right(cpuString.length() - match.length() - 2).trimmed(); + else + more = true; + } + + if (_sysInfo.cpuRevision.isEmpty()) + { + QString match = "revision\t"; + + if (cpuString.startsWith(match, Qt::CaseInsensitive)) + _sysInfo.cpuHardware = cpuString.right(cpuString.length() - match.length() - 2).trimmed(); + else + more = true; + } + + if (!more) + break; + } + + file.close(); + } + #endif + }; + + system["kernelType"] = _sysInfo.kernelType; + system["kernelVersion"] = _sysInfo.kernelVersion; + system["architecture"] = _sysInfo.architecture; + system["cpuModelName"] = _sysInfo.cpuModelName; + system["cpuModelType"] = _sysInfo.cpuModelType; + system["cpuHardware"] = _sysInfo.cpuHardware; + system["cpuRevision"] = _sysInfo.cpuRevision; + system["wordSize"] = _sysInfo.wordSize; + system["productType"] = _sysInfo.productType; + system["productVersion"] = _sysInfo.productVersion; + system["prettyName"] = _sysInfo.prettyName; + system["hostName"] = _sysInfo.hostName; + system["domainName"] = _sysInfo.domainName; + system["qtVersion"] = QT_VERSION_STR; +} diff --git a/sources/api/CMakeLists.txt b/sources/api/CMakeLists.txt index 38b9465f5..a0814ab8c 100644 --- a/sources/api/CMakeLists.txt +++ b/sources/api/CMakeLists.txt @@ -23,7 +23,7 @@ target_link_libraries(hyperhdr-api if(ENABLE_XZ) if (NOT LIBLZMA_FOUND) target_link_libraries(hyperhdr-api liblzma) - target_include_directories(hyperhdr-api PRIVATE "${CMAKE_SOURCE_DIR}/dependencies/external/xz/src/liblzma/api") + target_include_directories(hyperhdr-api PRIVATE "${CMAKE_SOURCE_DIR}/external/xz/src/liblzma/api") else() target_link_libraries(hyperhdr-api LibLZMA::LibLZMA) target_include_directories(hyperhdr-api PRIVATE ${LIBLZMA_INCLUDE_DIR}) diff --git a/sources/api/CallbackAPI.cpp b/sources/api/CallbackAPI.cpp new file mode 100644 index 000000000..8023a2903 --- /dev/null +++ b/sources/api/CallbackAPI.cpp @@ -0,0 +1,396 @@ +#ifndef PCH_ENABLED + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hyperhdr; + +CallbackAPI::CallbackAPI(Logger* log, bool localConnection, QObject* parent) + : BaseAPI(log, localConnection, parent) +{ + _lutCalibrator = nullptr; + _availableCommands << "components-update" << "performance-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update" << "grabberstate-update" << "lut-calibration-update" + << "adjustment-update" << "leds-colors" << "live-video" << "videomodehdr-update" << "settings-update" << "leds-update" << "instance-update" << "token-update" << "benchmark-update"; +} + +void CallbackAPI::addSubscriptions() +{ + // get current subs + QStringList currSubs(_subscribedCommands); + + // stop subs + removeSubscriptions(); + + // re-apply subs + for (const auto& entry : currSubs) + { + subscribeFor(entry); + } +} + +bool CallbackAPI::subscribeFor(const QString& type, bool unsubscribe) +{ + if (!_availableCommands.contains(type)) + return false; + + GrabberWrapper* grabberWrapper = (_videoGrabber != nullptr) ? _videoGrabber->grabberWrapper() : nullptr; + + if (unsubscribe) + _subscribedCommands.removeAll(type); + else + _subscribedCommands << type; + + if (type == "components-update" && _hyperhdr != nullptr) + { + if (unsubscribe) + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalComponentStateChanged, this, &CallbackAPI::componentStateHandler); + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalComponentStateChanged, this, &CallbackAPI::componentStateHandler, Qt::UniqueConnection); + } + + if (type == "performance-update") + { + if (unsubscribe) + disconnect(_performanceCounters.get(), &PerformanceCounters::SignalPerformanceStatisticsUpdated, this, &CallbackAPI::performanceUpdateHandler); + else + { + connect(_performanceCounters.get(), &PerformanceCounters::SignalPerformanceStatisticsUpdated, this, &CallbackAPI::performanceUpdateHandler, Qt::UniqueConnection); + QUEUE_CALL_0(_performanceCounters.get(), triggerBroadcast); + } + } + + if (type == "lut-calibration-update") + { + if (unsubscribe) + { + if (_lutCalibrator != nullptr) + disconnect(_lutCalibrator.get(), &LutCalibrator::SignalLutCalibrationUpdated, this, &CallbackAPI::lutCalibrationUpdateHandler); + _lutCalibrator = nullptr; + } + else + { + _lutCalibrator = std::unique_ptr(new LutCalibrator()); + connect(_lutCalibrator.get(), &LutCalibrator::SignalLutCalibrationUpdated, this, &CallbackAPI::lutCalibrationUpdateHandler, Qt::UniqueConnection); + } + } + + if (type == "sessions-update" && _discoveryWrapper != nullptr) + { +#ifdef ENABLE_BONJOUR + if (unsubscribe) + disconnect(_discoveryWrapper.get(), &DiscoveryWrapper::SignalDiscoveryFoundService, this, &CallbackAPI::signalDiscoveryFoundServiceHandler); + else + connect(_discoveryWrapper.get(), &DiscoveryWrapper::SignalDiscoveryFoundService, this, &CallbackAPI::signalDiscoveryFoundServiceHandler, Qt::UniqueConnection); +#endif + } + + if (type == "priorities-update" && _hyperhdr != nullptr) + { + if (unsubscribe) + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalPrioritiesChanged, this, &CallbackAPI::priorityUpdateHandler); + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalPrioritiesChanged, this, &CallbackAPI::priorityUpdateHandler, Qt::UniqueConnection); + } + + if (type == "imageToLedMapping-update" && _hyperhdr != nullptr) + { + if (unsubscribe) + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalImageToLedsMappingChanged, this, &CallbackAPI::imageToLedsMappingChangeHandler); + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalImageToLedsMappingChanged, this, &CallbackAPI::imageToLedsMappingChangeHandler, Qt::UniqueConnection); + } + + if (type == "adjustment-update" && _hyperhdr != nullptr) + { + if (unsubscribe) + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalAdjustmentUpdated, this, &CallbackAPI::signalAdjustmentUpdatedHandler); + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalAdjustmentUpdated, this, &CallbackAPI::signalAdjustmentUpdatedHandler, Qt::UniqueConnection); + } + + if (type == "grabberstate-update" && grabberWrapper != nullptr) + { + if (unsubscribe) + disconnect(grabberWrapper, &GrabberWrapper::SignalVideoStreamChanged, this, &CallbackAPI::videoStreamChangedHandler); + else + connect(grabberWrapper, &GrabberWrapper::SignalVideoStreamChanged, this, &CallbackAPI::videoStreamChangedHandler, Qt::UniqueConnection); + } + + if (type == "videomodehdr-update") + { + if (unsubscribe) + disconnect(_instanceManager.get(), &HyperHdrManager::SignalSetNewComponentStateToAllInstances, this, &CallbackAPI::videoModeHdrChangeHandler); + else + connect(_instanceManager.get(), &HyperHdrManager::SignalSetNewComponentStateToAllInstances, this, &CallbackAPI::videoModeHdrChangeHandler, Qt::UniqueConnection); + } + + if (type == "settings-update" && _hyperhdr != nullptr) + { + if (unsubscribe) + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalInstanceSettingsChanged, this, &CallbackAPI::settingsChangeHandler); + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalInstanceSettingsChanged, this, &CallbackAPI::settingsChangeHandler, Qt::UniqueConnection); + } + + if (type == "leds-update" && _hyperhdr != nullptr) + { + if (unsubscribe) + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalInstanceSettingsChanged, this, &CallbackAPI::ledsConfigChangeHandler); + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalInstanceSettingsChanged, this, &CallbackAPI::ledsConfigChangeHandler, Qt::UniqueConnection); + } + + if (type == "leds-colors" && _hyperhdr != nullptr) + { + if (unsubscribe) + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalRawColorsChanged, this, &CallbackAPI::handleIncomingColors); + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalRawColorsChanged, this, &CallbackAPI::handleIncomingColors, Qt::UniqueConnection); + } + + if (type == "live-video" && _hyperhdr != nullptr) + { + if (unsubscribe) + { + _liveImage = Image(); + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalInstanceImageUpdated, this, &CallbackAPI::handlerInstanceImageUpdated); + } + else + connect(_hyperhdr.get(), &HyperHdrInstance::SignalInstanceImageUpdated, this, &CallbackAPI::handlerInstanceImageUpdated, Qt::UniqueConnection); + } + + if (type == "instance-update") + { + if (unsubscribe) + disconnect(_instanceManager.get(), &HyperHdrManager::SignalInstancesListChanged, this, &CallbackAPI::instancesListChangedHandler); + else + connect(_instanceManager.get(), &HyperHdrManager::SignalInstancesListChanged, this, &CallbackAPI::instancesListChangedHandler, Qt::UniqueConnection); + } + + if (type == "token-update") + { + if (unsubscribe) + disconnect(_accessManager.get(), &AccessManager::SignalTokenUpdated, this, &CallbackAPI::tokenChangeHandler); + else + connect(_accessManager.get(), &AccessManager::SignalTokenUpdated, this, &CallbackAPI::tokenChangeHandler, Qt::UniqueConnection); + } + + if (type == "benchmark-update" && grabberWrapper != nullptr) + { + if (unsubscribe) + disconnect(grabberWrapper, &GrabberWrapper::SignalBenchmarkUpdate, this, &CallbackAPI::signalBenchmarkUpdateHandler); + else + connect(grabberWrapper, &GrabberWrapper::SignalBenchmarkUpdate, this, &CallbackAPI::signalBenchmarkUpdateHandler, Qt::UniqueConnection); + } + + return true; +} + +void CallbackAPI::subscribe(QJsonArray subsArr) +{ + // catch the all keyword and build a list of all cmds + if (subsArr.contains("all")) + { + subsArr = QJsonArray(); + for (const auto& entry : _availableCommands) + { + subsArr.append(entry); + } + } + + for (const QJsonValueRef entry : subsArr) + { + // config callbacks just if auth is set + if ((entry == "settings-update" || entry == "token-update" || entry == "imagestream-start") && + !BaseAPI::isAdminAuthorized()) + continue; + // silent failure if a subscribe type is not found + subscribeFor(entry.toString()); + } +} + +void CallbackAPI::removeSubscriptions() +{ + if (_hyperhdr != nullptr) + { + QStringList toRemove(_subscribedCommands); + + for (const auto& entry : toRemove) + { + subscribeFor(entry, true); + } + } +} + +void CallbackAPI::doCallback(const QString& cmd, const QVariant& data) +{ + QJsonObject obj; + obj["command"] = cmd; + + if (data.userType() == QMetaType::QJsonArray) + obj["data"] = data.toJsonArray(); + else + obj["data"] = data.toJsonObject(); + + emit SignalCallbackToClient(obj); +} + +void CallbackAPI::componentStateHandler(hyperhdr::Components comp, bool state) +{ + QJsonObject data; + data["name"] = componentToIdString(comp); + data["enabled"] = state; + + doCallback("components-update", QVariant(data)); +} + +#ifdef ENABLE_BONJOUR + void CallbackAPI::signalDiscoveryFoundServiceHandler(DiscoveryRecord::Service type, QList records) + { + QJsonObject retValue; + QJsonArray data; + + for (const auto& session : records) + { + QJsonObject item; + item["name"] = session.getName(); + item["host"] = session.hostName; + item["address"] = session.address; + item["port"] = session.port; + data.append(item); + } + + retValue["service"] = DiscoveryRecord::getName(type); + retValue["items"] = data; + doCallback("sessions-update", QVariant(retValue)); + } +#endif + +void CallbackAPI::priorityUpdateHandler() +{ + QJsonObject info; + QJsonArray priorities; + + if (_hyperhdr == nullptr) + return; + + BLOCK_CALL_2(_hyperhdr.get(), putJsonInfo, QJsonObject&, info, bool, false); + + doCallback("priorities-update", QVariant(info)); +} + +void CallbackAPI::imageToLedsMappingChangeHandler(int mappingType) +{ + QJsonObject data; + data["imageToLedMappingType"] = ImageToLedManager::mappingTypeToStr(mappingType); + + doCallback("imageToLedMapping-update", QVariant(data)); +} + +void CallbackAPI::signalAdjustmentUpdatedHandler(const QJsonArray& newConfig) +{ + doCallback("adjustment-update", QVariant(newConfig)); +} + +void CallbackAPI::videoModeHdrChangeHandler(hyperhdr::Components component, bool enable) +{ + if (component == hyperhdr::Components::COMP_HDR) + { + QJsonObject data; + data["videomodehdr"] = enable; + doCallback("videomodehdr-update", QVariant(data)); + } +} + +void CallbackAPI::videoStreamChangedHandler(QString device, QString videoMode) +{ + QJsonObject data; + data["device"] = device; + data["videoMode"] = videoMode; + doCallback("grabberstate-update", QVariant(data)); +} + +void CallbackAPI::settingsChangeHandler(settings::type type, const QJsonDocument& data) +{ + QJsonObject dat; + if (data.isObject()) + dat[typeToString(type)] = data.object(); + else + dat[typeToString(type)] = data.array(); + + doCallback("settings-update", QVariant(dat)); +} + +void CallbackAPI::ledsConfigChangeHandler(settings::type type, const QJsonDocument& data) +{ + if (type == settings::type::LEDS) + { + QJsonObject dat; + dat[typeToString(type)] = data.array(); + doCallback("leds-update", QVariant(dat)); + } +} + +void CallbackAPI::instancesListChangedHandler() +{ + QJsonArray arr; + QVector entries; + + SAFE_CALL_0_RET(_instanceManager.get(), getInstanceData, QVector, entries); + + for (const auto& entry : entries) + { + QJsonObject obj; + obj.insert("friendly_name", entry["friendly_name"].toString()); + obj.insert("instance", entry["instance"].toInt()); + //obj.insert("last_use", entry["last_use"].toString()); + obj.insert("running", entry["running"].toBool()); + arr.append(obj); + } + doCallback("instance-update", QVariant(arr)); +} + +void CallbackAPI::tokenChangeHandler(const QVector& def) +{ + QJsonArray arr; + for (const auto& entry : def) + { + QJsonObject sub; + sub["comment"] = entry.comment; + sub["id"] = entry.id; + sub["last_use"] = entry.lastUse; + arr.push_back(sub); + } + doCallback("token-update", QVariant(arr)); +} + +void CallbackAPI::signalBenchmarkUpdateHandler(int status, QString message) +{ + QJsonObject dat; + dat["status"] = status; + dat["message"] = message; + doCallback("benchmark-update", QVariant(dat)); +} + +void CallbackAPI::lutCalibrationUpdateHandler(const QJsonObject& data) +{ + doCallback("lut-calibration-update", QVariant(data)); +} + + +void CallbackAPI::performanceUpdateHandler(const QJsonObject& data) +{ + doCallback("performance-update", QVariant(data)); +} + diff --git a/sources/api/JsonAPI.cpp b/sources/api/HyperAPI.cpp similarity index 62% rename from sources/api/JsonAPI.cpp rename to sources/api/HyperAPI.cpp index 114f3c1e4..798a27a2a 100644 --- a/sources/api/JsonAPI.cpp +++ b/sources/api/HyperAPI.cpp @@ -1,23 +1,25 @@ -// project includes -#include - -// stl includes -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include +#endif -// Qt includes #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include +#include +#include +#include #include #include #include @@ -26,53 +28,45 @@ #include #include #include +#include +#include +#include #include #include -#include -#include #include #include #include -#include // bonjour wrapper #ifdef ENABLE_BONJOUR -#include + #include #endif -// ledmapping int <> string transform methods -#include - -// api includes -#include - -// auth manager -#include - -#include - using namespace hyperhdr; -JsonAPI::JsonAPI(QString peerAddress, Logger* log, bool localConnection, QObject* parent, bool noListener) - : API(log, localConnection, parent), _semaphore(1) +HyperAPI::HyperAPI(QString peerAddress, Logger* log, bool localConnection, QObject* parent, bool noListener) + : CallbackAPI(log, localConnection, parent) { + _logsManager = LoggerManager::getInstance(); _noListener = noListener; _peerAddress = peerAddress; - _jsonCB = new JsonCB(this); _streaming_logging_activated = false; _ledStreamTimer = new QTimer(this); - _lastSendImage = InternalClock::now(); _colorsStreamingInterval = 50; + _lastSentImage = 0; - connect(_ledStreamTimer, &QTimer::timeout, this, &JsonAPI::handleLedColorsTimer, Qt::UniqueConnection); + connect(_ledStreamTimer, &QTimer::timeout, this, &HyperAPI::handleLedColorsTimer, Qt::UniqueConnection); Q_INIT_RESOURCE(JSONRPC_schemas); } -void JsonAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader) +void HyperAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader) { try { + if (HyperHdrInstance::isTerminated()) + return; + const QString ident = "JsonRpc@" + _peerAddress; QJsonObject message; // parse the message @@ -109,14 +103,14 @@ void JsonAPI::handleMessage(const QString& messageString, const QString& httpAut } // check auth state - if (!API::isAuthorized()) + if (!BaseAPI::isAuthorized()) { bool authOk = false; if (_noListener) { QString cToken = httpAuthHeader.mid(5).trimmed(); - if (API::isTokenAuthorized(cToken)) + if (BaseAPI::isTokenAuthorized(cToken)) authOk = true; } @@ -130,7 +124,7 @@ void JsonAPI::handleMessage(const QString& messageString, const QString& httpAut bool isRunning = false; quint8 currentIndex = getCurrentInstanceIndex(); - SAFE_CALL_1_RET(_instanceManager, IsInstanceRunning, bool, isRunning, quint8, currentIndex); + SAFE_CALL_1_RET(_instanceManager.get(), IsInstanceRunning, bool, isRunning, quint8, currentIndex); if (_hyperhdr == nullptr || !isRunning) { sendErrorReply("Not ready", command, tan); @@ -199,10 +193,6 @@ void JsonAPI::handleMessage(const QString& messageString, const QString& httpAut handleSmoothingCommand(message, command, tan); else if (command == "current-state") handleCurrentStateCommand(message, command, tan); - else if (command == "transform" || command == "correction" || command == "temperature") - sendErrorReply("The command " + command + "is deprecated, please use the HyperHDR Web Interface to configure", command, tan); - // END - // handle not implemented commands else handleNotImplemented(command, tan); @@ -214,43 +204,39 @@ void JsonAPI::handleMessage(const QString& messageString, const QString& httpAut } } -void JsonAPI::initialize() +void HyperAPI::initialize() { // init API, REQUIRED! - API::init(); - // REMOVE when jsonCB is migrated - handleInstanceSwitch(0); + BaseAPI::init(); // setup auth interface - connect(this, &API::onPendingTokenRequest, this, &JsonAPI::newPendingTokenRequest); - connect(this, &API::onTokenResponse, this, &JsonAPI::handleTokenResponse); + connect(this, &BaseAPI::SignalPendingTokenClientNotification, this, &HyperAPI::newPendingTokenRequest); + connect(this, &BaseAPI::SignalTokenClientNotification, this, &HyperAPI::handleTokenResponse); // listen for killed instances - connect(_instanceManager, &HyperHdrIManager::instanceStateChanged, this, &JsonAPI::handleInstanceStateChange); + connect(_instanceManager.get(), &HyperHdrManager::SignalInstanceStateChanged, this, &HyperAPI::handleInstanceStateChange); // pipe callbacks from subscriptions to parent - connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage); + connect(this, &CallbackAPI::SignalCallbackToClient, this, &HyperAPI::SignalCallbackJsonMessage); // notify hyperhdr about a jsonMessageForward if (_hyperhdr != nullptr) - connect(this, &JsonAPI::forwardJsonMessage, _hyperhdr, &HyperHdrInstance::forwardJsonMessage); + connect(this, &HyperAPI::SignalForwardJsonMessage, _hyperhdr.get(), &HyperHdrInstance::SignalForwardJsonMessage); } -bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced) +bool HyperAPI::handleInstanceSwitch(quint8 inst, bool forced) { - if (API::setHyperhdrInstance(inst)) - { + if (BaseAPI::setHyperhdrInstance(inst)) + { Debug(_log, "Client '%s' switch to HyperHDR instance %d", QSTRING_CSTR(_peerAddress), inst); - // the JsonCB creates json messages you can subscribe to e.g. data change events - _jsonCB->setSubscriptionsTo(_hyperhdr); return true; } return false; } -void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleColorCommand(const QJsonObject& message, const QString& command, int tan) { - emit forwardJsonMessage(message); + emit SignalForwardJsonMessage(message); int priority = message["priority"].toInt(); int duration = message["duration"].toInt(-1); const QString origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress; @@ -263,15 +249,15 @@ void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& comm colors.emplace_back(uint8_t(entry.toInt())); } - API::setColor(priority, colors, duration, origin); + BaseAPI::setColor(priority, colors, duration, origin); sendSuccessReply(command, tan); } -void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleImageCommand(const QJsonObject& message, const QString& command, int tan) { - emit forwardJsonMessage(message); + emit SignalForwardJsonMessage(message); - API::ImageCmdData idata; + BaseAPI::ImageCmdData idata; idata.priority = message["priority"].toInt(); idata.origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress; idata.duration = message["duration"].toInt(-1); @@ -283,7 +269,7 @@ void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& comm idata.data = QByteArray::fromBase64(QByteArray(message["imagedata"].toString().toUtf8())); QString replyMsg; - if (!API::setImage(idata, COMP_IMAGE, replyMsg)) + if (!BaseAPI::setImage(idata, COMP_IMAGE, replyMsg)) { sendErrorReply(replyMsg, command, tan); return; @@ -291,9 +277,9 @@ void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& comm sendSuccessReply(command, tan); } -void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleEffectCommand(const QJsonObject& message, const QString& command, int tan) { - emit forwardJsonMessage(message); + emit SignalForwardJsonMessage(message); EffectCmdData dat; dat.priority = message["priority"].toInt(); @@ -304,22 +290,22 @@ void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& com dat.data = message["imageData"].toString("").toUtf8(); dat.args = message["effect"].toObject()["args"].toObject(); - if (API::setEffect(dat)) + if (BaseAPI::setEffect(dat)) sendSuccessReply(command, tan); else sendErrorReply("Effect '" + dat.effectName + "' not found", command, tan); } -hyperhdr::Components JsonAPI::getActiveComponent() +hyperhdr::Components HyperAPI::getActiveComponent() { - PriorityMuxer::InputInfo prio; + hyperhdr::Components active; - SAFE_CALL_0_RET(_hyperhdr, getCurrentPriorityInfo, PriorityMuxer::InputInfo, prio); + SAFE_CALL_0_RET(_hyperhdr.get(), getCurrentPriorityActiveComponent, hyperhdr::Components, active); - return prio.componentId; + return active; } -void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleServerInfoCommand(const QJsonObject& message, const QString& command, int tan) { try { @@ -343,8 +329,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& // Instance report // ///////////////////// - SAFE_CALL_1_RET(_hyperhdr, getJsonInfo, QJsonObject, info, bool, true); - + BLOCK_CALL_2(_hyperhdr.get(), putJsonInfo, QJsonObject&, info, bool, true); /////////////////////////// // Available LED devices // @@ -360,38 +345,36 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& ledDevices["available"] = availableLedDevices; info["ledDevices"] = ledDevices; - /////////////////////// // Sound Device Info // /////////////////////// #if defined(ENABLE_SOUNDCAPLINUX) || defined(ENABLE_SOUNDCAPWINDOWS) || defined(ENABLE_SOUNDCAPMACOS) - if (SoundCapture::getInstance() != NULL) - { - QJsonObject resultSound; - auto soundInstance = SoundCapture::getInstance(); - SAFE_CALL_0_RET(soundInstance, getJsonInfo, QJsonObject, resultSound); + QJsonObject resultSound; + if (_soundCapture != nullptr) + SAFE_CALL_0_RET(_soundCapture.get(), getJsonInfo, QJsonObject, resultSound); + + if (!resultSound.isEmpty()) info["sound"] = resultSound; - } -#endif +#endif ///////////////////////// // System Grabber Info // ///////////////////////// #if defined(ENABLE_DX) || defined(ENABLE_MAC_SYSTEM) || defined(ENABLE_X11) || defined(ENABLE_FRAMEBUFFER) - if (SystemWrapper::getInstance() != nullptr) - { - QJsonObject systemGrabbers; - auto systemInstance = SystemWrapper::getInstance(); - SAFE_CALL_0_RET(systemInstance, getJsonInfo, QJsonObject, systemGrabbers); + QJsonObject resultSGrabber; + + if (_systemGrabber != nullptr && _systemGrabber->systemWrapper() != nullptr) + SAFE_CALL_0_RET(_systemGrabber->systemWrapper(), getJsonInfo, QJsonObject, resultSGrabber); + + if (!resultSGrabber.isEmpty()) + info["systemGrabbers"] = resultSGrabber; - info["systemGrabbers"] = systemGrabbers; - } #endif @@ -400,27 +383,15 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& ////////////////////// QJsonObject grabbers; + GrabberWrapper* grabberWrapper = (_videoGrabber != nullptr) ? _videoGrabber->grabberWrapper() : nullptr; #if defined(ENABLE_V4L2) || defined(ENABLE_MF) || defined(ENABLE_AVF) - if (GrabberWrapper::getInstance() != nullptr) - SAFE_CALL_0_RET((GrabberWrapper::getInstance()), getJsonInfo, QJsonObject, grabbers); + if (grabberWrapper != nullptr) + SAFE_CALL_0_RET(grabberWrapper, getJsonInfo, QJsonObject, grabbers); #endif info["grabbers"] = grabbers; - if (GrabberWrapper::getInstance() != nullptr) - { - info["videomodehdr"] = GrabberWrapper::getInstance()->getHdrToneMappingEnabled(); - } - else - { - if (FlatBufferServer::getInstance() != nullptr) - info["videomodehdr"] = FlatBufferServer::getInstance()->getHdrToneMappingEnabled(); - else - info["videomodehdr"] = 0; - } - - ////////////////////////////////// // Instances found by Bonjour // ////////////////////////////////// @@ -429,7 +400,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& #ifdef ENABLE_BONJOUR QList services; - SAFE_CALL_0_RET((DiscoveryWrapper::getInstance()), getAllServices, QList, services); + if (_discoveryWrapper != nullptr) + SAFE_CALL_0_RET(_discoveryWrapper.get(), getAllServices, QList, services); for (const auto& session : services) { @@ -449,7 +421,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& /////////////////////////// QJsonArray instanceInfo; - for (const auto& entry : API::getAllInstanceData()) + for (const auto& entry : BaseAPI::getAllInstanceData()) { QJsonObject obj; obj.insert("friendly_name", entry["friendly_name"].toString()); @@ -497,25 +469,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& if (_noListener) return; - QJsonArray subsArr = message["subscribe"].toArray(); - // catch the all keyword and build a list of all cmds - if (subsArr.contains("all")) - { - subsArr = QJsonArray(); - for (const auto& entry : _jsonCB->getCommands()) - { - subsArr.append(entry); - } - } - - for (const QJsonValueRef entry : subsArr) - { - // config callbacks just if auth is set - if ((entry == "settings-update" || entry == "token-update") && !API::isAdminAuthorized()) - continue; - // silent failure if a subscribe type is not found - _jsonCB->subscribeFor(entry.toString()); - } + CallbackAPI::subscribe(message["subscribe"].toArray()); } } catch (...) @@ -524,13 +478,13 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& } } -void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleClearCommand(const QJsonObject& message, const QString& command, int tan) { - emit forwardJsonMessage(message); + emit SignalForwardJsonMessage(message); int priority = message["priority"].toInt(); QString replyMsg; - if (!API::clearPriority(priority, replyMsg)) + if (!BaseAPI::clearPriority(priority, replyMsg)) { sendErrorReply(replyMsg, command, tan); return; @@ -538,15 +492,15 @@ void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& comm sendSuccessReply(command, tan); } -void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleClearallCommand(const QJsonObject& message, const QString& command, int tan) { - emit forwardJsonMessage(message); + emit SignalForwardJsonMessage(message); QString replyMsg; - API::clearPriority(-1, replyMsg); + BaseAPI::clearPriority(-1, replyMsg); sendSuccessReply(command, tan); } -void JsonAPI::handleHelpCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleHelpCommand(const QJsonObject& message, const QString& command, int tan) { QJsonObject req; @@ -554,41 +508,43 @@ void JsonAPI::handleHelpCommand(const QJsonObject& message, const QString& comma sendSuccessDataReply(QJsonDocument(req), command, tan); } -void JsonAPI::handleCropCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleCropCommand(const QJsonObject& message, const QString& command, int tan) { + GrabberWrapper* grabberWrapper = (_videoGrabber != nullptr) ? _videoGrabber->grabberWrapper() : nullptr; const QJsonObject& adjustment = message["crop"].toObject(); int l = adjustment["left"].toInt(0); int r = adjustment["right"].toInt(0); int t = adjustment["top"].toInt(0); int b = adjustment["bottom"].toInt(0); - if (GrabberWrapper::getInstance() != nullptr) - emit GrabberWrapper::getInstance()->setCropping(l, r, t, b); + if (grabberWrapper != nullptr) + emit grabberWrapper->setCropping(l, r, t, b); sendSuccessReply(command, tan); } -void JsonAPI::handleBenchmarkCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleBenchmarkCommand(const QJsonObject& message, const QString& command, int tan) { + GrabberWrapper* grabberWrapper = (_videoGrabber != nullptr) ? _videoGrabber->grabberWrapper() : nullptr; const QString& subc = message["subcommand"].toString().trimmed(); int status = message["status"].toInt(); - if (GrabberWrapper::getInstance() != nullptr) + if (grabberWrapper != nullptr) { if (subc == "ping") { - emit GrabberWrapper::getInstance()->benchmarkUpdate(status, "pong"); + emit grabberWrapper->SignalBenchmarkUpdate(status, "pong"); } else { - GrabberWrapper::getInstance()->benchmarkCapture(status, subc); + BLOCK_CALL_2(grabberWrapper, benchmarkCapture, int, status, QString, subc); } } sendSuccessReply(command, tan); } -void JsonAPI::lutDownloaded(QNetworkReply* reply, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time) +void HyperAPI::lutDownloaded(QNetworkReply* reply, int hardware_brightness, int hardware_contrast, int hardware_saturation, qint64 time) { QString fileName = QDir::cleanPath(_instanceManager->getRootPath() + QDir::separator() + "lut_lin_tables.3d"); QString error = installLut(reply, fileName, hardware_brightness, hardware_contrast, hardware_saturation, time); @@ -596,9 +552,18 @@ void JsonAPI::lutDownloaded(QNetworkReply* reply, int hardware_brightness, int h if (error == nullptr) { Info(_log, "Reloading LUT..."); - API::setVideoModeHdr(0); - API::setVideoModeHdr(1); - QTimer::singleShot(0, _hyperhdr, [=]() { _hyperhdr->saveGrabberParams(hardware_brightness, hardware_contrast, hardware_saturation); }); + BaseAPI::setVideoModeHdr(0); + BaseAPI::setVideoModeHdr(1); + + QJsonDocument newSet; + SAFE_CALL_1_RET(_hyperhdr.get(), getSetting, QJsonDocument, newSet, settings::type, settings::type::VIDEOGRABBER); + QJsonObject grabber = QJsonObject(newSet.object()); + grabber["hardware_brightness"] = hardware_brightness; + grabber["hardware_contrast"] = hardware_contrast; + grabber["hardware_saturation"] = hardware_saturation; + QString newConfig = QJsonDocument(grabber).toJson(QJsonDocument::Compact); + BLOCK_CALL_2(_hyperhdr.get(), setSetting, settings::type, settings::type::VIDEOGRABBER, QString, newConfig); + Info(_log, "New LUT has been installed as: %s (from: %s)", QSTRING_CSTR(fileName), QSTRING_CSTR(reply->url().toString())); } else @@ -606,16 +571,13 @@ void JsonAPI::lutDownloaded(QNetworkReply* reply, int hardware_brightness, int h Error(_log, "Error occured while installing new LUT: %s", QSTRING_CSTR(error)); } - reply->manager()->deleteLater(); - reply->deleteLater(); - QJsonObject report; report["status"] = (error == nullptr) ? 1 : 0; report["error"] = error; - _jsonCB->handleLutInstallUpdate(report); + sendSuccessDataReply(QJsonDocument(report), "lut-install-update"); } -void JsonAPI::handleLutInstallCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleLutInstallCommand(const QJsonObject& message, const QString& command, int tan) { const QString& address = QString("%1/lut_lin_tables.3d.xz").arg(message["subcommand"].toString().trimmed()); int hardware_brightness = message["hardware_brightness"].toInt(0); @@ -628,8 +590,11 @@ void JsonAPI::handleLutInstallCommand(const QJsonObject& message, const QString& if (_adminAuthorized) { QNetworkAccessManager* mgr = new QNetworkAccessManager(this); - connect(mgr, &QNetworkAccessManager::finished, this, [=](QNetworkReply* reply) { - lutDownloaded(reply, hardware_brightness, hardware_contrast, hardware_saturation, time); + connect(mgr, &QNetworkAccessManager::finished, this, + [this, mgr, hardware_brightness, hardware_contrast, hardware_saturation, time](QNetworkReply* reply) { + lutDownloaded(reply, hardware_brightness, hardware_contrast, hardware_saturation, time); + reply->deleteLater(); + mgr->deleteLater(); }); QNetworkRequest request(address); mgr->get(request); @@ -639,34 +604,34 @@ void JsonAPI::handleLutInstallCommand(const QJsonObject& message, const QString& sendErrorReply("No Authorization", command, tan); } -void JsonAPI::handleCurrentStateCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleCurrentStateCommand(const QJsonObject& message, const QString& command, int tan) { const QString& subc = message["subcommand"].toString().trimmed(); int instance = message["instance"].toInt(0); if (subc == "average-color") { - QJsonObject avColor = API::getAverageColor(instance); + QJsonObject avColor = BaseAPI::getAverageColor(instance); sendSuccessDataReply(QJsonDocument(avColor), command + "-" + subc, tan); } else handleNotImplemented(command, tan); } -void JsonAPI::handleSmoothingCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleSmoothingCommand(const QJsonObject& message, const QString& command, int tan) { const QString& subc = message["subcommand"].toString().trimmed().toLower(); int time = message["time"].toInt(); if (subc=="all") - QTimer::singleShot(0, _instanceManager, [=]() { _instanceManager->setSmoothing(time); }); + QUEUE_CALL_1(_instanceManager.get(), setSmoothing, int, time) else - QTimer::singleShot(0, _hyperhdr, [=]() { _hyperhdr->setSmoothing(time); }); + QUEUE_CALL_1(_hyperhdr.get(), setSmoothing, int, time); sendSuccessReply(command, tan); } -void JsonAPI::handleVideoControlsCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleVideoControlsCommand(const QJsonObject& message, const QString& command, int tan) { #if defined(__APPLE__) @@ -680,21 +645,25 @@ void JsonAPI::handleVideoControlsCommand(const QJsonObject& message, const QStri int hardware_saturation = adjustment["hardware_saturation"].toInt(); int hardware_hue = adjustment["hardware_hue"].toInt(); - if (GrabberWrapper::getInstance() != nullptr) - emit GrabberWrapper::getInstance()->setBrightnessContrastSaturationHue(hardware_brightness, hardware_contrast, hardware_saturation, hardware_hue); + GrabberWrapper* grabberWrapper = (_videoGrabber != nullptr) ? _videoGrabber->grabberWrapper() : nullptr; + + if (grabberWrapper != nullptr) + { + QUEUE_CALL_4(grabberWrapper, setBrightnessContrastSaturationHue, int, hardware_brightness, int, hardware_contrast, int, hardware_saturation, int, hardware_hue); + } sendSuccessReply(command, tan); } -void JsonAPI::handleSourceSelectCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleSourceSelectCommand(const QJsonObject& message, const QString& command, int tan) { if (message.contains("auto")) { - API::setSourceAutoSelect(message["auto"].toBool(false)); + BaseAPI::setSourceAutoSelect(message["auto"].toBool(false)); } else if (message.contains("priority")) { - API::setVisiblePriority(message["priority"].toInt()); + BaseAPI::setVisiblePriority(message["priority"].toInt()); } else { @@ -704,13 +673,13 @@ void JsonAPI::handleSourceSelectCommand(const QJsonObject& message, const QStrin sendSuccessReply(command, tan); } -void JsonAPI::handleSaveDB(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleSaveDB(const QJsonObject& message, const QString& command, int tan) { if (_adminAuthorized) { QJsonObject backup; - SAFE_CALL_0_RET(_instanceManager, getBackup, QJsonObject, backup); + SAFE_CALL_0_RET(_instanceManager.get(), getBackup, QJsonObject, backup); if (!backup.empty()) sendSuccessDataReply(QJsonDocument(backup), command, tan); @@ -721,20 +690,23 @@ void JsonAPI::handleSaveDB(const QJsonObject& message, const QString& command, i sendErrorReply("No Authorization", command, tan); } -void JsonAPI::handleLoadDB(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleLoadDB(const QJsonObject& message, const QString& command, int tan) { if (_adminAuthorized) { QString error; - SAFE_CALL_1_RET(_instanceManager, restoreBackup, QString, error, QJsonObject, message); + SAFE_CALL_1_RET(_instanceManager.get(), restoreBackup, QString, error, QJsonObject, message); if (error.isEmpty()) { #ifdef __linux__ - raise(SIGSEGV); + Info(_log, "Exiting now. If HyperHDR is running as a service, systemd should restart the process."); + HyperHdrInstance::signalTerminateTriggered(); + QTimer::singleShot(0, _instanceManager.get(), []() {QCoreApplication::exit(1); }); #else - QCoreApplication::quit(); + HyperHdrInstance::signalTerminateTriggered(); + QTimer::singleShot(0, _instanceManager.get(), []() {QCoreApplication::quit(); }); #endif } else @@ -744,32 +716,33 @@ void JsonAPI::handleLoadDB(const QJsonObject& message, const QString& command, i sendErrorReply("No Authorization", command, tan); } -void JsonAPI::handlePerformanceCounters(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handlePerformanceCounters(const QJsonObject& message, const QString& command, int tan) { QString subcommand = message["subcommand"].toString(""); QString full_command = command + "-" + subcommand; if (subcommand == "all") { - emit PerformanceCounters::getInstance()->performanceInfoRequest(true); + QUEUE_CALL_1(_performanceCounters.get(), performanceInfoRequest, bool, true); sendSuccessReply(command, tan); } else if (subcommand == "resources") { - emit PerformanceCounters::getInstance()->performanceInfoRequest(false); + QUEUE_CALL_1(_performanceCounters.get(), performanceInfoRequest, bool, false); sendSuccessReply(command, tan); } else sendErrorReply("Unknown subcommand", command, tan); } -void JsonAPI::handleLoadSignalCalibration(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleLoadSignalCalibration(const QJsonObject& message, const QString& command, int tan) { QJsonDocument retVal; QString subcommand = message["subcommand"].toString(""); QString full_command = command + "-" + subcommand; + GrabberWrapper* grabberWrapper = (_videoGrabber != nullptr) ? _videoGrabber->grabberWrapper() : nullptr; - if (GrabberWrapper::getInstance() == nullptr) + if (grabberWrapper == nullptr) { sendErrorReply("No grabbers available", command, tan); return; @@ -779,7 +752,7 @@ void JsonAPI::handleLoadSignalCalibration(const QJsonObject& message, const QStr { if (_adminAuthorized) { - SAFE_CALL_0_RET((GrabberWrapper::getInstance()), startCalibration, QJsonDocument, retVal); + SAFE_CALL_0_RET(grabberWrapper, startCalibration, QJsonDocument, retVal); sendSuccessDataReply(retVal, full_command, tan); } else @@ -787,19 +760,19 @@ void JsonAPI::handleLoadSignalCalibration(const QJsonObject& message, const QStr } else if (subcommand == "stop") { - SAFE_CALL_0_RET((GrabberWrapper::getInstance()), stopCalibration, QJsonDocument, retVal); + SAFE_CALL_0_RET(grabberWrapper, stopCalibration, QJsonDocument, retVal); sendSuccessDataReply(retVal, full_command, tan); } else if (subcommand == "get-info") { - SAFE_CALL_0_RET((GrabberWrapper::getInstance()), getCalibrationInfo, QJsonDocument, retVal); + SAFE_CALL_0_RET(grabberWrapper, getCalibrationInfo, QJsonDocument, retVal); sendSuccessDataReply(retVal, full_command, tan); } else sendErrorReply("Unknown subcommand", command, tan); } -void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleConfigCommand(const QJsonObject& message, const QString& command, int tan) { QString subcommand = message["subcommand"].toString(""); QString full_command = command + "-" + subcommand; @@ -818,7 +791,11 @@ void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& com else if (subcommand == "getconfig") { if (_adminAuthorized) - sendSuccessDataReply(QJsonDocument(_hyperhdr->getQJsonConfig()), full_command, tan); + { + QJsonObject getconfig; + BLOCK_CALL_1(_hyperhdr.get(), putJsonConfig, QJsonObject&, getconfig); + sendSuccessDataReply(QJsonDocument(getconfig), full_command, tan); + } else sendErrorReply("No Authorization", command, tan); } @@ -828,14 +805,14 @@ void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& com } } -void JsonAPI::handleConfigSetCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleConfigSetCommand(const QJsonObject& message, const QString& command, int tan) { if (message.contains("config")) { QJsonObject config = message["config"].toObject(); - if (API::isHyperhdrEnabled()) + if (BaseAPI::isHyperhdrEnabled()) { - if (API::saveSettings(config)) + if (BaseAPI::saveSettings(config)) { sendSuccessReply(command, tan); } @@ -849,7 +826,7 @@ void JsonAPI::handleConfigSetCommand(const QJsonObject& message, const QString& } } -void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleSchemaGetCommand(const QJsonObject& message, const QString& command, int tan) { // create result QJsonObject schemaJson, alldevices, properties; @@ -875,32 +852,20 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString& properties.insert("alldevices", alldevices); // collect all available effect schemas - QJsonObject pyEffectSchemas, pyEffectSchema; - QJsonArray in, ex; - - - if (!in.empty()) - pyEffectSchema.insert("internal", in); - if (!ex.empty()) - pyEffectSchema.insert("external", ex); - - pyEffectSchemas = pyEffectSchema; - properties.insert("effectSchemas", pyEffectSchemas); - schemaJson.insert("properties", properties); // send the result sendSuccessDataReply(QJsonDocument(schemaJson), command, tan); } -void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleComponentStateCommand(const QJsonObject& message, const QString& command, int tan) { const QJsonObject& componentState = message["componentstate"].toObject(); QString comp = componentState["component"].toString("invalid"); bool compState = componentState["state"].toBool(true); QString replyMsg; - if (!API::setComponentState(comp, compState, replyMsg)) + if (!BaseAPI::setComponentState(comp, compState, replyMsg)) { sendErrorReply(replyMsg, command, tan); return; @@ -908,7 +873,7 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QStr sendSuccessReply(command, tan); } -void JsonAPI::handleLedColorsIncoming(const std::vector& ledValues) +void HyperAPI::handleIncomingColors(const std::vector& ledValues) { _currentLedValues = ledValues; @@ -916,12 +881,12 @@ void JsonAPI::handleLedColorsIncoming(const std::vector& ledValues) _ledStreamTimer->start(_colorsStreamingInterval); } -void JsonAPI::handleLedColorsTimer() +void HyperAPI::handleLedColorsTimer() { emit streamLedcolorsUpdate(_currentLedValues); } -void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleLedColorsCommand(const QJsonObject& message, const QString& command, int tan) { // create result QString subcommand = message["subcommand"].toString(""); @@ -935,31 +900,34 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString& _streaming_leds_reply["command"] = command + "-ledstream-update"; _streaming_leds_reply["tan"] = tan; - connect(_hyperhdr, &HyperHdrInstance::rawLedColors, this, &JsonAPI::handleLedColorsIncoming, Qt::UniqueConnection); + subscribeFor("leds-colors"); if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != _colorsStreamingInterval) _ledStreamTimer->start(_colorsStreamingInterval); - QUEUE_CALL_0(_hyperhdr, requestForColors); + QUEUE_CALL_0(_hyperhdr.get(), update); } else if (subcommand == "ledstream-stop") { - disconnect(_hyperhdr, &HyperHdrInstance::rawLedColors, this, 0); + subscribeFor("leds-colors", true); _ledStreamTimer->stop(); } else if (subcommand == "imagestream-start") { - _streaming_image_reply["success"] = true; - _streaming_image_reply["command"] = command + "-imagestream-update"; - _streaming_image_reply["tan"] = tan; - - connect(_hyperhdr, &HyperHdrInstance::onCurrentImage, this, &JsonAPI::setImage, Qt::UniqueConnection); + if (BaseAPI::isAdminAuthorized()) + { + _streaming_image_reply["success"] = true; + _streaming_image_reply["command"] = command + "-imagestream-update"; + _streaming_image_reply["tan"] = tan; - emit _hyperhdr->onCurrentImage(); + subscribeFor("live-video"); + } + else + sendErrorReply("No Authorization", command, tan); } else if (subcommand == "imagestream-stop") { - disconnect(_hyperhdr, &HyperHdrInstance::onCurrentImage, this, 0); + subscribeFor("live-video", true); } else { @@ -969,12 +937,12 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString& sendSuccessReply(command + "-" + subcommand, tan); } -void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleLoggingCommand(const QJsonObject& message, const QString& command, int tan) { // create result QString subcommand = message["subcommand"].toString(""); - if (API::isAdminAuthorized()) + if (BaseAPI::isAdminAuthorized()) { _streaming_logging_reply["success"] = true; _streaming_logging_reply["command"] = command; @@ -985,7 +953,7 @@ void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString& co if (!_streaming_logging_activated) { _streaming_logging_reply["command"] = command + "-update"; - connect(LoggerManager::getInstance(), &LoggerManager::newLogMessage, this, &JsonAPI::incommingLogMessage); + connect(_logsManager.get(), &LoggerManager::newLogMessage, this, &HyperAPI::incommingLogMessage); Debug(_log, "log streaming activated for client %s", _peerAddress.toStdString().c_str()); // needed to trigger log sending } } @@ -993,7 +961,7 @@ void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString& co { if (_streaming_logging_activated) { - disconnect(LoggerManager::getInstance(), &LoggerManager::newLogMessage, this, &JsonAPI::incommingLogMessage); + disconnect(_logsManager.get(), &LoggerManager::newLogMessage, this, &HyperAPI::incommingLogMessage); _streaming_logging_activated = false; Debug(_log, "log streaming deactivated for client %s", _peerAddress.toStdString().c_str()); } @@ -1011,26 +979,33 @@ void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString& co } } -void JsonAPI::handleProcessingCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleProcessingCommand(const QJsonObject& message, const QString& command, int tan) { - API::setLedMappingType(ImageProcessor::mappingTypeToInt(message["mappingType"].toString("multicolor_mean"))); + BaseAPI::setLedMappingType(ImageToLedManager::mappingTypeToInt(message["mappingType"].toString("multicolor_mean"))); sendSuccessReply(command, tan); } -void JsonAPI::handleVideoModeHdrCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleVideoModeHdrCommand(const QJsonObject& message, const QString& command, int tan) { if (message.contains("flatbuffers_user_lut_filename")) { - API::setFlatbufferUserLUT(message["flatbuffers_user_lut_filename"].toString("")); + BaseAPI::setFlatbufferUserLUT(message["flatbuffers_user_lut_filename"].toString("")); } - API::setVideoModeHdr(message["HDR"].toInt()); + BaseAPI::setVideoModeHdr(message["HDR"].toInt()); sendSuccessReply(command, tan); } -void JsonAPI::handleLutCalibrationCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleLutCalibrationCommand(const QJsonObject& message, const QString& command, int tan) { QString subcommand = message["subcommand"].toString(""); + + if (_lutCalibrator == nullptr) + { + sendErrorReply("Please refresh the page and start again", command + "-" + subcommand, tan); + return; + } + int checksum = message["checksum"].toInt(-1); QJsonObject startColor = message["startColor"].toObject(); QJsonObject endColor = message["endColor"].toObject(); @@ -1050,15 +1025,15 @@ void JsonAPI::handleLutCalibrationCommand(const QJsonObject& message, const QStr _endColor.green = endColor["g"].toInt(255); _endColor.blue = endColor["b"].toInt(255); + sendSuccessReply(command, tan); + if (subcommand == "capture") - emit LutCalibrator::getInstance()->assign(getActiveComponent(), checksum, _startColor, _endColor, limitedRange, saturation, luminance, gammaR, gammaG, gammaB, coef); + _lutCalibrator->incomingCommand(_instanceManager->getRootPath(), _videoGrabber->grabberWrapper(), getActiveComponent(), checksum, _startColor, _endColor, limitedRange, saturation, luminance, gammaR, gammaG, gammaB, coef); else - emit LutCalibrator::getInstance()->stop(); - - sendSuccessReply(command, tan); + _lutCalibrator->stopHandler(); } -void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleInstanceCommand(const QJsonObject& message, const QString& command, int tan) { const QString& subc = message["subcommand"].toString(); const quint8& inst = message["instance"].toInt(); @@ -1079,8 +1054,9 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c if (subc == "startInstance") { - connect(this, &API::onStartInstanceResponse, [=](const int& tan) { sendSuccessReply(command + "-" + subc, tan); }); - if (!API::startInstance(inst, tan)) + connect(this, &BaseAPI::SignalInstanceStartedClientNotification, + this, [this, command, subc](const int& tan) { sendSuccessReply(command + "-" + subc, tan); }); + if (!BaseAPI::startInstance(inst, tan)) sendErrorReply("Can't start HyperHDR instance index " + QString::number(inst), command + "-" + subc, tan); return; } @@ -1088,7 +1064,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c if (subc == "stopInstance") { // silent fail - API::stopInstance(inst); + BaseAPI::stopInstance(inst); sendSuccessReply(command + "-" + subc, tan); return; } @@ -1096,7 +1072,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c if (subc == "deleteInstance") { QString replyMsg; - if (API::deleteInstance(inst, replyMsg)) + if (BaseAPI::deleteInstance(inst, replyMsg)) sendSuccessReply(command + "-" + subc, tan); else sendErrorReply(replyMsg, command + "-" + subc, tan); @@ -1109,7 +1085,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c if (subc == "createInstance") { - QString replyMsg = API::createInstance(name); + QString replyMsg = BaseAPI::createInstance(name); if (replyMsg.isEmpty()) sendSuccessReply(command + "-" + subc, tan); else @@ -1119,7 +1095,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c if (subc == "saveName") { - QString replyMsg = API::setInstanceName(inst, name); + QString replyMsg = BaseAPI::setInstanceName(inst, name); if (replyMsg.isEmpty()) sendSuccessReply(command + "-" + subc, tan); else @@ -1128,7 +1104,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c } } -void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleLedDeviceCommand(const QJsonObject& message, const QString& command, int tan) { Debug(_log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData()); @@ -1146,11 +1122,11 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString& */ { QJsonObject config; config.insert("type", devType); - LedDevice* ledDevice = nullptr; + std::unique_ptr ledDevice; if (subc == "discover") { - ledDevice = LedDeviceFactory::construct(config); + ledDevice = std::unique_ptr(LedDeviceFactory::construct(config)); const QJsonObject& params = message["params"].toObject(); const QJsonObject devicesDiscovered = ledDevice->discover(params); @@ -1160,7 +1136,7 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString& } else if (subc == "getProperties") { - ledDevice = LedDeviceFactory::construct(config); + ledDevice = std::unique_ptr(LedDeviceFactory::construct(config)); const QJsonObject& params = message["params"].toObject(); const QJsonObject deviceProperties = ledDevice->getProperties(params); @@ -1174,26 +1150,32 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString& if (devType == "philipshuev2" || devType == "blink") { - QUEUE_CALL_1(_hyperhdr, identifyLed, QJsonObject, params); + QUEUE_CALL_1(_hyperhdr.get(), identifyLed, QJsonObject, params); } else { - ledDevice = LedDeviceFactory::construct(config); + ledDevice = std::unique_ptr(LedDeviceFactory::construct(config)); ledDevice->identify(params); } sendSuccessReply(full_command, tan); } + else if (subc == "hasLedClock") + { + int hasLedClock = 0; + QJsonObject ret; + SAFE_CALL_0_RET(_hyperhdr.get(), hasLedClock, int, hasLedClock); + ret["hasLedClock"] = hasLedClock; + sendSuccessDataReply(QJsonDocument(ret), "hasLedClock-update", tan); + } else { sendErrorReply("Unknown or missing subcommand", full_command, tan); - } - - delete ledDevice; + } } } -void JsonAPI::streamLedcolorsUpdate(const std::vector& ledColors) +void HyperAPI::streamLedcolorsUpdate(const std::vector& ledColors) { QJsonObject result; QJsonArray leds; @@ -1207,57 +1189,33 @@ void JsonAPI::streamLedcolorsUpdate(const std::vector& ledColors) _streaming_leds_reply["result"] = result; // send the result - emit callbackMessage(_streaming_leds_reply); + emit SignalCallbackJsonMessage(_streaming_leds_reply); } -void JsonAPI::setImage() +void HyperAPI::handlerInstanceImageUpdated(const Image& image) { - uint64_t _currentTime = InternalClock::now(); - - if (!_semaphore.tryAcquire() && (_lastSendImage < _currentTime && (_currentTime - _lastSendImage < 2000))) - return; - - _lastSendImage = _currentTime; + _liveImage = image; + QUEUE_CALL_0(this, sendImage); +} - const PriorityMuxer* muxer = this->_hyperhdr->getMuxerInstance(); - const int priority = muxer->getCurrentPriority(); - const PriorityMuxer::InputInfo& priorityInfo = muxer->getInputInfo(priority); - const Image& image = priorityInfo.image; +void HyperAPI::sendImage() +{ + qint64 _currentTime = InternalClock::now(); + int timeLimit = (_liveImage.width() > 1280) ? 60 : 30; - if (image.width() <= 1 || image.height() <= 1) + if ((_liveImage.width() <= 1 || _liveImage.height() <= 1) || + (_lastSentImage + timeLimit > _currentTime && _lastSentImage + 100 < _currentTime)) { - if (_semaphore.available() == 0) - _semaphore.release(); return; } - QImage jpgImage((const uchar*)image.rawMem(), image.width(), image.height() / 2, 6 * image.width(), QImage::Format_RGB888); - QByteArray ba; - QBuffer buffer(&ba); - buffer.open(QIODevice::WriteOnly); - - if (image.width() > 1920) - { - jpgImage = jpgImage.scaled(image.width() / 2, image.height() / 2); - } - - jpgImage.save(&buffer, "jpg"); - - QJsonObject result; - result["image"] = "data:image/jpg;base64," + QString(ba.toBase64()); - _streaming_image_reply["result"] = result; - _streaming_image_reply["isImage"] = 1; + emit SignalCallbackBinaryImageMessage(_liveImage); - emit callbackMessage(_streaming_image_reply); + _liveImage = Image(); + _lastSentImage = _currentTime; } -void JsonAPI::releaseLock() -{ - if (_semaphore.available() == 0) - _semaphore.release(); -} - -void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg) +void HyperAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg) { QJsonObject result, message; QJsonArray messageArray; @@ -1265,24 +1223,10 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg) if (!_streaming_logging_activated) { _streaming_logging_activated = true; - const QList* logBuffer = LoggerManager::getInstance()->getLogMessageBuffer(); - for (int i = 0; i < logBuffer->length(); i++) - { - message["appName"] = logBuffer->at(i).appName; - message["loggerName"] = logBuffer->at(i).loggerName; - message["function"] = logBuffer->at(i).function; - message["line"] = QString::number(logBuffer->at(i).line); - message["fileName"] = logBuffer->at(i).fileName; - message["message"] = logBuffer->at(i).message; - message["levelString"] = logBuffer->at(i).levelString; - message["utime"] = QString::number(logBuffer->at(i).utime); - - messageArray.append(message); - } + SAFE_CALL_0_RET(_logsManager.get(), getLogMessageBuffer, QJsonArray, messageArray); } else { - message["appName"] = msg.appName; message["loggerName"] = msg.loggerName; message["function"] = msg.function; message["line"] = QString::number(msg.line); @@ -1298,10 +1242,10 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg) _streaming_logging_reply["result"] = result; // send the result - emit callbackMessage(_streaming_logging_reply); + emit SignalCallbackJsonMessage(_streaming_logging_reply); } -void JsonAPI::newPendingTokenRequest(const QString& id, const QString& comment) +void HyperAPI::newPendingTokenRequest(const QString& id, const QString& comment) { QJsonObject obj; obj["comment"] = comment; @@ -1311,7 +1255,7 @@ void JsonAPI::newPendingTokenRequest(const QString& id, const QString& comment) sendSuccessDataReply(QJsonDocument(obj), "authorize-tokenRequest", 1); } -void JsonAPI::handleTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan) +void HyperAPI::handleTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan) { const QString cmd = "authorize-requestToken"; QJsonObject result; @@ -1325,17 +1269,11 @@ void JsonAPI::handleTokenResponse(bool success, const QString& token, const QStr sendErrorReply("Token request timeout or denied", cmd, tan); } -void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name) +void HyperAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name) { switch (state) { - case InstanceState::H_PRE_DELETE: - if (getCurrentInstanceIndex() == instance) - { - _jsonCB->setSubscriptionsTo(nullptr); - } - break; - case InstanceState::H_ON_STOP: + case InstanceState::STOP: if (getCurrentInstanceIndex() == instance) { handleInstanceSwitch(); @@ -1346,17 +1284,17 @@ void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, co } } -void JsonAPI::stopDataConnections() +void HyperAPI::stopDataConnections() { LoggerManager::getInstance()->disconnect(); _streaming_logging_activated = false; - _jsonCB->resetSubscriptions(); + CallbackAPI::removeSubscriptions(); // led stream colors - disconnect(_hyperhdr, &HyperHdrInstance::rawLedColors, this, 0); + disconnect(_hyperhdr.get(), &HyperHdrInstance::SignalRawColorsChanged, this, 0); _ledStreamTimer->stop(); } -void JsonAPI::handleTunnel(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleTunnel(const QJsonObject& message, const QString& command, int tan) { const QString& subcommand = message["subcommand"].toString().trimmed(); const QString& full_command = command + "-" + subcommand; @@ -1406,6 +1344,9 @@ void JsonAPI::handleTunnel(const QJsonObject& message, const QString& command, i } } + if (subcommand != "get") + provider.addHeader("Content-Type", "application/json"); + if (subcommand == "put") result = provider.put(url, data); else if (subcommand == "post") @@ -1429,7 +1370,7 @@ void JsonAPI::handleTunnel(const QJsonObject& message, const QString& command, i else reply["info"] = doc.object(); - emit callbackMessage(reply); + emit SignalCallbackJsonMessage(reply); } else sendErrorReply("Service not supported", full_command, tan); @@ -1438,7 +1379,7 @@ void JsonAPI::handleTunnel(const QJsonObject& message, const QString& command, i sendErrorReply("No Authorization", full_command, tan); } -bool JsonAPI::isLocal(QString hostname) +bool HyperAPI::isLocal(QString hostname) { QHostAddress address(hostname); @@ -1483,7 +1424,7 @@ bool JsonAPI::isLocal(QString hostname) return false; } -void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, int tan) +void HyperAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, int tan) { // create result QJsonObject result; @@ -1492,23 +1433,8 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, i result["command"] = command; result["tan"] = tan; - SysInfo::HyperhdrSysInfo data = SysInfo::get(); QJsonObject system; - system["kernelType"] = data.kernelType; - system["kernelVersion"] = data.kernelVersion; - system["architecture"] = data.architecture; - system["cpuModelName"] = data.cpuModelName; - system["cpuModelType"] = data.cpuModelType; - system["cpuHardware"] = data.cpuHardware; - system["cpuRevision"] = data.cpuRevision; - system["wordSize"] = data.wordSize; - system["productType"] = data.productType; - system["productVersion"] = data.productVersion; - system["prettyName"] = data.prettyName; - system["hostName"] = data.hostName; - system["domainName"] = data.domainName; - system["qtVersion"] = data.qtVersion; - system["pyVersion"] = data.pyVersion; + putSystemInfo(system); info["system"] = system; QJsonObject hyperhdr; @@ -1516,134 +1442,29 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, i hyperhdr["build"] = QString(HYPERHDR_BUILD_ID); hyperhdr["gitremote"] = QString(HYPERHDR_GIT_REMOTE); hyperhdr["time"] = QString(__DATE__ " " __TIME__); - hyperhdr["id"] = _authManager->getID(); + hyperhdr["id"] = _accessManager->getID(); bool readOnly = true; - SAFE_CALL_0_RET(_hyperhdr, getReadOnlyMode, bool, readOnly); + SAFE_CALL_0_RET(_hyperhdr.get(), getReadOnlyMode, bool, readOnly); hyperhdr["readOnlyMode"] = readOnly; info["hyperhdr"] = hyperhdr; // send the result result["info"] = info; - emit callbackMessage(result); + emit SignalCallbackJsonMessage(result); } -void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleAdjustmentCommand(const QJsonObject& message, const QString& command, int tan) { const QJsonObject& adjustment = message["adjustment"].toObject(); - const QString adjustmentId = adjustment["id"].toString(_hyperhdr->getAdjustmentIds().first()); - ColorAdjustment* colorAdjustment = _hyperhdr->getAdjustment(adjustmentId); - if (colorAdjustment == nullptr) - { - Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str()); - return; - } - - if (adjustment.contains("classic_config")) - { - colorAdjustment->_rgbTransform.setClassicConfig(adjustment["classic_config"].toBool(false)); - } - - if (adjustment.contains("temperatureRed")) - { - colorAdjustment->_rgbRedAdjustment.setCorrection(adjustment["temperatureRed"].toInt()); - } - - if (adjustment.contains("temperatureGreen")) - { - colorAdjustment->_rgbGreenAdjustment.setCorrection(adjustment["temperatureGreen"].toInt()); - } - - if (adjustment.contains("temperatureBlue")) - { - colorAdjustment->_rgbBlueAdjustment.setCorrection(adjustment["temperatureBlue"].toInt()); - } - - if (adjustment.contains("red")) - { - const QJsonArray& values = adjustment["red"].toArray(); - colorAdjustment->_rgbRedAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); - } - - if (adjustment.contains("green")) - { - const QJsonArray& values = adjustment["green"].toArray(); - colorAdjustment->_rgbGreenAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); - } - - if (adjustment.contains("blue")) - { - const QJsonArray& values = adjustment["blue"].toArray(); - colorAdjustment->_rgbBlueAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); - } - if (adjustment.contains("cyan")) - { - const QJsonArray& values = adjustment["cyan"].toArray(); - colorAdjustment->_rgbCyanAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); - } - if (adjustment.contains("magenta")) - { - const QJsonArray& values = adjustment["magenta"].toArray(); - colorAdjustment->_rgbMagentaAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); - } - if (adjustment.contains("yellow")) - { - const QJsonArray& values = adjustment["yellow"].toArray(); - colorAdjustment->_rgbYellowAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); - } - if (adjustment.contains("white")) - { - const QJsonArray& values = adjustment["white"].toArray(); - colorAdjustment->_rgbWhiteAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); - } - - if (adjustment.contains("gammaRed")) - { - colorAdjustment->_rgbTransform.setGamma(adjustment["gammaRed"].toDouble(), colorAdjustment->_rgbTransform.getGammaG(), colorAdjustment->_rgbTransform.getGammaB()); - } - if (adjustment.contains("gammaGreen")) - { - colorAdjustment->_rgbTransform.setGamma(colorAdjustment->_rgbTransform.getGammaR(), adjustment["gammaGreen"].toDouble(), colorAdjustment->_rgbTransform.getGammaB()); - } - if (adjustment.contains("gammaBlue")) - { - colorAdjustment->_rgbTransform.setGamma(colorAdjustment->_rgbTransform.getGammaR(), colorAdjustment->_rgbTransform.getGammaG(), adjustment["gammaBlue"].toDouble()); - } - - if (adjustment.contains("backlightThreshold")) - { - colorAdjustment->_rgbTransform.setBacklightThreshold(adjustment["backlightThreshold"].toDouble()); - } - if (adjustment.contains("backlightColored")) - { - colorAdjustment->_rgbTransform.setBacklightColored(adjustment["backlightColored"].toBool()); - } - if (adjustment.contains("brightness")) - { - colorAdjustment->_rgbTransform.setBrightness(adjustment["brightness"].toInt()); - } - if (adjustment.contains("brightnessCompensation")) - { - colorAdjustment->_rgbTransform.setBrightnessCompensation(adjustment["brightnessCompensation"].toInt()); - } - if (adjustment.contains("saturationGain")) - { - colorAdjustment->_rgbTransform.setSaturationGain(adjustment["saturationGain"].toDouble()); - } - if (adjustment.contains("luminanceGain")) - { - colorAdjustment->_rgbTransform.setLuminanceGain(adjustment["luminanceGain"].toDouble()); - } - - // commit the changes - BLOCK_CALL_0(_hyperhdr, adjustmentsUpdated); + QUEUE_CALL_1(_hyperhdr.get(), updateAdjustments, QJsonObject, adjustment); sendSuccessReply(command, tan); } -void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& command, int tan) +void HyperAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& command, int tan) { const QString& subc = message["subcommand"].toString().trimmed(); const QString& id = message["id"].toString().trimmed(); @@ -1655,7 +1476,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& if (subc == "tokenRequired") { QJsonObject req; - req["required"] = !API::isAuthorized(); + req["required"] = !BaseAPI::isAuthorized(); sendSuccessDataReply(QJsonDocument(req), command + "-" + subc, tan); return; @@ -1665,7 +1486,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& if (subc == "adminRequired") { QJsonObject req; - req["adminRequired"] = !API::isAdminAuthorized(); + req["adminRequired"] = !BaseAPI::isAdminAuthorized(); sendSuccessDataReply(QJsonDocument(req), command + "-" + subc, tan); return; } @@ -1674,7 +1495,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& if (subc == "newPasswordRequired") { QJsonObject req; - req["newPasswordRequired"] = API::hasHyperhdrDefaultPw(); + req["newPasswordRequired"] = BaseAPI::hasHyperhdrDefaultPw(); sendSuccessDataReply(QJsonDocument(req), command + "-" + subc, tan); return; } @@ -1682,9 +1503,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& // catch logout if (subc == "logout") { - // disconnect all kind of data callbacks - JsonAPI::stopDataConnections(); // TODO move to API - API::logout(); + BaseAPI::logout(); sendSuccessReply(command + "-" + subc, tan); return; } @@ -1693,9 +1512,9 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& if (subc == "newPassword") { // use password, newPassword - if (API::isAdminAuthorized()) + if (BaseAPI::isAdminAuthorized()) { - if (API::updateHyperhdrPassword(password, newPassword)) + if (BaseAPI::updateHyperhdrPassword(password, newPassword)) { sendSuccessReply(command + "-" + subc, tan); return; @@ -1712,8 +1531,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& { // use comment // for user authorized sessions - AuthManager::AuthDefinition def; - const QString res = API::createToken(comment, def); + AccessManager::AuthDefinition def; + const QString res = BaseAPI::createToken(comment, def); if (res.isEmpty()) { QJsonObject newTok; @@ -1732,7 +1551,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& if (subc == "renameToken") { // use id/comment - const QString res = API::renameToken(id, comment); + const QString res = BaseAPI::renameToken(id, comment); if (res.isEmpty()) { sendSuccessReply(command + "-" + subc, tan); @@ -1746,7 +1565,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& if (subc == "deleteToken") { // use id - const QString res = API::deleteToken(id); + const QString res = BaseAPI::deleteToken(id); if (res.isEmpty()) { sendSuccessReply(command + "-" + subc, tan); @@ -1763,9 +1582,9 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& const QString& comment = message["comment"].toString().trimmed(); const bool& acc = message["accept"].toBool(true); if (acc) - API::setNewTokenRequest(comment, id, tan); + BaseAPI::setNewTokenRequest(comment, id, tan); else - API::cancelNewTokenRequest(comment, id); + BaseAPI::cancelNewTokenRequest(comment, id); // client should wait for answer return; } @@ -1773,8 +1592,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& // get pending token requests if (subc == "getPendingTokenRequests") { - QVector vec; - if (API::getPendingTokenRequests(vec)) + QVector vec; + if (BaseAPI::getPendingTokenRequests(vec)) { QJsonArray arr; for (const auto& entry : vec) @@ -1800,7 +1619,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& { // use id const bool& accept = message["accept"].toBool(false); - if (!API::handlePendingTokenRequest(id, accept)) + if (!BaseAPI::handlePendingTokenRequest(id, accept)) sendErrorReply("No Authorization", command + "-" + subc, tan); return; } @@ -1808,8 +1627,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& // get token list if (subc == "getTokenList") { - QVector defVect; - if (API::getTokenList(defVect)) + QVector defVect; + if (BaseAPI::getTokenList(defVect)) { QJsonArray tArr; for (const auto& entry : defVect) @@ -1839,7 +1658,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& // userToken is longer if (token.length() > 36) { - if (API::isUserTokenAuthorized(token)) + if (BaseAPI::isUserTokenAuthorized(token)) sendSuccessReply(command + "-" + subc, tan); else sendErrorReply("No Authorization", command + "-" + subc, tan); @@ -1849,7 +1668,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& // usual app token is 36 if (token.length() == 36) { - if (API::isTokenAuthorized(token)) + if (BaseAPI::isTokenAuthorized(token)) { sendSuccessReply(command + "-" + subc, tan); } @@ -1864,14 +1683,14 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& if (password.length() >= 8) { QString userTokenRep; - if (API::isUserAuthorized(password) && API::getUserToken(userTokenRep)) + if (BaseAPI::isUserAuthorized(password) && BaseAPI::getUserToken(userTokenRep)) { // Return the current valid HyperHDR user token QJsonObject obj; obj["token"] = userTokenRep; sendSuccessDataReply(QJsonDocument(obj), command + "-" + subc, tan); } - else if (API::isUserBlocked()) + else if (BaseAPI::isUserBlocked()) sendErrorReply("Too many login attempts detected. Please restart HyperHDR.", command + "-" + subc, tan); else sendErrorReply("No Authorization", command + "-" + subc, tan); @@ -1881,12 +1700,12 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& } } -void JsonAPI::handleNotImplemented(const QString& command, int tan) +void HyperAPI::handleNotImplemented(const QString& command, int tan) { sendErrorReply("Command not implemented", command, tan); } -void JsonAPI::sendSuccessReply(const QString& command, int tan) +void HyperAPI::sendSuccessReply(const QString& command, int tan) { // create reply QJsonObject reply; @@ -1895,10 +1714,10 @@ void JsonAPI::sendSuccessReply(const QString& command, int tan) reply["tan"] = tan; // send reply - emit callbackMessage(reply); + emit SignalCallbackJsonMessage(reply); } -void JsonAPI::sendSuccessDataReply(const QJsonDocument& doc, const QString& command, int tan) +void HyperAPI::sendSuccessDataReply(const QJsonDocument& doc, const QString& command, int tan) { QJsonObject reply; reply["success"] = true; @@ -1909,10 +1728,10 @@ void JsonAPI::sendSuccessDataReply(const QJsonDocument& doc, const QString& comm else reply["info"] = doc.object(); - emit callbackMessage(reply); + emit SignalCallbackJsonMessage(reply); } -void JsonAPI::sendErrorReply(const QString& error, const QString& command, int tan) +void HyperAPI::sendErrorReply(const QString& error, const QString& command, int tan) { // create reply QJsonObject reply; @@ -1922,5 +1741,5 @@ void JsonAPI::sendErrorReply(const QString& error, const QString& command, int t reply["tan"] = tan; // send reply - emit callbackMessage(reply); + emit SignalCallbackJsonMessage(reply); } diff --git a/sources/api/JSONRPC_schema/schema-leddevice.json b/sources/api/JSONRPC_schema/schema-leddevice.json index 847c1c1dc..829487d3c 100644 --- a/sources/api/JSONRPC_schema/schema-leddevice.json +++ b/sources/api/JSONRPC_schema/schema-leddevice.json @@ -13,7 +13,7 @@ "subcommand": { "type" : "string", "required" : true, - "enum" : ["discover","getProperties","identify"] + "enum" : ["discover","getProperties","identify","hasLedClock"] }, "ledDeviceType": { "type" : "string", diff --git a/sources/api/JsonCB.cpp b/sources/api/JsonCB.cpp deleted file mode 100644 index 36624c899..000000000 --- a/sources/api/JsonCB.cpp +++ /dev/null @@ -1,436 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace hyperhdr; - -JsonCB::JsonCB(QObject* parent) - : QObject(parent) - , _hyperhdr(nullptr) -{ - _availableCommands << "components-update" << "performance-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update" << "grabberstate-update" << "lut-calibration-update" - << "adjustment-update" << "videomodehdr-update" << "settings-update" << "leds-update" << "instance-update" << "token-update" << "benchmark-update"; -} - -void JsonCB::setSubscriptionsTo(HyperHdrInstance* hyperhdr) -{ - // get current subs - QStringList currSubs(getSubscribedCommands()); - - if (hyperhdr == nullptr) - { - _hyperhdr = nullptr; - return; - } - - // stop subs - resetSubscriptions(); - - // update pointer - _hyperhdr = hyperhdr; - - // re-apply subs - for (const auto& entry : currSubs) - { - subscribeFor(entry); - } -} - -bool JsonCB::subscribeFor(const QString& type, bool unsubscribe) -{ - if (!_availableCommands.contains(type)) - return false; - - if (unsubscribe) - _subscribedCommands.removeAll(type); - else - _subscribedCommands << type; - - if (type == "components-update" && _hyperhdr != nullptr) - { - ComponentRegister* _componentRegister = &_hyperhdr->getComponentRegister(); - if (unsubscribe) - disconnect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState); - else - connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection); - } - - if (type == "performance-update") - { - if (unsubscribe) - disconnect(PerformanceCounters::getInstance(), &PerformanceCounters::performanceUpdate, this, &JsonCB::handlePerformanceUpdate); - else - { - connect(PerformanceCounters::getInstance(), &PerformanceCounters::performanceUpdate, this, &JsonCB::handlePerformanceUpdate, Qt::UniqueConnection); - emit PerformanceCounters::getInstance()->triggerBroadcast(); - } - } - - if (type == "lut-calibration-update") - { - if (unsubscribe) - disconnect(LutCalibrator::getInstance(), &LutCalibrator::lutCalibrationUpdate, this, &JsonCB::handleLutCalibrationUpdate); - else - connect(LutCalibrator::getInstance(), &LutCalibrator::lutCalibrationUpdate, this, &JsonCB::handleLutCalibrationUpdate, Qt::UniqueConnection); - } - - if (type == "sessions-update") - { -#ifdef ENABLE_BONJOUR - if (unsubscribe) - disconnect(DiscoveryWrapper::getInstance(), &DiscoveryWrapper::foundService, this, &JsonCB::handleNetworkDiscoveryChange); - else - connect(DiscoveryWrapper::getInstance(), &DiscoveryWrapper::foundService, this, &JsonCB::handleNetworkDiscoveryChange, Qt::UniqueConnection); -#endif - } - - if (type == "priorities-update" && _hyperhdr != nullptr) - { - PriorityMuxer* _prioMuxer = _hyperhdr->getMuxerInstance(); - if (unsubscribe) - disconnect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate); - else - connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection); - } - - if (type == "imageToLedMapping-update" && _hyperhdr != nullptr) - { - if (unsubscribe) - disconnect(_hyperhdr, &HyperHdrInstance::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange); - else - connect(_hyperhdr, &HyperHdrInstance::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange, Qt::UniqueConnection); - } - - if (type == "adjustment-update" && _hyperhdr != nullptr) - { - if (unsubscribe) - disconnect(_hyperhdr, &HyperHdrInstance::adjustmentChanged, this, &JsonCB::handleAdjustmentChange); - else - connect(_hyperhdr, &HyperHdrInstance::adjustmentChanged, this, &JsonCB::handleAdjustmentChange, Qt::UniqueConnection); - } - - if (type == "grabberstate-update" && GrabberWrapper::instance != nullptr) - { - if (unsubscribe) - disconnect(GrabberWrapper::instance, &GrabberWrapper::StateChanged, this, &JsonCB::handleGrabberStateChange); - else - connect(GrabberWrapper::instance, &GrabberWrapper::StateChanged, this, &JsonCB::handleGrabberStateChange, Qt::UniqueConnection); - } - - if (type == "videomodehdr-update" && GrabberWrapper::instance != nullptr) - { - if (unsubscribe) - disconnect(GrabberWrapper::instance, &GrabberWrapper::HdrChanged, this, &JsonCB::handleVideoModeHdrChange); - else - connect(GrabberWrapper::instance, &GrabberWrapper::HdrChanged, this, &JsonCB::handleVideoModeHdrChange, Qt::UniqueConnection); - } - - if (type == "videomodehdr-update" && GrabberWrapper::instance == nullptr && FlatBufferServer::instance != nullptr) - { - if (unsubscribe) - disconnect(FlatBufferServer::instance, &FlatBufferServer::HdrChanged, this, &JsonCB::handleVideoModeHdrChange); - else - connect(FlatBufferServer::instance, &FlatBufferServer::HdrChanged, this, &JsonCB::handleVideoModeHdrChange, Qt::UniqueConnection); - } - - if (type == "settings-update" && _hyperhdr != nullptr) - { - if (unsubscribe) - disconnect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &JsonCB::handleSettingsChange); - else - connect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection); - } - - if (type == "leds-update" && _hyperhdr != nullptr) - { - if (unsubscribe) - disconnect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &JsonCB::handleLedsConfigChange); - else - connect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &JsonCB::handleLedsConfigChange, Qt::UniqueConnection); - } - - - if (type == "instance-update") - { - if (unsubscribe) - disconnect(HyperHdrIManager::getInstance(), &HyperHdrIManager::change, this, &JsonCB::handleInstanceChange); - else - connect(HyperHdrIManager::getInstance(), &HyperHdrIManager::change, this, &JsonCB::handleInstanceChange, Qt::UniqueConnection); - } - - if (type == "token-update") - { - if (unsubscribe) - disconnect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCB::handleTokenChange); - else - connect(AuthManager::getInstance(), &AuthManager::tokenChange, this, &JsonCB::handleTokenChange, Qt::UniqueConnection); - } - - if (type == "benchmark-update" && GrabberWrapper::instance != nullptr) - { - if (unsubscribe) - disconnect(GrabberWrapper::instance, &GrabberWrapper::benchmarkUpdate, this, &JsonCB::handleBenchmarkUpdate); - else - connect(GrabberWrapper::instance, &GrabberWrapper::benchmarkUpdate, this, &JsonCB::handleBenchmarkUpdate, Qt::UniqueConnection); - } - - return true; -} - -QStringList JsonCB::getCommands() -{ - return _availableCommands; -}; - -QStringList JsonCB::getSubscribedCommands() -{ - return _subscribedCommands; -}; - -void JsonCB::resetSubscriptions() -{ - for (const auto& entry : getSubscribedCommands()) - { - subscribeFor(entry, true); - } -} - -void JsonCB::doCallback(const QString& cmd, const QVariant& data) -{ - QJsonObject obj; - obj["command"] = cmd; - - if (data.userType() == QMetaType::QJsonArray) - obj["data"] = data.toJsonArray(); - else - obj["data"] = data.toJsonObject(); - - emit newCallback(obj); -} - -void JsonCB::handleComponentState(hyperhdr::Components comp, bool state) -{ - QJsonObject data; - data["name"] = componentToIdString(comp); - data["enabled"] = state; - - doCallback("components-update", QVariant(data)); -} - -#ifdef ENABLE_BONJOUR - void JsonCB::handleNetworkDiscoveryChange(DiscoveryRecord::Service type, QList records) - { - QJsonObject retValue; - QJsonArray data; - - for (const auto& session : records) - { - QJsonObject item; - item["name"] = session.getName(); - item["host"] = session.hostName; - item["address"] = session.address; - item["port"] = session.port; - data.append(item); - } - - retValue["service"] = DiscoveryRecord::getName(type); - retValue["items"] = data; - doCallback("sessions-update", QVariant(retValue)); - } -#endif - -void JsonCB::handlePriorityUpdate() -{ - QJsonObject info; - QJsonArray priorities; - - if (_hyperhdr == nullptr) - return; - - SAFE_CALL_1_RET(_hyperhdr, getJsonInfo, QJsonObject, info, bool, false); - - doCallback("priorities-update", QVariant(info)); -} - -void JsonCB::handleImageToLedsMappingChange(int mappingType) -{ - QJsonObject data; - data["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(mappingType); - - doCallback("imageToLedMapping-update", QVariant(data)); -} - -void JsonCB::handleAdjustmentChange() -{ - if (_hyperhdr == nullptr) - return; - - QJsonArray adjustmentArray; - for (const QString& adjustmentId : _hyperhdr->getAdjustmentIds()) - { - const ColorAdjustment* colorAdjustment = _hyperhdr->getAdjustment(adjustmentId); - if (colorAdjustment == nullptr) - { - continue; - } - - QJsonObject adjustment; - adjustment["id"] = adjustmentId; - - QJsonArray whiteAdjust; - whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentR()); - whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentG()); - whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentB()); - adjustment.insert("white", whiteAdjust); - - QJsonArray redAdjust; - redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentR()); - redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentG()); - redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentB()); - adjustment.insert("red", redAdjust); - - QJsonArray greenAdjust; - greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentR()); - greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentG()); - greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentB()); - adjustment.insert("green", greenAdjust); - - QJsonArray blueAdjust; - blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentR()); - blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentG()); - blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentB()); - adjustment.insert("blue", blueAdjust); - - QJsonArray cyanAdjust; - cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentR()); - cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentG()); - cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentB()); - adjustment.insert("cyan", cyanAdjust); - - QJsonArray magentaAdjust; - magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentR()); - magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentG()); - magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentB()); - adjustment.insert("magenta", magentaAdjust); - - QJsonArray yellowAdjust; - yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentR()); - yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentG()); - yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentB()); - adjustment.insert("yellow", yellowAdjust); - - adjustment["backlightThreshold"] = colorAdjustment->_rgbTransform.getBacklightThreshold(); - adjustment["backlightColored"] = colorAdjustment->_rgbTransform.getBacklightColored(); - adjustment["brightness"] = colorAdjustment->_rgbTransform.getBrightness(); - adjustment["brightnessCompensation"] = colorAdjustment->_rgbTransform.getBrightnessCompensation(); - adjustment["gammaRed"] = colorAdjustment->_rgbTransform.getGammaR(); - adjustment["gammaGreen"] = colorAdjustment->_rgbTransform.getGammaG(); - adjustment["gammaBlue"] = colorAdjustment->_rgbTransform.getGammaB(); - - adjustmentArray.append(adjustment); - } - - doCallback("adjustment-update", QVariant(adjustmentArray)); -} - -void JsonCB::handleVideoModeHdrChange(int hdr) -{ - QJsonObject data; - data["videomodehdr"] = hdr; - doCallback("videomodehdr-update", QVariant(data)); -} - -void JsonCB::handleGrabberStateChange(QString device, QString videoMode) -{ - QJsonObject data; - data["device"] = device; - data["videoMode"] = videoMode; - doCallback("grabberstate-update", QVariant(data)); -} - -void JsonCB::handleSettingsChange(settings::type type, const QJsonDocument& data) -{ - QJsonObject dat; - if (data.isObject()) - dat[typeToString(type)] = data.object(); - else - dat[typeToString(type)] = data.array(); - - doCallback("settings-update", QVariant(dat)); -} - -void JsonCB::handleLedsConfigChange(settings::type type, const QJsonDocument& data) -{ - if (type == settings::type::LEDS) - { - QJsonObject dat; - dat[typeToString(type)] = data.array(); - doCallback("leds-update", QVariant(dat)); - } -} - -void JsonCB::handleInstanceChange() -{ - QJsonArray arr; - - for (const auto& entry : HyperHdrIManager::getInstance()->getInstanceData()) - { - QJsonObject obj; - obj.insert("friendly_name", entry["friendly_name"].toString()); - obj.insert("instance", entry["instance"].toInt()); - //obj.insert("last_use", entry["last_use"].toString()); - obj.insert("running", entry["running"].toBool()); - arr.append(obj); - } - doCallback("instance-update", QVariant(arr)); -} - -void JsonCB::handleTokenChange(const QVector& def) -{ - QJsonArray arr; - for (const auto& entry : def) - { - QJsonObject sub; - sub["comment"] = entry.comment; - sub["id"] = entry.id; - sub["last_use"] = entry.lastUse; - arr.push_back(sub); - } - doCallback("token-update", QVariant(arr)); -} - -void JsonCB::handleBenchmarkUpdate(int status, QString message) -{ - QJsonObject dat; - dat["status"] = status; - dat["message"] = message; - doCallback("benchmark-update", QVariant(dat)); -} - -void JsonCB::handleLutCalibrationUpdate(const QJsonObject& data) -{ - doCallback("lut-calibration-update", QVariant(data)); -} - - -void JsonCB::handlePerformanceUpdate(const QJsonObject& data) -{ - doCallback("performance-update", QVariant(data)); -} - -void JsonCB::handleLutInstallUpdate(const QJsonObject& data) -{ - doCallback("lut-install-update", QVariant(data)); -} diff --git a/sources/base/AuthManager.cpp b/sources/base/AccessManager.cpp similarity index 63% rename from sources/base/AuthManager.cpp rename to sources/base/AccessManager.cpp index 055867468..243052783 100644 --- a/sources/base/AuthManager.cpp +++ b/sources/base/AccessManager.cpp @@ -1,39 +1,36 @@ -#include +#ifndef PCH_ENABLED + #include + #include +#endif -// db +#include #include #include -#include -// qt -#include -#include -AuthManager* AuthManager::manager = nullptr; +using namespace hyperhdr; -AuthManager::AuthManager(QObject* parent, bool readonlyMode) +AccessManager::AccessManager(QObject* parent, bool readonlyMode) : QObject(parent) - , _authTable(new AuthTable("", this, readonlyMode)) - , _metaTable(new MetaTable(this, readonlyMode)) + , _authTable(new AuthTable(readonlyMode)) + , _metaTable(new MetaTable(readonlyMode)) , _pendingRequests() , _authRequired(true) , _timer(new QTimer(this)) , _authBlockTimer(new QTimer(this)) { - AuthManager::manager = this; - // get uuid _uuid = _metaTable->getUUID(); // Register meta - qRegisterMetaType>("QVector"); + qRegisterMetaType>("QVector"); // setup timer _timer->setInterval(1000); - connect(_timer, &QTimer::timeout, this, &AuthManager::checkTimeout); + connect(_timer, &QTimer::timeout, this, &AccessManager::checkTimeout); // setup authBlockTimer _authBlockTimer->setInterval(60000); - connect(_authBlockTimer, &QTimer::timeout, this, &AuthManager::checkAuthBlockTimeout); + connect(_authBlockTimer, &QTimer::timeout, this, &AccessManager::checkAuthBlockTimeout); // init with default user and password if (!_authTable->userExist(DEFAULT_CONFIG_USER)) @@ -45,12 +42,16 @@ AuthManager::AuthManager(QObject* parent, bool readonlyMode) _authTable->setUserToken(DEFAULT_CONFIG_USER); } -const QString AuthManager::loadPipewire() +AccessManager::~AccessManager() +{ +} + +const QString AccessManager::loadPipewire() { return _authTable->loadPipewire(); } -AuthManager::AuthDefinition AuthManager::createToken(const QString& comment) +AccessManager::AuthDefinition AccessManager::createToken(const QString& comment) { const QString token = QUuid::createUuid().toString().mid(1, 36); const QString id = QUuid::createUuid().toString().mid(1, 36).left(5); @@ -62,19 +63,19 @@ AuthManager::AuthDefinition AuthManager::createToken(const QString& comment) def.token = token; def.id = id; - emit tokenChange(getTokenList()); + emit SignalTokenUpdated(getTokenList()); return def; } -void AuthManager::savePipewire(const QString& wToken) +void AccessManager::savePipewire(const QString& wToken) { _authTable->savePipewire(wToken); } -QVector AuthManager::getTokenList() const +QVector AccessManager::getTokenList() const { QVector vector = _authTable->getTokenList(); - QVector finalVec; + QVector finalVec; for (const auto& entry : vector) { AuthDefinition def; @@ -89,13 +90,13 @@ QVector AuthManager::getTokenList() const return finalVec; } -QString AuthManager::getUserToken(const QString& usr) const +QString AccessManager::getUserToken(const QString& usr) const { QString tok = _authTable->getUserToken(usr); return QString(_authTable->getUserToken(usr)); } -void AuthManager::setAuthBlock(bool user) +void AccessManager::setAuthBlock(bool user) { // current timestamp +10 minutes if (user) @@ -106,7 +107,7 @@ void AuthManager::setAuthBlock(bool user) _authBlockTimer->start(); } -bool AuthManager::isUserAuthorized(const QString& user, const QString& pw) +bool AccessManager::isUserAuthorized(const QString& user, const QString& pw) { if (isUserAuthBlocked()) return false; @@ -119,7 +120,7 @@ bool AuthManager::isUserAuthorized(const QString& user, const QString& pw) return true; } -bool AuthManager::isTokenAuthorized(const QString& token) +bool AccessManager::isTokenAuthorized(const QString& token) { if (isTokenAuthBlocked()) return false; @@ -130,11 +131,11 @@ bool AuthManager::isTokenAuthorized(const QString& token) return false; } // timestamp update - tokenChange(getTokenList()); + SignalTokenUpdated(getTokenList()); return true; } -bool AuthManager::isUserTokenAuthorized(const QString& usr, const QString& token) +bool AccessManager::isUserTokenAuthorized(const QString& usr, const QString& token) { if (isUserAuthBlocked()) return false; @@ -147,7 +148,7 @@ bool AuthManager::isUserTokenAuthorized(const QString& usr, const QString& token return true; } -bool AuthManager::updateUserPassword(const QString& user, const QString& pw, const QString& newPw) +bool AccessManager::updateUserPassword(const QString& user, const QString& pw, const QString& newPw) { if (isUserAuthorized(user, pw)) return _authTable->updateUserPassword(user, newPw); @@ -155,12 +156,12 @@ bool AuthManager::updateUserPassword(const QString& user, const QString& pw, con return false; } -bool AuthManager::resetHyperhdrUser() +bool AccessManager::resetHyperhdrUser() { return _authTable->resetHyperhdrUser(); } -void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, const QString& id, const int& tan) +void AccessManager::setNewTokenRequest(QObject* caller, const QString& comment, const QString& id, const int& tan) { if (!_pendingRequests.contains(id)) { @@ -171,7 +172,7 @@ void AuthManager::setNewTokenRequest(QObject* caller, const QString& comment, co } } -void AuthManager::cancelNewTokenRequest(QObject* caller, const QString&, const QString& id) +void AccessManager::cancelNewTokenRequest(QObject* caller, const QString&, const QString& id) { if (_pendingRequests.contains(id)) { @@ -182,7 +183,7 @@ void AuthManager::cancelNewTokenRequest(QObject* caller, const QString&, const Q } } -void AuthManager::handlePendingTokenRequest(const QString& id, bool accept) +void AccessManager::handlePendingTokenRequest(const QString& id, bool accept) { if (_pendingRequests.contains(id)) { @@ -192,19 +193,19 @@ void AuthManager::handlePendingTokenRequest(const QString& id, bool accept) { const QString token = QUuid::createUuid().toString().remove("{").remove("}"); _authTable->createToken(token, def.comment, id); - emit tokenResponse(true, def.caller, token, def.comment, id, def.tan); - emit tokenChange(getTokenList()); + emit SignalTokenNotifyClient(true, def.caller, token, def.comment, id, def.tan); + emit SignalTokenUpdated(getTokenList()); } else { - emit tokenResponse(false, def.caller, QString(), def.comment, id, def.tan); + emit SignalTokenNotifyClient(false, def.caller, QString(), def.comment, id, def.tan); } } } -QVector AuthManager::getPendingRequests() const +QVector AccessManager::getPendingRequests() const { - QVector finalVec; + QVector finalVec; for (const auto& entry : _pendingRequests) { AuthDefinition def; @@ -216,27 +217,27 @@ QVector AuthManager::getPendingRequests() const return finalVec; } -bool AuthManager::renameToken(const QString& id, const QString& comment) +bool AccessManager::renameToken(const QString& id, const QString& comment) { if (_authTable->renameToken(id, comment)) { - emit tokenChange(getTokenList()); + emit SignalTokenUpdated(getTokenList()); return true; } return false; } -bool AuthManager::deleteToken(const QString& id) +bool AccessManager::deleteToken(const QString& id) { if (_authTable->deleteToken(id)) { - emit tokenChange(getTokenList()); + emit SignalTokenUpdated(getTokenList()); return true; } return false; } -void AuthManager::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +void AccessManager::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { if (type == settings::type::NETWORK) { @@ -247,7 +248,7 @@ void AuthManager::handleSettingsUpdate(settings::type type, const QJsonDocument& } } -void AuthManager::checkTimeout() +void AccessManager::checkTimeout() { const uint64_t now = InternalClock::now(); @@ -259,7 +260,7 @@ void AuthManager::checkTimeout() const AuthDefinition& def = i.value(); if (def.timeoutTime <= now) { - emit tokenResponse(false, def.caller, QString(), def.comment, def.id, def.tan); + emit SignalTokenNotifyClient(false, def.caller, QString(), def.comment, def.id, def.tan); _pendingRequests.remove(i.key()); } } @@ -268,7 +269,7 @@ void AuthManager::checkTimeout() _timer->stop(); } -void AuthManager::checkAuthBlockTimeout() +void AccessManager::checkAuthBlockTimeout() { // handle user auth block QVector::iterator itu = _userAuthAttempts.begin(); @@ -293,37 +294,32 @@ void AuthManager::checkAuthBlockTimeout() _authBlockTimer->stop(); } -QString AuthManager::getID() const +QString AccessManager::getID() const { return _uuid; } -bool AuthManager::isAuthRequired() const +bool AccessManager::isAuthRequired() const { return _authRequired; } -bool AuthManager::isLocalAuthRequired() const +bool AccessManager::isLocalAuthRequired() const { return _localAuthRequired; } -bool AuthManager::isLocalAdminAuthRequired() const +bool AccessManager::isLocalAdminAuthRequired() const { return _localAdminAuthRequired; } -bool AuthManager::isUserAuthBlocked() const +bool AccessManager::isUserAuthBlocked() const { return (_userAuthAttempts.length() >= 10); } -bool AuthManager::isTokenAuthBlocked() const +bool AccessManager::isTokenAuthBlocked() const { return (_tokenAuthAttempts.length() >= 25); } - -AuthManager* AuthManager::getInstance() -{ - return manager; -} diff --git a/sources/base/BGEffectHandler.cpp b/sources/base/BGEffectHandler.cpp deleted file mode 100644 index 59843ed6c..000000000 --- a/sources/base/BGEffectHandler.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -BGEffectHandler::BGEffectHandler(HyperHdrInstance* hyperhdr) - : QObject(hyperhdr) - , _hyperhdr(hyperhdr) -{ - // listen for config changes - connect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &BGEffectHandler::handleSettingsUpdate); - - // initialization - handleSettingsUpdate(settings::type::BGEFFECT, _hyperhdr->getSetting(settings::type::BGEFFECT)); -} - - -void BGEffectHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) -{ - if (type == settings::type::BGEFFECT) - { - const QJsonObject& BGEffectConfig = config.object(); - - - - // clear background priority - _hyperhdr->clear(254); - // initial background effect/color - if (BGEffectConfig["enable"].toBool(true)) - { - const QString bgTypeConfig = BGEffectConfig["type"].toString("effect"); - const QString bgEffectConfig = BGEffectConfig["effect"].toString("Warm mood blobs"); - const QJsonValue bgColorConfig = BGEffectConfig["color"]; - - if (bgTypeConfig.contains("color")) - { - QJsonArray BGCONFIG_ARRAY = bgColorConfig.toArray(); - - if (BGCONFIG_ARRAY.size() < 3) - return; - - std::vector bg_color = { - ColorRgb { - static_cast(BGCONFIG_ARRAY.at(0).toInt(0)), - static_cast(BGCONFIG_ARRAY.at(1).toInt(0)), - static_cast(BGCONFIG_ARRAY.at(2).toInt(0)) - } - }; - - _hyperhdr->setColor(254, bg_color); - Info(Logger::getInstance("HYPERHDR"), "Initial background color set (%d %d %d)", bg_color.at(0).red, bg_color.at(0).green, bg_color.at(0).blue); - } - else - { - int result = _hyperhdr->setEffect(bgEffectConfig, 254); - Info(Logger::getInstance("HYPERHDR"), "Initial background effect '%s' %s", QSTRING_CSTR(bgEffectConfig), ((result == 0) ? "started" : "failed")); - } - } - } -} - diff --git a/sources/base/CMakeLists.txt b/sources/base/CMakeLists.txt index a9b2196a7..2921a8bdc 100644 --- a/sources/base/CMakeLists.txt +++ b/sources/base/CMakeLists.txt @@ -1,4 +1,3 @@ - # Define the current source locations SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/base) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/base) diff --git a/sources/base/ColorCalibration.cpp b/sources/base/ColorCalibration.cpp new file mode 100644 index 000000000..fb415858a --- /dev/null +++ b/sources/base/ColorCalibration.cpp @@ -0,0 +1,214 @@ +/* ColorCalibration.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include + +ColorCalibration::ColorCalibration(uint8_t instance, const QJsonObject& adjustmentConfig) +{ + _id = adjustmentConfig["id"].toString("default"); + _blackCalibration = std::unique_ptr(new ChannelCalibration(instance, "black", adjustmentConfig, 0, 0, 0)); + _whiteCalibration = std::unique_ptr(new ChannelCalibration(instance, "white", adjustmentConfig, 255, 255, 255)); + _redCalibration = std::unique_ptr(new ChannelCalibration(instance, "red", adjustmentConfig, 255, 0, 0)); + _greenCalibration = std::unique_ptr(new ChannelCalibration(instance, "green", adjustmentConfig, 0, 255, 0)); + _blueCalibration = std::unique_ptr(new ChannelCalibration(instance, "blue", adjustmentConfig, 0, 0, 255)); + _cyanCalibration = std::unique_ptr(new ChannelCalibration(instance, "cyan", adjustmentConfig, 0, 255, 255)); + _magentaCalibration = std::unique_ptr(new ChannelCalibration(instance, "magenta", adjustmentConfig, 255, 0, 255)); + _yellowCalibration = std::unique_ptr(new ChannelCalibration(instance, "yellow", adjustmentConfig, 255, 255, 0)); + _colorspaceCalibration = std::unique_ptr(new ColorSpaceCalibration(instance, adjustmentConfig)); +} + +QString ColorCalibration::getId() +{ + return _id; +} + +void ColorCalibration::setBackLightEnabled(bool enabled) +{ + _colorspaceCalibration->setBackLightEnabled(enabled); +} + +void ColorCalibration::calibrate(ColorRgb& color) +{ + uint8_t& ored = color.red; + uint8_t& ogreen = color.green; + uint8_t& oblue = color.blue; + + if (_colorspaceCalibration->_classic_config) + { + _colorspaceCalibration->applySaturationLuminance(ored, ogreen, oblue); + _colorspaceCalibration->applyGamma(ored, ogreen, oblue); + + if (_redCalibration->isEnabled() || + _greenCalibration->isEnabled() || + _blueCalibration->isEnabled()) + { + int RR = _redCalibration->adjustmentR(ored); + int RG = ored > ogreen ? _redCalibration->adjustmentG(ored - ogreen) : 0; + int RB = ored > oblue ? _redCalibration->adjustmentB(ored - oblue) : 0; + + int GR = ogreen > ored ? _greenCalibration->adjustmentR(ogreen - ored) : 0; + int GG = _greenCalibration->adjustmentG(ogreen); + int GB = ogreen > oblue ? _greenCalibration->adjustmentB(ogreen - oblue) : 0; + + int BR = oblue > ored ? _blueCalibration->adjustmentR(oblue - ored) : 0; + int BG = oblue > ogreen ? _blueCalibration->adjustmentG(oblue - ogreen) : 0; + int BB = _blueCalibration->adjustmentB(oblue); + + int ledR = RR + GR + BR; + int maxR = _redCalibration->getAdjustment().red; + int ledG = RG + GG + BG; + int maxG = _greenCalibration->getAdjustment().green; + int ledB = RB + GB + BB; + int maxB = _blueCalibration->getAdjustment().blue; + + color.red = static_cast(std::min(ledR, maxR)); + color.green = static_cast(std::min(ledG, maxG)); + color.blue = static_cast(std::min(maxB, ledB)); + } + + // temperature + color.red = _redCalibration->correction(color.red); + color.green = _greenCalibration->correction(color.green); + color.blue = _blueCalibration->correction(color.blue); + } + else + { + uint8_t B_RGB = 0, B_CMY = 0, B_W = 0; + + _colorspaceCalibration->getBrightnessComponents(B_RGB, B_CMY, B_W); + _colorspaceCalibration->applyGamma(ored, ogreen, oblue); + if (!_blackCalibration->isEnabled() && !_redCalibration->isEnabled() && + !_greenCalibration->isEnabled() && !_blueCalibration->isEnabled() && + !_cyanCalibration->isEnabled() && !_magentaCalibration->isEnabled() && + !_yellowCalibration->isEnabled() && !_whiteCalibration->isEnabled()) + { + if (B_RGB != 255) + { + color.red = ((uint32_t)color.red * B_RGB) / 255; + color.green = ((uint32_t)color.green * B_RGB) / 255; + color.blue = ((uint32_t)color.blue * B_RGB) / 255; + } + } + else + { + uint32_t nrng = (uint32_t)(255 - ored) * (255 - ogreen); + uint32_t rng = (uint32_t)(ored) * (255 - ogreen); + uint32_t nrg = (uint32_t)(255 - ored) * (ogreen); + uint32_t rg = (uint32_t)(ored) * (ogreen); + + uint8_t black = nrng * (255 - oblue) / 65025; + uint8_t red = rng * (255 - oblue) / 65025; + uint8_t green = nrg * (255 - oblue) / 65025; + uint8_t blue = nrng * (oblue) / 65025; + uint8_t cyan = nrg * (oblue) / 65025; + uint8_t magenta = rng * (oblue) / 65025; + uint8_t yellow = rg * (255 - oblue) / 65025; + uint8_t white = rg * (oblue) / 65025; + + uint8_t OR, OG, OB, RR, RG, RB, GR, GG, GB, BR, BG, BB; + uint8_t CR, CG, CB, MR, MG, MB, YR, YG, YB, WR, WG, WB; + + _blackCalibration->apply(black, 255, OR, OG, OB); + _redCalibration->apply(red, B_RGB, RR, RG, RB); + _greenCalibration->apply(green, B_RGB, GR, GG, GB); + _blueCalibration->apply(blue, B_RGB, BR, BG, BB); + _cyanCalibration->apply(cyan, B_CMY, CR, CG, CB); + _magentaCalibration->apply(magenta, B_CMY, MR, MG, MB); + _yellowCalibration->apply(yellow, B_CMY, YR, YG, YB); + _whiteCalibration->apply(white, B_W, WR, WG, WB); + + color.red = OR + RR + GR + BR + CR + MR + YR + WR; + color.green = OG + RG + GG + BG + CG + MG + YG + WG; + color.blue = OB + RB + GB + BB + CB + MB + YB + WB; + } + } + _colorspaceCalibration->applyBacklight(ored, ogreen, oblue); +} + +void ColorCalibration::putCurrentConfig(QJsonObject& adjustment) +{ + ChannelCalibration* calibColors[] = { _blackCalibration.get(), _whiteCalibration.get(), _redCalibration.get(), + _greenCalibration.get(), _blueCalibration.get(), _cyanCalibration.get(), + _magentaCalibration.get(), _yellowCalibration.get() }; + + adjustment["id"] = _id; + + for (const auto& calibChannel : calibColors) + { + QJsonArray adj; + adj.append(calibChannel->getAdjustment().red); + adj.append(calibChannel->getAdjustment().green); + adj.append(calibChannel->getAdjustment().blue); + adjustment.insert(calibChannel->getChannelName(), adj); + } + + adjustment["backlightThreshold"] = _colorspaceCalibration->getBacklightThreshold(); + adjustment["backlightColored"] = _colorspaceCalibration->getBacklightColored(); + adjustment["brightness"] = _colorspaceCalibration->getBrightness(); + adjustment["brightnessCompensation"] = _colorspaceCalibration->getBrightnessCompensation(); + adjustment["gammaRed"] = _colorspaceCalibration->getGammaR(); + adjustment["gammaGreen"] = _colorspaceCalibration->getGammaG(); + adjustment["gammaBlue"] = _colorspaceCalibration->getGammaB(); + adjustment["temperatureRed"] = _redCalibration->getCorrection(); + adjustment["temperatureGreen"] = _greenCalibration->getCorrection(); + adjustment["temperatureBlue"] = _blueCalibration->getCorrection(); + adjustment["saturationGain"] = _colorspaceCalibration->getSaturationGain(); + adjustment["luminanceGain"] = _colorspaceCalibration->getLuminanceGain(); + adjustment["classic_config"] = _colorspaceCalibration->getClassicConfig(); +} + +void ColorCalibration::updateConfig(const QJsonObject& adjustment) +{ + if (adjustment.contains("classic_config")) + _colorspaceCalibration->setClassicConfig(adjustment["classic_config"].toBool(false)); + + _redCalibration->setCorrection(adjustment["temperatureRed"].toInt(-1)); + _greenCalibration->setCorrection(adjustment["temperatureGreen"].toInt(-1)); + _blueCalibration->setCorrection(adjustment["temperatureBlue"].toInt(-1)); + + _blackCalibration->setAdjustment(adjustment["black"].toArray()); + _redCalibration->setAdjustment(adjustment["red"].toArray()); + _greenCalibration->setAdjustment(adjustment["green"].toArray()); + _blueCalibration->setAdjustment(adjustment["blue"].toArray()); + _cyanCalibration->setAdjustment(adjustment["cyan"].toArray()); + _magentaCalibration->setAdjustment(adjustment["magenta"].toArray()); + _yellowCalibration->setAdjustment(adjustment["yellow"].toArray()); + _whiteCalibration->setAdjustment(adjustment["white"].toArray()); + + if (adjustment.contains("gammaRed") || adjustment.contains("gammaGreen") || adjustment.contains("gammaBlue")) + _colorspaceCalibration->setGamma(adjustment["gammaRed"].toDouble(_colorspaceCalibration->getGammaR()), + adjustment["gammaGreen"].toDouble(_colorspaceCalibration->getGammaG()), + adjustment["gammaBlue"].toDouble(_colorspaceCalibration->getGammaB())); + + _colorspaceCalibration->setBacklightThreshold(adjustment["backlightThreshold"].toDouble(-1)); + if (adjustment.contains("backlightColored")) + _colorspaceCalibration->setBacklightColored(adjustment["backlightColored"].toBool(_colorspaceCalibration->getBacklightColored())); + _colorspaceCalibration->setBrightness(adjustment["brightness"].toInt(-1)); + _colorspaceCalibration->setBrightnessCompensation(adjustment["brightnessCompensation"].toInt(-1)); + _colorspaceCalibration->setSaturationGain(adjustment["saturationGain"].toDouble(-1)); + _colorspaceCalibration->setLuminanceGain(adjustment["luminanceGain"].toDouble(-1)); +} diff --git a/sources/base/ComponentController.cpp b/sources/base/ComponentController.cpp new file mode 100644 index 000000000..6d70b84c9 --- /dev/null +++ b/sources/base/ComponentController.cpp @@ -0,0 +1,95 @@ +#ifndef PCH_ENABLED + #include +#endif + +#include +#include +#include + +using namespace hyperhdr; + +ComponentController::ComponentController(HyperHdrInstance* hyperhdr) + : _log(Logger::getInstance(QString("COMPONENTCTRL%1").arg(hyperhdr->getInstanceIndex()))) +{ + // init all comps to false + QVector vect; + vect << COMP_ALL << COMP_HDR << COMP_SMOOTHING << COMP_BLACKBORDER << COMP_FORWARDER; + +#if defined(ENABLE_BOBLIGHT) + vect << COMP_BOBLIGHTSERVER; +#endif + + vect << COMP_VIDEOGRABBER << COMP_SYSTEMGRABBER << COMP_LEDDEVICE; + for (auto e : vect) + { + _componentStates.emplace(e, (e == COMP_ALL)); + } + + connect(this, &ComponentController::SignalRequestComponent, hyperhdr, &HyperHdrInstance::SignalRequestComponent); + connect(hyperhdr, &HyperHdrInstance::SignalRequestComponent, this, &ComponentController::handleCompStateChangeRequest); + Debug(_log, "ComponentController is initialized"); +} + +ComponentController::~ComponentController() +{ + Debug(_log, "ComponentController is released"); +} + +void ComponentController::handleCompStateChangeRequest(hyperhdr::Components comps, bool activated) +{ + if (comps == COMP_ALL) + { + if (!activated && _prevComponentStates.empty()) + { + Debug(_log, "Disabling HyperHDR instance: saving current component states first"); + for (const auto& comp : _componentStates) + if (comp.first != COMP_ALL) + { + _prevComponentStates.emplace(comp.first, comp.second); + if (comp.second) + { + emit SignalRequestComponent(comp.first, false); + } + } + setNewComponentState(COMP_ALL, false); + } + else + { + if (activated && !_prevComponentStates.empty()) + { + Debug(_log, "Enabling HyperHDR instance: recovering previuosly saved component states"); + for (const auto& comp : _prevComponentStates) + if (comp.first != COMP_ALL) + { + if (comp.second) + { + emit SignalRequestComponent(comp.first, true); + } + } + _prevComponentStates.clear(); + setNewComponentState(COMP_ALL, true); + } + } + } +} + +int ComponentController::isComponentEnabled(hyperhdr::Components comp) const +{ + return (_componentStates.count(comp)) ? _componentStates.at(comp) : -1; +} + +const std::map& ComponentController::getComponentsState() const +{ + return _componentStates; +} + +void ComponentController::setNewComponentState(hyperhdr::Components comp, bool activated) +{ + if (_componentStates[comp] != activated) + { + Info(_log, "%s: %s", componentToString(comp), (activated ? "enabled" : "disabled")); + _componentStates[comp] = activated; + // emit component has changed state + emit SignalComponentStateChanged(comp, activated); + } +} diff --git a/sources/base/ComponentRegister.cpp b/sources/base/ComponentRegister.cpp deleted file mode 100644 index 3f2a5b34b..000000000 --- a/sources/base/ComponentRegister.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include - -using namespace hyperhdr; - -ComponentRegister::ComponentRegister(HyperHdrInstance* hyperhdr) - : _hyperhdr(hyperhdr) - , _log(Logger::getInstance(QString("COMPONENTREG%1").arg(hyperhdr->getInstanceIndex()))) -{ - // init all comps to false - QVector vect; - vect << COMP_ALL << COMP_HDR << COMP_SMOOTHING << COMP_BLACKBORDER << COMP_FORWARDER; - -#if defined(ENABLE_BOBLIGHT) - vect << COMP_BOBLIGHTSERVER; -#endif - - vect << COMP_VIDEOGRABBER << COMP_SYSTEMGRABBER << COMP_LEDDEVICE; - for (auto e : vect) - { - _componentStates.emplace(e, (e == COMP_ALL)); - } - - connect(_hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &ComponentRegister::handleCompStateChangeRequest); -} - -ComponentRegister::~ComponentRegister() -{ -} - -int ComponentRegister::isComponentEnabled(hyperhdr::Components comp) const -{ - return (_componentStates.count(comp)) ? _componentStates.at(comp) : -1; -} - -void ComponentRegister::setNewComponentState(hyperhdr::Components comp, bool activated) -{ - if (_componentStates[comp] != activated) - { - Info(_log, "%s: %s", componentToString(comp), (activated ? "enabled" : "disabled")); - _componentStates[comp] = activated; - // emit component has changed state - emit updatedComponentState(comp, activated); - } -} - -void ComponentRegister::handleCompStateChangeRequest(hyperhdr::Components comps, bool activated) -{ - if (comps == COMP_ALL && !_inProgress) - { - _inProgress = true; - if (!activated && _prevComponentStates.empty()) - { - Debug(_log, "Disable Hyperhdr, store current component states"); - for (const auto& comp : _componentStates) - { - // save state - _prevComponentStates.emplace(comp.first, comp.second); - // disable if enabled - if (comp.second) - { - emit _hyperhdr->compStateChangeRequest(comp.first, false); - } - } - setNewComponentState(COMP_ALL, false); - } - else - { - if (activated && !_prevComponentStates.empty()) - { - Debug(_log, "Enable Hyperhdr, recover previous component states"); - for (const auto& comp : _prevComponentStates) - { - // if comp was enabled, enable again - if (comp.second) - { - emit _hyperhdr->compStateChangeRequest(comp.first, true); - } - } - _prevComponentStates.clear(); - setNewComponentState(COMP_ALL, true); - } - } - _inProgress = false; - } -} diff --git a/sources/base/DetectionAutomatic.cpp b/sources/base/DetectionAutomatic.cpp index f99051905..3a31a41c1 100644 --- a/sources/base/DetectionAutomatic.cpp +++ b/sources/base/DetectionAutomatic.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,10 +25,13 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include +#endif + #include #include -#include -#include +#include DetectionAutomatic::DetectionAutomatic() : _log(Logger::getInstance("SIGNAL_AUTO")), @@ -50,15 +53,15 @@ QJsonDocument DetectionAutomatic::startCalibration() { calibrationData.reset(); - calibrationData.backupHDR = GrabberWrapper::getInstance()->getHdrToneMappingEnabled(); + calibrationData.backupHDR = getHdrToneMappingEnabled(); if (_backupDecimation > 0) calibrationData.decimationFPS = _backupDecimation; else - calibrationData.decimationFPS = std::max(GrabberWrapper::getInstance()->getFpsSoftwareDecimation(), 1); + calibrationData.decimationFPS = std::max(getFpsSoftwareDecimation(), 1); - GrabberWrapper::getInstance()->setHdrToneMappingEnabled(0); - GrabberWrapper::getInstance()->setFpsSoftwareDecimation(1); + setHdrToneMappingEnabled(0); + setFpsSoftwareDecimation(1); calibrationData.tolerance = _errorTolerance; calibrationData.model = _modelTolerance; calibrationData.isRunning = true; @@ -79,8 +82,8 @@ QJsonDocument DetectionAutomatic::stopCalibration() { calibrationData.isRunning = false; calibrationData.status = "Calibration was interrupted"; - GrabberWrapper::getInstance()->setHdrToneMappingEnabled(calibrationData.backupHDR); - GrabberWrapper::getInstance()->setFpsSoftwareDecimation(calibrationData.decimationFPS); + setHdrToneMappingEnabled(calibrationData.backupHDR); + setFpsSoftwareDecimation(calibrationData.decimationFPS); Warning(_log, "%s", QSTRING_CSTR(calibrationData.status)); } @@ -183,17 +186,9 @@ DetectionAutomatic::calibrationPoint::calibrationPoint() b = 0; } -QString DetectionAutomatic::calibration::getSignature() -{ - auto info = GrabberWrapper::getInstance()->getVideoCurrentMode(); - return QString("%1 %2"). - arg(info[Grabber::currentVideoModeInfo::resolution]). - arg(info[Grabber::currentVideoModeInfo::device]); -} - -void DetectionAutomatic::calibration::buildPoints(int _width, int _height) +void DetectionAutomatic::calibration::buildPoints(QString _signature, int _width, int _height) { - signature = getSignature(); + signature = _signature; width = _width; height = _height; sdrPoint.clear(); @@ -239,8 +234,8 @@ void DetectionAutomatic::calibrateFrame(Image& image) { calibrationData.status = "Timeout out waiting for frames"; calibrationData.isRunning = false; - GrabberWrapper::getInstance()->setHdrToneMappingEnabled(calibrationData.backupHDR); - GrabberWrapper::getInstance()->setFpsSoftwareDecimation(calibrationData.decimationFPS); + setHdrToneMappingEnabled(calibrationData.backupHDR); + setFpsSoftwareDecimation(calibrationData.decimationFPS); Error(_log, "The calibration is finished. %s", QSTRING_CSTR(calibrationData.status)); return; } @@ -257,23 +252,21 @@ void DetectionAutomatic::calibrateFrame(Image& image) { calibrationData.status = "Stream width or height has changed during calibration"; calibrationData.isRunning = false; - GrabberWrapper::getInstance()->setHdrToneMappingEnabled(calibrationData.backupHDR); - GrabberWrapper::getInstance()->setFpsSoftwareDecimation(calibrationData.decimationFPS); + setHdrToneMappingEnabled(calibrationData.backupHDR); + setFpsSoftwareDecimation(calibrationData.decimationFPS); Error(_log, "The calibration is finished. %s", QSTRING_CSTR(calibrationData.status)); return; } - if (calibrationData.currentPhase == calibrationPhase::WAITING_FOR_SDR && - GrabberWrapper::getInstance()->getHdrToneMappingEnabled() == 0) + if (calibrationData.currentPhase == calibrationPhase::WAITING_FOR_SDR && getHdrToneMappingEnabled() == 0) { - calibrationData.buildPoints(image.width(), image.height()); + calibrationData.buildPoints(getSignature(), image.width(), image.height()); calibrationData.phaseEndTime = calibrationData.endTime; calibrationData.currentPhase = calibrationPhase::CALIBRATING_SDR; calibrationData.status = "Capturing SDR frames"; Debug(_log, "%s", QSTRING_CSTR(calibrationData.status)); } - else if (calibrationData.currentPhase == calibrationPhase::WAITING_FOR_HDR && - GrabberWrapper::getInstance()->getHdrToneMappingEnabled() == 1) + else if (calibrationData.currentPhase == calibrationPhase::WAITING_FOR_HDR && getHdrToneMappingEnabled() == 1) { calibrationData.phaseEndTime = calibrationData.endTime; calibrationData.currentPhase = calibrationPhase::CALIBRATING_HDR; @@ -315,7 +308,7 @@ void DetectionAutomatic::calibrateFrame(Image& image) calibrationData.status = QString("Processing SDR frames: %1 / 100").arg(calibrationData.currentSDRframe); if (calibrationData.currentSDRframe == 100) { - GrabberWrapper::getInstance()->setHdrToneMappingEnabled(1); + setHdrToneMappingEnabled(1); calibrationData.phaseStartTime = InternalClock::now() + 500; calibrationData.phaseEndTime = InternalClock::now() + 3000 + 500; calibrationData.currentPhase = calibrationPhase::WAITING_FOR_HDR; @@ -360,8 +353,8 @@ void DetectionAutomatic::calibrateFrame(Image& image) arg(calibrationData.sdrStat).arg(calibrationData.hdrStat).arg(calibrationData.sdrPoint.size()).arg(calibrationData.hdrPoint.size()).arg(res); Debug(_log, "%s", QSTRING_CSTR(calibrationData.status)); - GrabberWrapper::getInstance()->setHdrToneMappingEnabled(calibrationData.backupHDR); - GrabberWrapper::getInstance()->setFpsSoftwareDecimation(calibrationData.decimationFPS); + setHdrToneMappingEnabled(calibrationData.backupHDR); + setFpsSoftwareDecimation(calibrationData.decimationFPS); saveResult(); @@ -429,7 +422,7 @@ void DetectionAutomatic::saveResult() obj["calibration_hdr"] = hdr; QString saveData = QString(QJsonDocument(obj).toJson(QJsonDocument::Compact)); - HyperHdrIManager::getInstance()->saveCalibration(saveData); + emit SignalSaveCalibration(saveData); } } @@ -455,7 +448,7 @@ void DetectionAutomatic::resetStats() bool DetectionAutomatic::checkSignal(Image& image) { - int hdrMode = GrabberWrapper::getInstance()->getHdrToneMappingEnabled(); + int hdrMode = getHdrToneMappingEnabled(); if (checkData.width <= 0 || checkData.height <= 0 || checkData.quality <= 0 || checkData.sdrPoint.size() == 0 || checkData.hdrPoint.size() == 0) { @@ -493,12 +486,12 @@ bool DetectionAutomatic::checkSignal(Image& image) return true; } - if (checkData.signature != checkData.getSignature()) + if (checkData.signature != getSignature()) { QString oldStatus = checkData.status; checkData.status = "Calibration data signature is different from the current stream signature. Just a warning. Please run the calibration procedure in the panel grabber tab."; if (oldStatus != checkData.status) - Warning(_log, "%s (have: '%s' <> current: '%s')", QSTRING_CSTR(checkData.status), QSTRING_CSTR(checkData.signature), QSTRING_CSTR(checkData.getSignature())); + Warning(_log, "%s (have: '%s' <> current: '%s')", QSTRING_CSTR(checkData.status), QSTRING_CSTR(checkData.signature), QSTRING_CSTR(getSignature())); } std::vector& data = (hdrMode == 0) ? checkData.sdrPoint : checkData.hdrPoint; @@ -540,13 +533,13 @@ bool DetectionAutomatic::checkSignal(Image& image) { if (_saveResources) { - _backupDecimation = GrabberWrapper::getInstance()->getFpsSoftwareDecimation(); + _backupDecimation = getFpsSoftwareDecimation(); - int currentFPS = GrabberWrapper::getInstance()->getActualFps(); + int currentFPS = getActualFps(); int relax = std::max(currentFPS / 3, 1); - GrabberWrapper::getInstance()->setFpsSoftwareDecimation(relax); + setFpsSoftwareDecimation(relax); } Info(_log, "THE SIGNAL IS LOST"); @@ -572,7 +565,7 @@ bool DetectionAutomatic::checkSignal(Image& image) { if (_backupDecimation > 0) { - GrabberWrapper::getInstance()->setFpsSoftwareDecimation(_backupDecimation); + setFpsSoftwareDecimation(_backupDecimation); _backupDecimation = 0; } diff --git a/sources/base/DetectionManual.cpp b/sources/base/DetectionManual.cpp index c7dff8b9d..f14d5f5c8 100644 --- a/sources/base/DetectionManual.cpp +++ b/sources/base/DetectionManual.cpp @@ -1,4 +1,3 @@ - #include DetectionManual::DetectionManual() : diff --git a/sources/base/Grabber.cpp b/sources/base/Grabber.cpp index 3812eccaa..6ccdfdb33 100644 --- a/sources/base/Grabber.cpp +++ b/sources/base/Grabber.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,20 +25,24 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include + #include +#endif + #include #include -#include -#include +#include const QString Grabber::AUTO_SETTING = QString("auto"); const int Grabber::AUTO_INPUT = -1; const int Grabber::AUTO_FPS = 0; -Grabber::Grabber(const QString& configurationPath, const QString& grabberName, int width, int height, int cropLeft, int cropRight, int cropTop, int cropBottom) +Grabber::Grabber(const QString& configurationPath, const QString& grabberName) : _configurationPath(configurationPath) - , _width(width) - , _height(height) + , _width(0) + , _height(0) , _fps(Grabber::AUTO_FPS) , _input(Grabber::AUTO_INPUT) , _cropLeft(0) @@ -60,27 +64,26 @@ Grabber::Grabber(const QString& configurationPath, const QString& grabberName, i , _restartNeeded(false) , _initialized(false) , _fpsSoftwareDecimation(1) + , _hardware(false) , _actualVideoFormat(PixelFormat::NO_CHANGE) , _actualWidth(0) , _actualHeight(0) , _actualFPS(0) , _actualDeviceName("") - , _lutBuffer(NULL) + , _targetMonitorNits(200) , _lutBufferInit(false) , _lineLength(-1) , _frameByteSize(-1) , _signalDetectionEnabled(false) , _signalAutoDetectionEnabled(false) , _synchro(1) + , _benchmarkStatus(-1) + , _benchmarkMessage("") { - Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); } Grabber::~Grabber() { - if (_lutBuffer != NULL) - free(_lutBuffer); - _lutBuffer = NULL; } bool sortDevicePropertiesItem(const Grabber::DevicePropertiesItem& v1, const Grabber::DevicePropertiesItem& v2) @@ -99,6 +102,11 @@ void Grabber::setEnabled(bool enable) _enabled = enable; } +void Grabber::setMonitorNits(int nits) +{ + _targetMonitorNits = nits; +} + void Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) { if (_width > 0 && _height > 0) @@ -121,6 +129,11 @@ void Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTo } } +void Grabber::enableHardwareAcceleration(bool hardware) +{ + _hardware = hardware; +} + bool Grabber::trySetInput(int input) { if ((input >= 0) && (_input != input)) @@ -275,8 +288,7 @@ void Grabber::unblockAndRestart(bool running) uninit(); start(); - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::VIDEO_GRABBER), -1, "", -1, -1, -1, -1)); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(true, hyperhdr::PerformanceReportType::VIDEO_GRABBER, -1); } _blocked = false; @@ -391,6 +403,7 @@ void Grabber::resetCounter(int64_t from) frameStat.badFrame = 0; frameStat.goodFrame = 0; frameStat.segment = 0; + frameStat.directAccess = false; } int Grabber::getHdrToneMappingEnabled() @@ -412,10 +425,9 @@ void Grabber::loadLutFile(PixelFormat color, const QList& files) if (color == PixelFormat::NO_CHANGE) { - if (_lutBuffer == NULL) - _lutBuffer = (uint8_t*)malloc(LUT_FILE_SIZE + 4); + _lut.resize(LUT_FILE_SIZE + 4); - if (_lutBuffer != NULL) + if (_lut.data() != nullptr) { for (int y = 0; y < 256; y++) for (int u = 0; u < 256; u++) @@ -423,9 +435,9 @@ void Grabber::loadLutFile(PixelFormat color, const QList& files) { uint32_t ind_lutd = LUT_INDEX(y, u, v); ColorSys::yuv2rgb(y, u, v, - _lutBuffer[ind_lutd], - _lutBuffer[ind_lutd + 1], - _lutBuffer[ind_lutd + 2]); + _lut.data()[ind_lutd], + _lut.data()[ind_lutd + 1], + _lut.data()[ind_lutd + 2]); } _lutBufferInit = true; } @@ -466,10 +478,9 @@ void Grabber::loadLutFile(PixelFormat color, const QList& files) file.seek(index); - if (_lutBuffer == NULL) - _lutBuffer = (unsigned char*)malloc(length + 4); + _lut.resize(length + 4); - if (file.read((char*)_lutBuffer, LUT_FILE_SIZE) != LUT_FILE_SIZE) + if (file.read((char*)_lut.data(), LUT_FILE_SIZE) != LUT_FILE_SIZE) { Error(_log, "Error reading LUT file %s", QSTRING_CSTR(fileName3d)); } @@ -576,8 +587,7 @@ QMap Grabber::getVideoCurrentMode() cons return retVal; } - -void Grabber::processSystemFrameBGRA(uint8_t* source, int lineSize) +int Grabber::getTargetSystemFrameDimension(int& targetSizeX, int& targetSizeY) { int startX = _cropLeft; int startY = _cropTop; @@ -591,142 +601,103 @@ void Grabber::processSystemFrameBGRA(uint8_t* source, int lineSize) } int checkWidth = realSizeX; - int division = 1; + int divide = 1; while (checkWidth > _width) { - division++; - checkWidth = realSizeX / division; + divide++; + checkWidth = realSizeX / divide; } - int targetSizeX = realSizeX / division; - int targetSizeY = realSizeY / division; + targetSizeX = realSizeX / divide; + targetSizeY = realSizeY / divide; + + return divide; +} +void Grabber::processSystemFrameBGRA(uint8_t* source, int lineSize, bool useLut) +{ + int targetSizeX, targetSizeY; + int divide = getTargetSystemFrameDimension(targetSizeX, targetSizeY); Image image(targetSizeX, targetSizeY); - FrameDecoder::processSystemImageBGRA(image, targetSizeX, targetSizeY, startX, startY, source, _actualWidth, _actualHeight, division, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lutBuffer, lineSize); + FrameDecoder::processSystemImageBGRA(image, targetSizeX, targetSizeY, _cropLeft, _cropTop, source, _actualWidth, _actualHeight, divide, (_hdrToneMappingEnabled == 0 || !_lutBufferInit || !useLut) ? nullptr : _lut.data(), lineSize); if (_signalDetectionEnabled) { if (checkSignalDetectionManual(image)) - emit newFrame(image); + emit SignalNewCapturedFrame(image); } else - emit newFrame(image); + emit SignalNewCapturedFrame(image); } void Grabber::processSystemFrameBGR(uint8_t* source, int lineSize) { - int startX = _cropLeft; - int startY = _cropTop; - int realSizeX = _actualWidth - startX - _cropRight; - int realSizeY = _actualHeight - startY - _cropBottom; - - if (realSizeX <= 16 || realSizeY <= 16) - { - realSizeX = _actualWidth; - realSizeY = _actualHeight; - } - - int checkWidth = realSizeX; - int division = 1; - - while (checkWidth > _width) - { - division++; - checkWidth = realSizeX / division; - } - - int targetSizeX = realSizeX / division; - int targetSizeY = realSizeY / division; - + int targetSizeX, targetSizeY; + int divide = getTargetSystemFrameDimension(targetSizeX, targetSizeY); Image image(targetSizeX, targetSizeY); - FrameDecoder::processSystemImageBGR(image, targetSizeX, targetSizeY, startX, startY, source, _actualWidth, _actualHeight, division, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lutBuffer, lineSize); + FrameDecoder::processSystemImageBGR(image, targetSizeX, targetSizeY, _cropLeft, _cropTop, source, _actualWidth, _actualHeight, divide, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lut.data(), lineSize); if (_signalDetectionEnabled) { if (checkSignalDetectionManual(image)) - emit newFrame(image); + emit SignalNewCapturedFrame(image); } else - emit newFrame(image); + emit SignalNewCapturedFrame(image); } void Grabber::processSystemFrameBGR16(uint8_t* source, int lineSize) { - int startX = _cropLeft; - int startY = _cropTop; - int realSizeX = _actualWidth - startX - _cropRight; - int realSizeY = _actualHeight - startY - _cropBottom; - - if (realSizeX <= 16 || realSizeY <= 16) - { - realSizeX = _actualWidth; - realSizeY = _actualHeight; - } - - int checkWidth = realSizeX; - int division = 1; - - while (checkWidth > _width) - { - division++; - checkWidth = realSizeX / division; - } - - int targetSizeX = realSizeX / division; - int targetSizeY = realSizeY / division; - + int targetSizeX, targetSizeY; + int divide = getTargetSystemFrameDimension(targetSizeX, targetSizeY); Image image(targetSizeX, targetSizeY); - FrameDecoder::processSystemImageBGR16(image, targetSizeX, targetSizeY, startX, startY, source, _actualWidth, _actualHeight, division, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lutBuffer, lineSize); + FrameDecoder::processSystemImageBGR16(image, targetSizeX, targetSizeY, _cropLeft, _cropTop, source, _actualWidth, _actualHeight, divide, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lut.data(), lineSize); if (_signalDetectionEnabled) { if (checkSignalDetectionManual(image)) - emit newFrame(image); + emit SignalNewCapturedFrame(image); } else - emit newFrame(image); + emit SignalNewCapturedFrame(image); } void Grabber::processSystemFrameRGBA(uint8_t* source, int lineSize) { - int startX = _cropLeft; - int startY = _cropTop; - int realSizeX = _actualWidth - startX - _cropRight; - int realSizeY = _actualHeight - startY - _cropBottom; + int targetSizeX, targetSizeY; + int divide = getTargetSystemFrameDimension(targetSizeX, targetSizeY); + Image image(targetSizeX, targetSizeY); - if (realSizeX <= 16 || realSizeY <= 16) - { - realSizeX = _actualWidth; - realSizeY = _actualHeight; - } + FrameDecoder::processSystemImageRGBA(image, targetSizeX, targetSizeY, _cropLeft, _cropTop, source, _actualWidth, _actualHeight, divide, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lut.data(), lineSize); - int checkWidth = realSizeX; - int division = 1; - - while (checkWidth > _width) + if (_signalDetectionEnabled) { - division++; - checkWidth = realSizeX / division; + if (checkSignalDetectionManual(image)) + emit SignalNewCapturedFrame(image); } + else + emit SignalNewCapturedFrame(image); +} - int targetSizeX = realSizeX / division; - int targetSizeY = realSizeY / division; - +void Grabber::processSystemFramePQ10(uint8_t* source, int lineSize) +{ + int targetSizeX, targetSizeY; + int divide = getTargetSystemFrameDimension(targetSizeX, targetSizeY); Image image(targetSizeX, targetSizeY); - FrameDecoder::processSystemImageRGBA(image, targetSizeX, targetSizeY, startX, startY, source, _actualWidth, _actualHeight, division, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lutBuffer, lineSize); + FrameDecoder::processSystemImagePQ10(image, targetSizeX, targetSizeY, _cropLeft, _cropTop, source, _actualWidth, _actualHeight, divide, (_hdrToneMappingEnabled == 0 || !_lutBufferInit) ? nullptr : _lut.data(), lineSize); if (_signalDetectionEnabled) { if (checkSignalDetectionManual(image)) - emit newFrame(image); + emit SignalNewCapturedFrame(image); } else - emit newFrame(image); + emit SignalNewCapturedFrame(image); } void Grabber::setSignalDetectionEnable(bool enable) @@ -790,7 +761,7 @@ QJsonObject Grabber::getJsonInfo() QJsonArray availableInputs; QMultiMap inputs = getVideoDeviceInputs(devicePath); - for (auto input = inputs.begin(); input != inputs.end(); input++) + for (auto input = inputs.begin(); input != inputs.end(); ++input) { QJsonObject availableInput; availableInput["inputName"] = input.key(); @@ -905,13 +876,13 @@ QJsonObject Grabber::getJsonInfo() grabbers["current"] = current; - if (_lutBuffer != NULL) + if (_lut.data() != nullptr) { uint32_t checkSum = 0; for (int i = 0; i < 256; i += 2) for (int j = 32; j <= 160; j += 64) { - checkSum ^= *(reinterpret_cast(&(_lutBuffer[LUT_INDEX(j, i, (255 - i))]))); + checkSum ^= *(reinterpret_cast(&(_lut.data()[LUT_INDEX(j, i, (255 - i))]))); } grabbers["lutFastCRC"] = "0x" + QString("%1").arg(checkSum, 4, 16).toUpper(); } @@ -919,11 +890,63 @@ QJsonObject Grabber::getJsonInfo() return grabbers; } -void Grabber::alternativeCaching(bool alternative) +QString Grabber::getConfigurationPath() { + return _configurationPath; } -QString Grabber::getConfigurationPath() +QString Grabber::getSignature() { - return _configurationPath; + auto info = getVideoCurrentMode(); + return QString("%1 %2"). + arg(info[Grabber::currentVideoModeInfo::resolution]). + arg(info[Grabber::currentVideoModeInfo::device]); +} + +void Grabber::handleNewFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) +{ + frameStat.goodFrame++; + frameStat.averageFrame += InternalClock::nowPrecise() - _frameBegin; + + if (image.width() > 1 && image.height() > 1) + { + if (_signalAutoDetectionEnabled || isCalibrating()) + { + if (checkSignalDetectionAutomatic(image)) + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + } + else if (_signalDetectionEnabled) + { + if (checkSignalDetectionManual(image)) + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + } + if (_benchmarkStatus >= 0) + { + ColorRgb pixel = image(image.width() / 2, image.height() / 2); + if ((_benchmarkMessage == "white" && pixel.red > 120 && pixel.green > 120 && pixel.blue > 120) || + (_benchmarkMessage == "red" && pixel.red > 120 && pixel.green < 30 && pixel.blue < 30) || + (_benchmarkMessage == "green" && pixel.red < 30 && pixel.green > 120 && pixel.blue < 30) || + (_benchmarkMessage == "blue" && pixel.red < 30 && pixel.green < 40 && pixel.blue > 120) || + (_benchmarkMessage == "black" && pixel.red < 30 && pixel.green < 30 && pixel.blue < 30)) + + { + emit SignalBenchmarkUpdate(_benchmarkStatus, _benchmarkMessage); + _benchmarkStatus = -1; + _benchmarkMessage = ""; + } + } + } + else + frameStat.directAccess = true; +} + +void Grabber::benchmarkCapture(int status, QString message) +{ + _benchmarkStatus = status; + _benchmarkMessage = message; +} + +bool Grabber::isInitialized() +{ + return _initialized; } diff --git a/sources/base/GrabberHelper.cpp b/sources/base/GrabberHelper.cpp new file mode 100644 index 000000000..b7d731df4 --- /dev/null +++ b/sources/base/GrabberHelper.cpp @@ -0,0 +1,49 @@ +/* GrabberHelper.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +#include +#include + +void GrabberHelper::setGrabber(QObject* grabber) +{ + _grabberInstance = grabber; +} + +GrabberHelper::~GrabberHelper() +{ + delete _grabberInstance; +} + +SystemWrapper* GrabberHelper::systemWrapper() +{ + return qobject_cast(_grabberInstance); +} + +GrabberWrapper* GrabberHelper::grabberWrapper() +{ + return qobject_cast(_grabberInstance); +} diff --git a/sources/base/GrabberWrapper.cpp b/sources/base/GrabberWrapper.cpp index b6d0e5efc..e42c4f4a2 100644 --- a/sources/base/GrabberWrapper.cpp +++ b/sources/base/GrabberWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,52 +25,43 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include + #include + #include + #include +#endif + +#include #include #include #include -#include - #include #include -#include - -#include -#include -#include -#include +#include +#include -GrabberWrapper* GrabberWrapper::instance = nullptr; - -GrabberWrapper::GrabberWrapper(const QString& grabberName, Grabber* ggrabber) +GrabberWrapper::GrabberWrapper(const QString& grabberName) : _grabberName(grabberName) , _log(Logger::getInstance(grabberName)) , _configLoaded(false) - , _grabber(ggrabber) + , _grabber(nullptr) , _cecHdrStart(0) , _cecHdrStop(0) , _autoResume(false) , _isPaused(false) , _pausingModeEnabled(false) - , _benchmarkStatus(-1) - , _benchmarkMessage("") { - GrabberWrapper::instance = this; - - // connect the image forwarding - - connect(this, &GrabberWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setVideoImage); - - // listen for source requests - connect(GlobalSignals::getInstance(), &GlobalSignals::requestSource, this, &GrabberWrapper::handleSourceRequest); - - connect(this, &GrabberWrapper::cecKeyPressed, this, &GrabberWrapper::cecKeyPressedHandler); - - connect(this, &GrabberWrapper::setBrightnessContrastSaturationHue, this, &GrabberWrapper::setBrightnessContrastSaturationHueHandler); + Debug(_log, "Starting the grabber wrapper"); - connect(this, &GrabberWrapper::instancePauseChanged, this, &GrabberWrapper::instancePauseChangedHandler); + qRegisterMetaType>("Image"); + connect(this, &GrabberWrapper::SignalNewVideoImage, GlobalSignals::getInstance(), &GlobalSignals::SignalNewVideoImage); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalRequestComponent, this, &GrabberWrapper::signalRequestSourceHandler); + connect(this, &GrabberWrapper::SignalCecKeyPressed, this, &GrabberWrapper::signalCecKeyPressedHandler); + connect(this, &GrabberWrapper::SignalInstancePauseChanged, this, &GrabberWrapper::signalInstancePauseChangedHandler); } -void GrabberWrapper::instancePauseChangedHandler(int instance, bool isEnabled) +void GrabberWrapper::signalInstancePauseChangedHandler(int instance, bool isEnabled) { static int signature = 0; int trigger = 0; @@ -108,7 +99,7 @@ void GrabberWrapper::instancePauseChangedHandler(int instance, bool isEnabled) Warning(_log, "LEDs are off and you have enabled the pausing feature for the USB grabber. Pausing the video grabber now."); auto _running_clients_copy = _running_clients; _running_clients.clear(); - handleSourceRequest(hyperhdr::Components::COMP_VIDEOGRABBER, -1, false); + signalRequestSourceHandler(hyperhdr::Components::COMP_VIDEOGRABBER, -1, false); _running_clients = _running_clients_copy; _isPaused = true; } @@ -118,7 +109,7 @@ void GrabberWrapper::instancePauseChangedHandler(int instance, bool isEnabled) if (_isPaused) { Warning(_log, "LEDs are on. Resuming the video grabber now."); - handleSourceRequest(hyperhdr::Components::COMP_VIDEOGRABBER, -1, true); + signalRequestSourceHandler(hyperhdr::Components::COMP_VIDEOGRABBER, -1, true); _isPaused = false; } } @@ -130,9 +121,11 @@ void GrabberWrapper::instancePauseChangedHandler(int instance, bool isEnabled) void GrabberWrapper::setCecStartStop(int cecHdrStart, int cecHdrStop) { _cecHdrStart = cecHdrStart; - _cecHdrStop = cecHdrStop; + _cecHdrStop = cecHdrStop; Debug(_log, "CEC keycode. Start: %i, stop: %i", _cecHdrStart, _cecHdrStop); + + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_CEC, -1, isCEC()); } QJsonDocument GrabberWrapper::startCalibration() @@ -159,7 +152,7 @@ QJsonDocument GrabberWrapper::getCalibrationInfo() return _grabber->getCalibrationInfo(); } -void GrabberWrapper::cecKeyPressedHandler(int key) +void GrabberWrapper::signalCecKeyPressedHandler(int key) { if (_grabber != nullptr) { @@ -179,33 +172,7 @@ bool GrabberWrapper::isCEC() return (_cecHdrStart > 0) || (_cecHdrStop > 0); } -void GrabberWrapper::newFrame(const Image& image) -{ - if (_benchmarkStatus >= 0) - { - ColorRgb pixel = image(image.width() / 2, image.height() / 2); - if ((_benchmarkMessage == "white" && pixel.red > 120 && pixel.green > 120 && pixel.blue > 120) || - (_benchmarkMessage == "red" && pixel.red > 120 && pixel.green < 30 && pixel.blue < 30) || - (_benchmarkMessage == "green" && pixel.red < 30 && pixel.green > 120 && pixel.blue < 30) || - (_benchmarkMessage == "blue" && pixel.red < 30 && pixel.green < 40 && pixel.blue > 120) || - (_benchmarkMessage == "black" && pixel.red < 30 && pixel.green < 30 && pixel.blue < 30)) - - { - emit benchmarkUpdate(_benchmarkStatus, _benchmarkMessage); - _benchmarkStatus = -1; - _benchmarkMessage = ""; - } - } - - if (image.setBufferCacheSize()) - { - Warning(_log, "Detected the video frame size changed (%ix%i). Cache buffer was cleared.", image.width(), image.height()); - } - - emit systemImage(_grabberName, image); -} - -void GrabberWrapper::readError(const char* err) +void GrabberWrapper::capturingExceptionHandler(const char* err) { Error(_log, "Grabber signals error (%s)", err); } @@ -213,6 +180,7 @@ void GrabberWrapper::readError(const char* err) GrabberWrapper::~GrabberWrapper() { Debug(_log, "Closing grabber: %s", QSTRING_CSTR(_grabberName)); + _grabber = nullptr; } QJsonObject GrabberWrapper::getJsonInfo() @@ -258,9 +226,16 @@ QJsonObject GrabberWrapper::getJsonInfo() return grabbers; } -void GrabberWrapper::handleSourceRequest(hyperhdr::Components component, int instanceIndex, bool listen) +void GrabberWrapper::signalRequestSourceHandler(hyperhdr::Components component, int instanceIndex, bool listen) { - if (component == hyperhdr::Components::COMP_VIDEOGRABBER) + if (component == hyperhdr::Components::COMP_HDR) + { + if (instanceIndex >= 0) + emit SignalSetNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, _grabber->getHdrToneMappingEnabled()); + else + setHdrToneMappingEnabled(listen); + } + else if (component == hyperhdr::Components::COMP_VIDEOGRABBER) { if (instanceIndex >= 0) { @@ -282,9 +257,9 @@ void GrabberWrapper::handleSourceRequest(hyperhdr::Components component, int ins //update state QString currentDevice = "", currentVideoMode = ""; - emit StateChanged(currentDevice, currentVideoMode); + emit SignalVideoStreamChanged(currentDevice, currentVideoMode); - emit PerformanceCounters::getInstance()->removeCounter(static_cast(PerformanceReportType::VIDEO_GRABBER), -1); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(false, PerformanceReportType::VIDEO_GRABBER, -1); } else { @@ -300,13 +275,9 @@ void GrabberWrapper::handleSourceRequest(hyperhdr::Components component, int ins if (currentInfo.contains(Grabber::currentVideoModeInfo::resolution)) currentVideoMode = currentInfo[Grabber::currentVideoModeInfo::resolution]; - emit StateChanged(currentDevice, currentVideoMode); + emit SignalVideoStreamChanged(currentDevice, currentVideoMode); - if (result) - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::VIDEO_GRABBER), -1, "", -1, -1, -1, -1)); - else - emit PerformanceCounters::getInstance()->removeCounter(static_cast(PerformanceReportType::VIDEO_GRABBER), -1); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(result, PerformanceReportType::VIDEO_GRABBER, -1); } } } @@ -323,16 +294,21 @@ QMap GrabberWrapper::getVideoCurrentMode bool GrabberWrapper::start() { if (_grabber != nullptr) - return _grabber->start(); + { + if (_grabber->isInitialized()) + return true; + else + return _grabber->start(); + } else return false; } void GrabberWrapper::stop() { - emit PerformanceCounters::getInstance()->removeCounter(static_cast(PerformanceReportType::VIDEO_GRABBER), -1); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(false, PerformanceReportType::VIDEO_GRABBER, -1); - if (_grabber != nullptr) + if (_grabber != nullptr && _grabber->isInitialized()) _grabber->stop(); } @@ -370,11 +346,7 @@ void GrabberWrapper::setHdrToneMappingEnabled(int mode) { if (_grabber != NULL) { - _grabber->setHdrToneMappingEnabled(mode); - - // make emit - emit HdrChanged(mode); - emit HyperHdrIManager::getInstance()->setNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, (mode != 0)); + _grabber->setHdrToneMappingEnabled(mode); } } @@ -386,16 +358,6 @@ int GrabberWrapper::getHdrToneMappingEnabled() return 0; } -int GrabberWrapper::getFpsSoftwareDecimation() -{ - return _grabber->getFpsSoftwareDecimation(); -} - -int GrabberWrapper::getActualFps() -{ - return _grabber->getActualFps(); -} - void GrabberWrapper::setFpsSoftwareDecimation(int decimation) { if (_grabber != nullptr) @@ -408,7 +370,7 @@ void GrabberWrapper::setEncoding(QString enc) _grabber->setEncoding(enc); } -void GrabberWrapper::setBrightnessContrastSaturationHueHandler(int brightness, int contrast, int saturation, int hue) +void GrabberWrapper::setBrightnessContrastSaturationHue(int brightness, int contrast, int saturation, int hue) { if (_grabber != nullptr) _grabber->setBrightnessContrastSaturationHue(brightness, contrast, saturation, hue); @@ -450,13 +412,15 @@ DetectionAutomatic::calibrationPoint GrabberWrapper::parsePoint(int width, int h void GrabberWrapper::revive() { if (_grabber != nullptr && _autoResume) - QTimer::singleShot(3000, _grabber, SLOT(revive())); + QTimer::singleShot(3000, _grabber.get(), &Grabber::revive); } void GrabberWrapper::benchmarkCapture(int status, QString message) { - _benchmarkStatus = status; - _benchmarkMessage = message; + if (_grabber != nullptr) + { + _grabber->benchmarkCapture(status, message); + } } bool GrabberWrapper::getAutoResume() @@ -606,10 +570,6 @@ void GrabberWrapper::handleSettingsUpdate(settings::type type, const QJsonDocume _grabber->setQFrameDecimation(obj["qFrame"].toBool(false)); - bool frameCache = obj["videoCache"].toBool(true); - Debug(_log, "Frame cache is: %s", (frameCache) ? "enabled" : "disabled"); - VideoMemoryManager::enableCache(frameCache); - _grabber->unblockAndRestart(_configLoaded); } catch (...) @@ -628,7 +588,14 @@ void GrabberWrapper::handleSettingsUpdate(settings::type type, const QJsonDocume if (currentInfo.contains(Grabber::currentVideoModeInfo::resolution)) currentVideoMode = currentInfo[Grabber::currentVideoModeInfo::resolution]; - emit StateChanged(currentDevice, currentVideoMode); + emit SignalVideoStreamChanged(currentDevice, currentVideoMode); } } +QString GrabberWrapper::getVideoCurrentModeResolution() +{ + QMap mode = _grabber->getVideoCurrentMode(); + QString vidMode = mode.contains(Grabber::currentVideoModeInfo::resolution) ? mode[Grabber::currentVideoModeInfo::resolution] : ""; + return vidMode; +} + diff --git a/sources/base/HyperHdrIManager.cpp b/sources/base/HyperHdrIManager.cpp deleted file mode 100644 index 908cd7bc7..000000000 --- a/sources/base/HyperHdrIManager.cpp +++ /dev/null @@ -1,344 +0,0 @@ -#include - -#include -#include -#include - -// qt -#include - -HyperHdrIManager* HyperHdrIManager::HIMinstance; - -HyperHdrIManager::HyperHdrIManager(const QString& rootPath, QObject* parent, bool readonlyMode) - : QObject(parent) - , _log(Logger::getInstance("HYPERMANAGER")) - , _instanceTable(new InstanceTable(rootPath, this, readonlyMode)) - , _rootPath(rootPath) - , _readonlyMode(readonlyMode) - , _fireStarter(0) -{ - HIMinstance = this; - qRegisterMetaType("InstanceState"); - connect(this, &HyperHdrIManager::instanceStateChanged, this, &HyperHdrIManager::handleInstanceStateChange); -} - -void HyperHdrIManager::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name) -{ - switch (state) - { - case InstanceState::H_STARTED: - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::INSTANCE), -1, name, -1, -1, -1, -1, instance)); - break; - case InstanceState::H_STOPPED: - emit PerformanceCounters::getInstance()->removeCounter(static_cast(PerformanceReportType::INSTANCE), instance); - emit PerformanceCounters::getInstance()->removeCounter(static_cast(PerformanceReportType::LED), instance); - break; - default: - break; - } -} - - -HyperHdrInstance* HyperHdrIManager::getHyperHdrInstance(quint8 instance) -{ - if (_runningInstances.contains(instance)) - return _runningInstances.value(instance); - - Warning(_log, "The requested instance index '%d' with name '%s' isn't running, return main instance", instance, QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); - return _runningInstances.value(0); -} - -QVector HyperHdrIManager::getInstanceData() const -{ - QVector instances = _instanceTable->getAllInstances(); - for (auto& entry : instances) - { - // add running state - entry["running"] = _runningInstances.contains(entry["instance"].toInt()); - } - return instances; -} - -bool HyperHdrIManager::areInstancesReady() -{ - if (_fireStarter > 0) - _fireStarter--; - - return (_fireStarter < 1); -} - -void HyperHdrIManager::startAll() -{ - auto instanceList = _instanceTable->getAllInstances(true); - - _fireStarter = instanceList.count(); - - for (const auto& entry : instanceList) - { - startInstance(entry["instance"].toInt()); - } - - if (GrabberWrapper::getInstance() != nullptr) - emit setNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, (GrabberWrapper::getInstance()->getHdrToneMappingEnabled() != 0)); -} - -void HyperHdrIManager::stopAll() -{ - // copy the instances due to loop corruption, even with .erase() return next iter - QMap instCopy = _runningInstances; - for (const auto instance : instCopy) - { - instance->stop(); - } -} - -void HyperHdrIManager::setSmoothing(int time) -{ - QMap instCopy = _runningInstances; - - for (const auto instance : instCopy) - QTimer::singleShot(0, instance, [=]() { instance->setSmoothing(time); }); -} - -QJsonObject HyperHdrIManager::getAverageColor(quint8 index) -{ - HyperHdrInstance* instance = HyperHdrIManager::getHyperHdrInstance(index); - QJsonObject res; - - SAFE_CALL_0_RET(instance, getAverageColor, QJsonObject, res); - - return res; -} - -bool HyperHdrIManager::isCEC() -{ - QMap instCopy = _runningInstances; - for (const auto instance : instCopy) - { - if (instance->isCEC()) - return true; - } - - return false; -} - -void HyperHdrIManager::setSignalStateByCEC(bool enable) -{ - QMap instCopy = _runningInstances; - for (const auto instance : instCopy) - { - instance->setSignalStateByCEC(enable); - } -} - -void HyperHdrIManager::toggleStateAllInstances(bool pause) -{ - // copy the instances due to loop corruption, even with .erase() return next iter - QMap instCopy = _runningInstances; - for (const auto instance : instCopy) - { - emit instance->compStateChangeRequest(hyperhdr::COMP_ALL, pause); - } -} - -void HyperHdrIManager::hibernate(bool wakeUp) -{ - if (!wakeUp) - { - Warning(_log, "The system is going to sleep"); - toggleStateAllInstances(false); - } - else - { - Warning(_log, "The system is going to wake up"); - QTimer::singleShot(3000, [this]() { toggleStateAllInstances(true); }); - } -} - -bool HyperHdrIManager::startInstance(quint8 inst, bool block, QObject* caller, int tan) -{ - if (_instanceTable->instanceExist(inst)) - { - if (!_runningInstances.contains(inst) && !_startQueue.contains(inst)) - { - QThread* hyperhdrThread = new QThread(); - hyperhdrThread->setObjectName("HyperHdrThread"); - HyperHdrInstance* hyperhdr = new HyperHdrInstance(inst, _readonlyMode, _instanceTable->getNamebyIndex(inst)); - hyperhdr->moveToThread(hyperhdrThread); - // setup thread management - connect(hyperhdrThread, &QThread::started, hyperhdr, &HyperHdrInstance::start); - connect(hyperhdr, &HyperHdrInstance::started, this, &HyperHdrIManager::handleStarted); - connect(hyperhdr, &HyperHdrInstance::finished, this, &HyperHdrIManager::handleFinished); - connect(hyperhdr, &HyperHdrInstance::finished, hyperhdrThread, &QThread::quit, Qt::DirectConnection); - - // setup further connections - // from HyperHDR - connect(hyperhdr, &HyperHdrInstance::settingsChanged, this, &HyperHdrIManager::settingsChanged); - - connect(hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &HyperHdrIManager::compStateChangeRequest); - - connect(this, &HyperHdrIManager::setNewComponentStateToAllInstances, hyperhdr, &HyperHdrInstance::setNewComponentState); - - // add to queue and start - _startQueue << inst; - hyperhdrThread->start(); - - // update db - _instanceTable->setLastUse(inst); - _instanceTable->setEnable(inst, true); - - if (block) - { - while (!hyperhdrThread->isRunning()) {}; - } - - if (!_pendingRequests.contains(inst) && caller != nullptr) - { - PendingRequests newDef{ caller, tan }; - _pendingRequests[inst] = newDef; - } - - return true; - } - Debug(_log, "Can't start Hyperhdr instance index '%d' with name '%s' it's already running or queued for start", inst, QSTRING_CSTR(_instanceTable->getNamebyIndex(inst))); - return false; - } - Debug(_log, "Can't start Hyperhdr instance index '%d' it doesn't exist in DB", inst); - return false; -} - -bool HyperHdrIManager::stopInstance(quint8 inst) -{ - // inst 0 can't be stopped - if (!isInstAllowed(inst)) - return false; - - if (_instanceTable->instanceExist(inst)) - { - if (_runningInstances.contains(inst)) - { - // notify a ON_STOP rather sooner than later, queued signal listener should have some time to drop the pointer before it's deleted - emit instanceStateChanged(InstanceState::H_ON_STOP, inst); - HyperHdrInstance* hyperhdr = _runningInstances.value(inst); - hyperhdr->stop(); - - // update db - _instanceTable->setEnable(inst, false); - - return true; - } - Debug(_log, "Can't stop HyperHDR instance index '%d' with name '%s' it's not running'", inst, QSTRING_CSTR(_instanceTable->getNamebyIndex(inst))); - return false; - } - Debug(_log, "Can't stop HyperHDR instance index '%d' it doesn't exist in DB", inst); - return false; -} - -bool HyperHdrIManager::createInstance(const QString& name, bool start) -{ - quint8 inst; - if (_instanceTable->createInstance(name, inst)) - { - Info(_log, "New Hyperhdr instance created with name '%s'", QSTRING_CSTR(name)); - emit instanceStateChanged(InstanceState::H_CREATED, inst, name); - emit change(); - - if (start) - startInstance(inst); - return true; - } - return false; -} - -bool HyperHdrIManager::deleteInstance(quint8 inst) -{ - // inst 0 can't be deleted - if (!isInstAllowed(inst)) - return false; - - // stop it if required as blocking and wait - stopInstance(inst); - - if (_instanceTable->deleteInstance(inst)) - { - Info(_log, "Hyperhdr instance with index '%d' has been deleted", inst); - emit instanceStateChanged(InstanceState::H_DELETED, inst); - emit change(); - - return true; - } - return false; -} - -bool HyperHdrIManager::saveName(quint8 inst, const QString& name) -{ - if (_instanceTable->saveName(inst, name)) - { - emit change(); - return true; - } - return false; -} - -void HyperHdrIManager::handleFinished() -{ - HyperHdrInstance* hyperhdr = qobject_cast(sender()); - quint8 instance = hyperhdr->getInstanceIndex(); - - Info(_log, "HyperHDR instance '%s' has been stopped", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); - - _runningInstances.remove(instance); - hyperhdr->thread()->deleteLater(); - hyperhdr->deleteLater(); - emit instanceStateChanged(InstanceState::H_STOPPED, instance); - emit change(); -} - -void HyperHdrIManager::handleStarted() -{ - HyperHdrInstance* hyperhdr = qobject_cast(sender()); - quint8 instance = hyperhdr->getInstanceIndex(); - - Info(_log, "HyperHDR instance '%s' has been started", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); - - _startQueue.removeAll(instance); - _runningInstances.insert(instance, hyperhdr); - emit instanceStateChanged(InstanceState::H_STARTED, instance, _instanceTable->getNamebyIndex(instance)); - emit change(); - - if (_pendingRequests.contains(instance)) - { - PendingRequests def = _pendingRequests.take(instance); - emit startInstanceResponse(def.caller, def.tan); - _pendingRequests.remove(instance); - } -} - -const QJsonObject HyperHdrIManager::getBackup() -{ - return _instanceTable->getBackup(); -} - - -QString HyperHdrIManager::restoreBackup(const QJsonObject& message) -{ - QString error("Empty instance table manager"); - - if (_instanceTable != nullptr) - { - error = _instanceTable->restoreBackup(message); - } - - return error; -} - -void HyperHdrIManager::saveCalibration(QString saveData) -{ - HyperHdrInstance* instance = HyperHdrIManager::getHyperHdrInstance(0); - - if (instance != nullptr) - QUEUE_CALL_1(instance, saveCalibration, QString, saveData) - else - Error(_log, "Hyperhdr instance is not running...can't save the calibration data"); -} diff --git a/sources/base/HyperHdrInstance.cpp b/sources/base/HyperHdrInstance.cpp index 7a974bcbc..07b7a3578 100644 --- a/sources/base/HyperHdrInstance.cpp +++ b/sources/base/HyperHdrInstance.cpp @@ -1,128 +1,164 @@ -// STL includes -#include -#include - -// QT includes -#include -#include -#include -#include +/* HyperHdrInstance.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include + #include + + #include +#endif #include - #include -#include -#include -#include -#include - -// utils +#include #include -#include - -// LedDevice includes #include #include - -#include -#include - -// effect engine includes +#include +#include #include - -// settingsManagaer -#include - -// BGEffectHandler -#include - -// CaptureControl (Daemon capture) +#include #include - -// SystemControl (Daemon capture) #include - #include +#include +#include +#include +#include +#include +#include +#include -// Boblight #if defined(ENABLE_BOBLIGHT) -#include + #include #else -class BoblightServer {}; + class BoblightServer {}; #endif -#include -#include - - +std::atomic HyperHdrInstance::_signalTerminate(false); +std::atomic HyperHdrInstance::_totalRunningCount(0); HyperHdrInstance::HyperHdrInstance(quint8 instance, bool readonlyMode, QString name) : QObject() , _instIndex(instance) , _bootEffect(QTime::currentTime().addSecs(5)) - , _imageProcessingUnit(nullptr) - , _settingsManager(new SettingsManager(instance, this, readonlyMode)) - , _componentRegister(this) - , _ledString(LedString::createLedString(getSetting(settings::type::LEDS).array(), LedString::createColorOrder(getSetting(settings::type::DEVICE).object()))) - , _imageProcessor(new ImageProcessor(_ledString, this)) - , _muxer(instance, static_cast(_ledString.leds().size()), this) - , _raw2ledAdjustment(MultiColorAdjustment::createLedColorsAdjustment(instance, static_cast(_ledString.leds().size()), getSetting(settings::type::COLOR).object())) + , _ledString() + , _instanceConfig(nullptr) + , _componentController(nullptr) + , _imageProcessor(nullptr) + , _muxer(nullptr) + , _ledColorCalibration(nullptr) , _ledDeviceWrapper(nullptr) , _smoothing(nullptr) , _effectEngine(nullptr) - , _messageForwarder(nullptr) - , _log(Logger::getInstance(QString("HYPERHDR%1").arg(instance))) - , _hwLedCount() - , _ledGridSize(LedString::getLedLayoutGridSize(getSetting(settings::type::LEDS).array())) - , _BGEffectHandler(nullptr) , _videoControl(nullptr) , _systemControl(nullptr) - , _globalLedBuffer(_ledString.leds().size(), ColorRgb::BLACK) , _boblightServer(nullptr) , _rawUdpServer(nullptr) + , _log(nullptr) + , _hwLedCount() + , _ledGridSize() + , _currentLedColors() , _name((name.isEmpty()) ? QString("INSTANCE%1").arg(instance) : name) , _readOnlyMode(readonlyMode) - { - + _totalRunningCount++; } -bool HyperHdrInstance::isCEC() +HyperHdrInstance::~HyperHdrInstance() { - if ((_systemControl != nullptr && _systemControl->isCEC()) || - (_videoControl != nullptr && _videoControl->isCEC()) || - (GrabberWrapper::getInstance() != nullptr && GrabberWrapper::getInstance()->isCEC()) - ) - return true; + Info(_log, "Stopping and releasing components of a HyperHDR instance..."); - return false; + disconnect(GlobalSignals::getInstance(), nullptr, this, nullptr); + disconnect(this, nullptr, nullptr, nullptr); + + if (_muxer != nullptr) + clear(-1, true); + + _boblightServer = nullptr; + Info(_log, "[ 1/9] Releasing HyperHDR%i->UdpServer...", _instIndex); + _rawUdpServer = nullptr; + Info(_log, "[ 2/9] Releasing HyperHDR%i->VideoControl...", _instIndex); + _videoControl = nullptr; + Info(_log, "[ 3/9] Releasing HyperHDR%i->SystemControl...", _instIndex); + _systemControl = nullptr; + Info(_log, "[ 4/9] Releasing HyperHDR%i->EffectEngine...", _instIndex); + _effectEngine = nullptr; + Info(_log, "[ 5/9] Releasing HyperHDR%i->ColorCalibration...", _instIndex); + _ledColorCalibration = nullptr; + Info(_log, "[ 6/9] Releasing HyperHDR%i->InstanceConfiguration...", _instIndex); + _instanceConfig = nullptr; + Info(_log, "[ 7/9] Releasing HyperHDR%i->LED driver wrapper...", _instIndex); + _ledDeviceWrapper = nullptr; + Info(_log, "[ 8/9] Releasing HyperHDR%i->ComponentController...", _instIndex); + _componentController = nullptr; + Info(_log, "[ 9/9] Releasing HyperHDR%i->Muxer...", _instIndex); + _muxer = nullptr; + + _totalRunningCount--; + + Warning(_log, "The instance has been released succesfully [left: %i]", (int)_totalRunningCount); } -void HyperHdrInstance::setSignalStateByCEC(bool enable) -{ - if (_systemControl != nullptr && _systemControl->isCEC()) - { - emit _systemControl->setSysCaptureEnableSignal(enable); - } - if (_videoControl != nullptr && _videoControl->isCEC()) +void HyperHdrInstance::start() +{ + _log = Logger::getInstance(QString("HYPERHDR%1").arg(_instIndex)); + + if (_signalTerminate) { - emit _videoControl->setUsbCaptureEnableSignal(enable); + Warning(_log, "Signal terminate detected. Skipping start of the instance"); + return; } -} + Info(_log, "Starting the instance"); -HyperHdrInstance::~HyperHdrInstance() -{ - freeObjects(); -} + _instanceConfig = std::unique_ptr(new InstanceConfig(false, _instIndex, this, _readOnlyMode)); + _componentController = std::unique_ptr(new ComponentController(this)); + connect(_componentController.get(), &ComponentController::SignalComponentStateChanged, this, &HyperHdrInstance::SignalComponentStateChanged); + _ledString = LedString::createLedString(getSetting(settings::type::LEDS).array(), LedString::createColorOrder(getSetting(settings::type::DEVICE).object())); + _imageProcessor = std::unique_ptr(new ImageToLedManager(_ledString, this)); + _muxer = std::unique_ptr(new Muxer(_instIndex, static_cast(_ledString.leds().size()), this)); + connect(_muxer.get(), &Muxer::SignalPrioritiesChanged, this, &HyperHdrInstance::SignalPrioritiesChanged); + connect(_muxer.get(), &Muxer::SignalVisiblePriorityChanged, this, &HyperHdrInstance::SignalVisiblePriorityChanged); + connect(_muxer.get(), &Muxer::SignalVisibleComponentChanged, this, &HyperHdrInstance::SignalVisibleComponentChanged); + _ledColorCalibration = std::unique_ptr(new LedCalibration(_instIndex, static_cast(_ledString.leds().size()), getSetting(settings::type::COLOR).object())); + _ledGridSize = LedString::getLedLayoutGridSize(getSetting(settings::type::LEDS).array()); + _currentLedColors = std::vector(_ledString.leds().size(), ColorRgb::BLACK); -void HyperHdrInstance::start() -{ - Info(_log, "Led strip RGB order is: %s", QSTRING_CSTR(colorOrderToString(_ledString.colorOrder))); + Info(_log, "Led strip RGB order is: %s", QSTRING_CSTR(LedString::colorOrderToString(_ledString.colorOrder))); - connect(_settingsManager, &SettingsManager::settingsChanged, this, &HyperHdrInstance::settingsChanged); + connect(_instanceConfig.get(), &InstanceConfig::SignalInstanceSettingsChanged, this, &HyperHdrInstance::SignalInstanceSettingsChanged); - if (!_raw2ledAdjustment->verifyAdjustments()) + if (!_ledColorCalibration->verifyAdjustments()) { Warning(_log, "At least one led has no color calibration, please add all leds from your led layout to an 'LED index' field!"); } @@ -130,98 +166,80 @@ void HyperHdrInstance::start() // handle hwLedCount _hwLedCount = qMax(getSetting(settings::type::DEVICE).object()["hardwareLedCount"].toInt(1), getLedCount()); - connect(&_muxer, &PriorityMuxer::visiblePriorityChanged, this, &HyperHdrInstance::update); - connect(&_muxer, &PriorityMuxer::visiblePriorityChanged, this, &HyperHdrInstance::handlePriorityChangedLedDevice); - connect(&_muxer, &PriorityMuxer::visibleComponentChanged, this, &HyperHdrInstance::handleVisibleComponentChanged); - - // listens for ComponentRegister changes of COMP_ALL to perform core enable/disable actions - + connect(this, &HyperHdrInstance::SignalVisiblePriorityChanged, this, &HyperHdrInstance::update); + connect(this, &HyperHdrInstance::SignalVisiblePriorityChanged, this, &HyperHdrInstance::handlePriorityChangedLedDevice); + connect(this, &HyperHdrInstance::SignalVisibleComponentChanged, this, &HyperHdrInstance::handleVisibleComponentChanged); // listen for settings updates of this instance (LEDS & COLOR) - connect(_settingsManager, &SettingsManager::settingsChanged, this, &HyperHdrInstance::handleSettingsUpdate); - - // procesing unit - _imageProcessingUnit = std::unique_ptr(new ImageProcessingUnit(this)); - connect(_imageProcessingUnit.get(), &ImageProcessingUnit::dataReadySignal, this, &HyperHdrInstance::updateResult); + connect(_instanceConfig.get(), &InstanceConfig::SignalInstanceSettingsChanged, this, &HyperHdrInstance::handleSettingsUpdate); // initialize LED-devices QJsonObject ledDevice = getSetting(settings::type::DEVICE).object(); ledDevice["currentLedCount"] = _hwLedCount; // Inject led count info // smoothing - _smoothing = new LinearSmoothing(getSetting(settings::type::SMOOTHING), this); - connect(this, &HyperHdrInstance::settingsChanged, _smoothing, &LinearSmoothing::handleSettingsUpdate); + _smoothing = std::unique_ptr(new Smoothing(getSetting(settings::type::SMOOTHING), this)); + connect(this, &HyperHdrInstance::SignalInstanceSettingsChanged, _smoothing.get(), &Smoothing::handleSettingsUpdate); + connect(this, &HyperHdrInstance::SignalSmoothingClockTick, _smoothing.get(), &Smoothing::SignalMasterClockTick, Qt::DirectConnection); - _ledDeviceWrapper = new LedDeviceWrapper(this); - connect(this, &HyperHdrInstance::compStateChangeRequest, _ledDeviceWrapper, &LedDeviceWrapper::handleComponentState); - connect(this, &HyperHdrInstance::ledDeviceData, _ledDeviceWrapper, &LedDeviceWrapper::updateLeds); - _ledDeviceWrapper->createLedDevice(ledDevice); - - // create the message forwarder only on main instance - if (_instIndex == 0) - { - _messageForwarder = new MessageForwarder(this); - } + _ledDeviceWrapper = std::unique_ptr(new LedDeviceWrapper(this)); + connect(this, &HyperHdrInstance::SignalRequestComponent, _ledDeviceWrapper.get(), &LedDeviceWrapper::handleComponentState); + _ledDeviceWrapper->createLedDevice(ledDevice, _smoothing->GetSuggestedInterval()); // create the effect engine; needs to be initialized after smoothing! - _effectEngine = new EffectEngine(this); - connect(&_muxer, &PriorityMuxer::visiblePriorityChanged, _effectEngine, &EffectEngine::visiblePriorityChanged); - - // handle background effect - _BGEffectHandler = new BGEffectHandler(this); + _effectEngine = std::unique_ptr(new EffectEngine(this)); + connect(this, &HyperHdrInstance::SignalVisiblePriorityChanged, _effectEngine.get(), &EffectEngine::visiblePriorityChanged); // create the Daemon capture interface - _videoControl = new VideoControl(this); + _videoControl = std::unique_ptr(new VideoControl(this)); - _systemControl = new SystemControl(this); + _systemControl = std::unique_ptr(new SystemControl(this)); // forwards global signals to the corresponding slots - connect(GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput, this, &HyperHdrInstance::registerInput); - connect(GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput, this, &HyperHdrInstance::clear); - connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor, this, &HyperHdrInstance::setColor); - connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &HyperHdrInstance::setInputImage); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalClearGlobalInput, this, &HyperHdrInstance::clear); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalColor, this, &HyperHdrInstance::signalSetGlobalColorHandler); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalImage, this, &HyperHdrInstance::signalSetGlobalImageHandler); // if there is no startup / background effect and no sending capture interface we probably want to push once BLACK (as PrioMuxer won't emit a priority change) update(); #ifdef ENABLE_BOBLIGHT // boblight, can't live in global scope as it depends on layout - _boblightServer = new BoblightServer(this, getSetting(settings::type::BOBLSERVER)); - connect(this, &HyperHdrInstance::settingsChanged, _boblightServer, &BoblightServer::handleSettingsUpdate); + _boblightServer = std::unique_ptr(new BoblightServer(this, getSetting(settings::type::BOBLSERVER))); + connect(this, &HyperHdrInstance::SignalInstanceSettingsChanged, _boblightServer.get(), &BoblightServer::handleSettingsUpdate); #endif - _rawUdpServer = new RawUdpServer(this, getSetting(settings::type::RAWUDPSERVER)); - connect(this, &HyperHdrInstance::settingsChanged, _rawUdpServer, &RawUdpServer::handleSettingsUpdate); + _rawUdpServer = std::unique_ptr(new RawUdpServer(this, getSetting(settings::type::RAWUDPSERVER))); + connect(this, &HyperHdrInstance::SignalInstanceSettingsChanged, _rawUdpServer.get(), &RawUdpServer::handleSettingsUpdate); + + // handle background effect + QUEUE_CALL_2(this, handleSettingsUpdate, + settings::type, settings::type::BGEFFECT, + QJsonDocument, getSetting(settings::type::BGEFFECT)); + + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::Components::COMP_HDR, _instIndex, true); // instance initiated, enter thread event loop - emit started(); + emit SignalInstanceJustStarted(); + + // exit + Info(_log, "The instance is running"); } -void HyperHdrInstance::stop() +void HyperHdrInstance::signalTerminateTriggered() { - Info(_log, "Stopping the instance..."); + _signalTerminate = true; + LedDevice::signalTerminateTriggered(); +} - emit finished(); - thread()->wait(); +bool HyperHdrInstance::isTerminated() +{ + return _signalTerminate; } -void HyperHdrInstance::freeObjects() +int HyperHdrInstance::getTotalRunningCount() { - Info(_log, "Freeing the objects..."); - - // switch off all leds - clear(-1, true); - - // delete components on exit - delete _boblightServer; - delete _rawUdpServer; - delete _videoControl; - delete _systemControl; - delete _effectEngine; - delete _raw2ledAdjustment; - delete _messageForwarder; - delete _settingsManager; - delete _ledDeviceWrapper; + return _totalRunningCount; } void HyperHdrInstance::handleSettingsUpdate(settings::type type, const QJsonDocument& config) @@ -233,41 +251,32 @@ void HyperHdrInstance::handleSettingsUpdate(settings::type type, const QJsonDocu { const QJsonObject obj = config.object(); // change in color recreate ledAdjustments - delete _raw2ledAdjustment; - _raw2ledAdjustment = MultiColorAdjustment::createLedColorsAdjustment(_instIndex, static_cast(_ledString.leds().size()), obj); + _ledColorCalibration = std::unique_ptr(new LedCalibration(_instIndex, static_cast(_ledString.leds().size()), obj)); - if (!_raw2ledAdjustment->verifyAdjustments()) + if (!_ledColorCalibration->verifyAdjustments()) { Warning(_log, "At least one led has no color calibration, please add all leds from your led layout to an 'LED index' field!"); } - emit imageToLedsMappingChanged(_imageProcessor->getLedMappingType()); + emit SignalImageToLedsMappingChanged(_imageProcessor->getLedMappingType()); } else if (type == settings::type::LEDS) { const QJsonArray leds = config.array(); - // stop and cache all running effects, as effects depend heavily on LED-layout - _effectEngine->cacheRunningEffects(); - // ledstring, img processor, muxer, ledGridSize (effect-engine image based effects), _ledBuffer and ByteOrder of ledstring _ledString = LedString::createLedString(leds, LedString::createColorOrder(getSetting(settings::type::DEVICE).object())); _imageProcessor->setLedString(_ledString); - _muxer.updateLedColorsLength(static_cast(_ledString.leds().size())); _ledGridSize = LedString::getLedLayoutGridSize(leds); std::vector color(_ledString.leds().size(), ColorRgb{ 0,0,0 }); - _globalLedBuffer = color; + _currentLedColors = color; // handle hwLedCount update _hwLedCount = qMax(getSetting(settings::type::DEVICE).object()["hardwareLedCount"].toInt(1), getLedCount()); // change in leds are also reflected in adjustment - delete _raw2ledAdjustment; - _raw2ledAdjustment = MultiColorAdjustment::createLedColorsAdjustment(_instIndex, static_cast(_ledString.leds().size()), getSetting(settings::type::COLOR).object()); - - // start cached effects - _effectEngine->startCachedEffects(); + _ledColorCalibration = std::unique_ptr(new LedCalibration(_instIndex, static_cast(_ledString.leds().size()), getSetting(settings::type::COLOR).object())); } else if (type == settings::type::DEVICE) { @@ -277,7 +286,7 @@ void HyperHdrInstance::handleSettingsUpdate(settings::type type, const QJsonDocu _hwLedCount = qMax(dev["hardwareLedCount"].toInt(1), getLedCount()); // force ledString update, if device ByteOrder changed - if (_ledString.colorOrder != stringToColorOrder(dev["colorOrder"].toString("rgb"))) + if (_ledString.colorOrder != LedString::stringToColorOrder(dev["colorOrder"].toString("rgb"))) { Info(_log, "New RGB order is: %s", QSTRING_CSTR(dev["colorOrder"].toString("rgb"))); _ledString = LedString::createLedString(getSetting(settings::type::LEDS).array(), LedString::createColorOrder(dev)); @@ -286,43 +295,99 @@ void HyperHdrInstance::handleSettingsUpdate(settings::type type, const QJsonDocu // do always reinit until the led devices can handle dynamic changes dev["currentLedCount"] = _hwLedCount; // Inject led count info - _ledDeviceWrapper->createLedDevice(dev); + _ledDeviceWrapper->createLedDevice(dev, _smoothing->GetSuggestedInterval()); // TODO: Check, if framegrabber frequency is lower than latchtime..., if yes, stop } + else if (type == settings::type::BGEFFECT || type == settings::type::FGEFFECT) + { + bool isBootEffect = (type == settings::type::FGEFFECT); + int effectPriority = (isBootEffect) ? 0 : 254; - // update once to push single color sets / adjustments/ ledlayout resizes and update ledBuffer color - update(); + if (isBootEffect) + { + if (QTime::currentTime() < _bootEffect) + _bootEffect = QTime::currentTime(); + else + return; + } + else + clear(effectPriority); + + const QJsonObject& effectObject = config.object(); + + if (effectObject["enable"].toBool(true)) + { + const char* effectDesc = (isBootEffect) ? "Boot effect" : "Background effect"; + const QString effectType = effectObject["type"].toString("effect"); + const QString effectAnimation = effectObject["effect"].toString("Rainbow swirl fast"); + const QJsonValue effectColors = effectObject["color"]; + int effectDuration = (isBootEffect) ? effectObject["duration_ms"].toInt(3000) : -1; + + if (isBootEffect && effectDuration <= 0) + { + effectDuration = 3000; + Warning(_log, "Boot effect duration 'infinity' is forbidden, set to default value %d ms", effectDuration); + } + + if (effectType.contains("color")) + { + const QJsonArray& COLORS = effectColors.toArray(); + + if (COLORS.size() < 3) + return; + + std::vector colors = { + ColorRgb { (uint8_t)(COLORS[0].toInt(0)), (uint8_t)(COLORS[1].toInt(0)), (uint8_t)(COLORS[2].toInt(0))} + }; + + setColor(effectPriority, colors, effectDuration); + Info(_log, "%s: color set to [%d, %d, %d]", effectDesc, colors[0].red, colors[0].green, colors[0].blue); + } + else + { + int result = setEffect(effectAnimation, effectPriority, effectDuration); + Info(_log, "%s: animation set to '%s' [%s]", effectDesc, QSTRING_CSTR(effectAnimation), ((result == 0) ? "started" : "failed")); + } + } + } + + if (type == settings::type::DEVICE || type == settings::type::LEDS) + update(); } QJsonDocument HyperHdrInstance::getSetting(settings::type type) const { - return _settingsManager->getSetting(type); + return _instanceConfig->getSetting(type); +} + +void HyperHdrInstance::setSetting(settings::type type, QString config) +{ + return _instanceConfig->saveSetting(type, config); } bool HyperHdrInstance::saveSettings(const QJsonObject& config, bool correct) { - return _settingsManager->saveSettings(config, correct); + return _instanceConfig->saveSettings(config, correct); } void HyperHdrInstance::saveCalibration(QString saveData) { - _settingsManager->saveSetting(settings::type::VIDEODETECTION, saveData); + _instanceConfig->saveSetting(settings::type::VIDEODETECTION, saveData); } void HyperHdrInstance::setSmoothing(int time) { - _smoothing->updateCurrentConfig(time); + _smoothing->UpdateCurrentConfig(time); } QJsonObject HyperHdrInstance::getAverageColor() { QJsonObject ret; - auto copy = _globalLedBuffer; long red = 0, green = 0, blue = 0, count = 0; - for (const ColorRgb& c : copy) + for (const ColorRgb& c : _currentLedColors) { red += c.red; green += c.green; @@ -348,7 +413,7 @@ QJsonObject HyperHdrInstance::getAverageColor() unsigned HyperHdrInstance::updateSmoothingConfig(unsigned id, int settlingTime_ms, double ledUpdateFrequency_hz, bool directMode) { - return _smoothing->updateConfig(id, settlingTime_ms, ledUpdateFrequency_hz, directMode); + return _smoothing->UpdateConfig(id, settlingTime_ms, ledUpdateFrequency_hz, directMode); } int HyperHdrInstance::getLedCount() const @@ -358,69 +423,54 @@ int HyperHdrInstance::getLedCount() const void HyperHdrInstance::setSourceAutoSelect(bool state) { - _muxer.setSourceAutoSelectEnabled(state); + _muxer->setSourceAutoSelectEnabled(state); } bool HyperHdrInstance::setVisiblePriority(int priority) { - return _muxer.setPriority(priority); + return _muxer->setPriority(priority); } bool HyperHdrInstance::sourceAutoSelectEnabled() const { - return _muxer.isSourceAutoSelectEnabled(); + return _muxer->isSourceAutoSelectEnabled(); } void HyperHdrInstance::setNewComponentState(hyperhdr::Components component, bool state) { - _componentRegister.setNewComponentState(component, state); + if (_componentController == nullptr || _muxer == nullptr) + return; - if (!_bootEffect.isNull() && hyperhdr::Components::COMP_LEDDEVICE == component && state) - { - // initial startup effect - if (QTime::currentTime() < _bootEffect) - EffectEngine::handleInitialEffect(this, getSetting(settings::type::FGEFFECT).object()); + _componentController->setNewComponentState(component, state); - _bootEffect = QTime(); + if (hyperhdr::Components::COMP_LEDDEVICE == component && state) + { + QUEUE_CALL_2(this, handleSettingsUpdate, + settings::type, settings::type::FGEFFECT, + QJsonDocument, getSetting(settings::type::FGEFFECT)); } if (hyperhdr::Components::COMP_LEDDEVICE == component) { - if (state) - emit PerformanceCounters::getInstance()->newCounter(PerformanceReport(static_cast(PerformanceReportType::LED), -1, "", -1, -1, -1, -1, getInstanceIndex())); - else - emit PerformanceCounters::getInstance()->removeCounter(static_cast(PerformanceReportType::LED), getInstanceIndex()); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(state, hyperhdr::PerformanceReportType::LED, getInstanceIndex()); - if (GrabberWrapper::getInstance() != nullptr) - { - emit GrabberWrapper::getInstance()->instancePauseChanged(_instIndex, state); - } + emit SignalInstancePauseChanged(_instIndex, state); } } -std::map HyperHdrInstance::getAllComponents() const -{ - return _componentRegister.getRegister(); -} - int HyperHdrInstance::isComponentEnabled(hyperhdr::Components comp) const { - return _componentRegister.isComponentEnabled(comp); + return _componentController->isComponentEnabled(comp); } void HyperHdrInstance::registerInput(int priority, hyperhdr::Components component, const QString& origin, const QString& owner, unsigned smooth_cfg) { - _muxer.registerInput(priority, component, origin, owner, smooth_cfg); + _muxer->registerInput(priority, component, origin, ColorRgb::BLACK, smooth_cfg, owner); } -void HyperHdrInstance::updateLedsValues(int priority, const std::vector& ledColors) +bool HyperHdrInstance::setInputLeds(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect) { - _muxer.updateLedsValues(priority, ledColors); -} - -bool HyperHdrInstance::setInput(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect) -{ - if (_muxer.setInput(priority, ledColors, timeout_ms)) + if (_muxer->setInput(priority, timeout_ms)) { // clear effect if this call does not come from an effect if (clearEffect) @@ -429,8 +479,14 @@ bool HyperHdrInstance::setInput(int priority, const std::vector& ledCo } // if this priority is visible, update immediately - if (priority == _muxer.getCurrentPriority()) + if (priority == _muxer->getCurrentPriority()) { + if (_currentLedColors.size() == ledColors.size()) + _currentLedColors = ledColors; + else + Warning(_log, "Discrepancy between the number of colors in the input (%i) and the current ones (%i)", + ledColors.size(), _currentLedColors.size()); + update(); } @@ -439,27 +495,26 @@ bool HyperHdrInstance::setInput(int priority, const std::vector& ledCo return false; } -void HyperHdrInstance::saveGrabberParams(int hardware_brightness, int hardware_contrast, int hardware_saturation) +void HyperHdrInstance::signalSetGlobalImageHandler(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin, QString clientDescription) { - QJsonDocument newSet = _settingsManager->getSetting(settings::type::VIDEOGRABBER); - QJsonObject grabber = QJsonObject(newSet.object()); - grabber["hardware_brightness"] = hardware_brightness; - grabber["hardware_contrast"] = hardware_contrast; - grabber["hardware_saturation"] = hardware_saturation; - _settingsManager->saveSetting(settings::type::VIDEOGRABBER, QJsonDocument(grabber).toJson(QJsonDocument::Compact)); + if (!_muxer->hasPriority(priority)) + { + _muxer->registerInput(priority, origin, clientDescription); + } + + setInputImage(priority, image, timeout_ms); } -bool HyperHdrInstance::setInputImage(int priority, const Image& image, int64_t timeout_ms, bool clearEffect) +void HyperHdrInstance::setInputImage(int priority, const Image& image, int64_t timeout_ms, bool clearEffect) { - if (!_muxer.hasPriority(priority)) - { - emit GlobalSignals::getInstance()->globalRegRequired(priority); - return false; + if (!_muxer->hasPriority(priority)) + { + return; } - bool isImage = image.width() > 1 || image.height() > 1; + bool isImage = image.width() > 1 && image.height() > 1; - if (_muxer.setInputImage(priority, (receivers(SIGNAL(onCurrentImage())) > 0 || !isImage || timeout_ms == 0) ? image : Image(), timeout_ms)) + if (_muxer->setInput(priority, timeout_ms)) { // clear effect if this call does not come from an effect if (clearEffect) @@ -468,22 +523,34 @@ bool HyperHdrInstance::setInputImage(int priority, const Image& image, } // if this priority is visible, update immediately - if (priority == _muxer.getCurrentPriority()) + if (priority == _muxer->getCurrentPriority()) { if (isImage) - emit _imageProcessingUnit->queueImageSignal(priority, image); + { + std::vector colors; + + _imageProcessor->processFrame(colors, image); + + if (colors.size() > 0) + { + updateResult(colors); + emit SignalInstanceImageUpdated(image); + } + } else update(); } - - return true; } - return false; } bool HyperHdrInstance::setInputInactive(quint8 priority) { - return _muxer.setInputInactive(priority); + return _muxer->setInputInactive(priority); +} + +void HyperHdrInstance::signalSetGlobalColorHandler(int priority, const std::vector& ledColor, int timeout_ms, hyperhdr::Components origin, QString clientDescription) +{ + setColor(priority, ledColor, timeout_ms, clientDescription, true); } void HyperHdrInstance::setColor(int priority, const std::vector& ledColors, int timeout_ms, const QString& origin, bool clearEffects) @@ -497,43 +564,21 @@ void HyperHdrInstance::setColor(int priority, const std::vector& ledCo _effectEngine->channelCleared(priority); } - // create full led vector from single/multiple colors - std::vector newLedColors; - auto currentCol = ledColors.begin(); - - while (newLedColors.size() < _ledString.leds().size()) - { - newLedColors.emplace_back(*currentCol); - - if (++currentCol == ledColors.end()) - currentCol = ledColors.begin(); - } - - if (getPriorityInfo(priority).componentId != hyperhdr::COMP_COLOR) + if (getComponentForPriority(priority) != hyperhdr::COMP_COLOR) { clear(priority); } // register color - registerInput(priority, hyperhdr::COMP_COLOR, origin); - - // write color to muxer - setInput(priority, newLedColors, timeout_ms); -} - -QStringList HyperHdrInstance::getAdjustmentIds() const -{ - return _raw2ledAdjustment->getAdjustmentIds(); -} - -ColorAdjustment* HyperHdrInstance::getAdjustment(const QString& id) const -{ - return _raw2ledAdjustment->getAdjustment(id); + _muxer->registerInput(priority, hyperhdr::COMP_COLOR, origin, ledColors[0]); + _muxer->setInput(priority, timeout_ms); + update(); } -void HyperHdrInstance::adjustmentsUpdated() +void HyperHdrInstance::updateAdjustments(const QJsonObject& config) { - emit adjustmentChanged(); + _ledColorCalibration->updateConfig(config); + emit SignalAdjustmentUpdated(_ledColorCalibration->getAdjustmentState()); update(); } @@ -542,7 +587,7 @@ bool HyperHdrInstance::clear(int priority, bool forceClearAll) bool isCleared = false; if (priority < 0) { - _muxer.clearAll(forceClearAll); + _muxer->clearAll(forceClearAll); // send clearall signal to the effect engine _effectEngine->allChannelsCleared(); @@ -554,7 +599,7 @@ bool HyperHdrInstance::clear(int priority, bool forceClearAll) // (outside the check so the effect gets cleared even when the effect is not sending colors) _effectEngine->channelCleared(priority); - if (_muxer.clearInput(priority)) + if (_muxer->clearInput(priority)) { isCleared = true; } @@ -564,28 +609,17 @@ bool HyperHdrInstance::clear(int priority, bool forceClearAll) int HyperHdrInstance::getCurrentPriority() const { - return _muxer.getCurrentPriority(); + return _muxer->getCurrentPriority(); } -bool HyperHdrInstance::isCurrentPriority(int priority) const +hyperhdr::Components HyperHdrInstance::getComponentForPriority(int priority) { - return getCurrentPriority() == priority; + return _muxer->getInputInfo(priority).componentId; } -QList HyperHdrInstance::getActivePriorities() const +hyperhdr::Components HyperHdrInstance::getCurrentPriorityActiveComponent() { - return _muxer.getPriorities(); -} - -const HyperHdrInstance::InputInfo& HyperHdrInstance::getPriorityInfo(int priority) const -{ - return _muxer.getInputInfo(priority); -} - -PriorityMuxer::InputInfo HyperHdrInstance::getCurrentPriorityInfo() -{ - PriorityMuxer::InputInfo val = _muxer.getInputInfo(getCurrentPriority()); - return val; + return _muxer->getInputInfo(getCurrentPriority()).componentId; } std::list HyperHdrInstance::getEffects() const @@ -593,28 +627,26 @@ std::list HyperHdrInstance::getEffects() const return _effectEngine->getEffects(); } -std::list HyperHdrInstance::getActiveEffects() const +void HyperHdrInstance::putJsonConfig(QJsonObject& info) const { - return _effectEngine->getActiveEffects(); -} - -QJsonObject HyperHdrInstance::getQJsonConfig() const -{ - QJsonObject obj; - if (_settingsManager != NULL) - return _settingsManager->getSettings(); - else - return obj; + info = _instanceConfig->getSettings(); } int HyperHdrInstance::setEffect(const QString& effectName, int priority, int timeout, const QString& origin) { - return _effectEngine->runEffect(effectName, priority, timeout, origin); + if (_effectEngine != nullptr) + return _effectEngine->runEffect(effectName, priority, timeout, origin); + else + return 0; } int HyperHdrInstance::setEffect(const QString& effectName, const QJsonObject& args, int priority, int timeout, const QString& origin, const QString& imageData) { - return _effectEngine->runEffect(effectName, args, priority, timeout, origin, 0, imageData); + if (_effectEngine != nullptr) + return _effectEngine->runEffect(effectName, priority, timeout, origin); + else + return 0; + } void HyperHdrInstance::setLedMappingType(int mappingType) @@ -622,67 +654,54 @@ void HyperHdrInstance::setLedMappingType(int mappingType) if (mappingType != _imageProcessor->getLedMappingType()) { _imageProcessor->setLedMappingType(mappingType); - emit imageToLedsMappingChanged(mappingType); + emit SignalImageToLedsMappingChanged(mappingType); } } -int HyperHdrInstance::getLedMappingType() const -{ - return _imageProcessor->getLedMappingType(); -} - -QString HyperHdrInstance::getActiveDeviceType() const -{ - return _ledDeviceWrapper->getActiveDeviceType(); -} - void HyperHdrInstance::handleVisibleComponentChanged(hyperhdr::Components comp) { _imageProcessor->setBlackbarDetectDisable((comp == hyperhdr::COMP_EFFECT)); - _raw2ledAdjustment->setBacklightEnabled((comp != hyperhdr::COMP_COLOR && comp != hyperhdr::COMP_EFFECT)); + _ledColorCalibration->setBacklightEnabled((comp != hyperhdr::COMP_COLOR && comp != hyperhdr::COMP_EFFECT)); } void HyperHdrInstance::handlePriorityChangedLedDevice(const quint8& priority) { - int previousPriority = _muxer.getPreviousPriority(); + if (_signalTerminate) + return; + + int previousPriority = _muxer->getPreviousPriority(); Info(_log, "New priority[%d], previous [%d]", priority, previousPriority); - if (priority == PriorityMuxer::LOWEST_PRIORITY) + if (priority == Muxer::LOWEST_PRIORITY) { Warning(_log, "No source left -> switch LED-Device off"); - emit compStateChangeRequest(hyperhdr::COMP_LEDDEVICE, false); - emit PerformanceCounters::getInstance()->removeCounter(static_cast(PerformanceReportType::INSTANCE), getInstanceIndex()); + emit SignalRequestComponent(hyperhdr::COMP_LEDDEVICE, false); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(false, hyperhdr::PerformanceReportType::INSTANCE, getInstanceIndex()); } else { - if (previousPriority == PriorityMuxer::LOWEST_PRIORITY) + if (previousPriority == Muxer::LOWEST_PRIORITY) { Info(_log, "New source available -> switch LED-Device on"); - emit compStateChangeRequest(hyperhdr::COMP_LEDDEVICE, true); - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::INSTANCE), -1, _name, -1, -1, -1, -1, getInstanceIndex())); + emit SignalRequestComponent(hyperhdr::COMP_LEDDEVICE, true); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(true, hyperhdr::PerformanceReportType::INSTANCE, getInstanceIndex(), _name); } } } -ImageProcessor* HyperHdrInstance::getImageProcessor() -{ - return _imageProcessor; -} - void HyperHdrInstance::update() { - const PriorityMuxer::InputInfo& priorityInfo = _muxer.getInputInfo(_muxer.getCurrentPriority()); - emit _imageProcessingUnit->clearQueueImageSignal(); - emit _imageProcessingUnit->dataReadySignal(priorityInfo.ledColors); -} + if (_signalTerminate) + return; -void HyperHdrInstance::requestForColors() -{ - const PriorityMuxer::InputInfo& priorityInfo = _muxer.getInputInfo(_muxer.getCurrentPriority()); - emit _imageProcessingUnit->dataReadySignal(priorityInfo.ledColors); + const Muxer::InputInfo& priorityInfo = _muxer->getInputInfo(_muxer->getCurrentPriority()); + if (priorityInfo.componentId == hyperhdr::Components::COMP_COLOR) + { + std::fill(_currentLedColors.begin(), _currentLedColors.end(), priorityInfo.staticColor); + } + updateResult(_currentLedColors); } void HyperHdrInstance::updateResult(std::vector _ledBuffer) @@ -702,8 +721,8 @@ void HyperHdrInstance::updateResult(std::vector _ledBuffer) { if (diff >= 59000 && diff <= 65000) - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::INSTANCE), _computeStats.token, _name, _computeStats.total / qMax(diff/1000.0, 1.0), _computeStats.total, 0, 0, getInstanceIndex())); + emit GlobalSignals::getInstance()->SignalPerformanceNewReport( + PerformanceReport(hyperhdr::PerformanceReportType::INSTANCE, _computeStats.token, _name, _computeStats.total / qMax(diff/1000.0, 1.0), _computeStats.total, 0, 0, getInstanceIndex())); _computeStats.statBegin = now; _computeStats.total = 1; @@ -711,53 +730,54 @@ void HyperHdrInstance::updateResult(std::vector _ledBuffer) else _computeStats.total++; - _globalLedBuffer = _ledBuffer; + _currentLedColors = _ledBuffer; for (int disabledProcessing = 0; disabledProcessing < 2; disabledProcessing++) { if (disabledProcessing == 1) - _raw2ledAdjustment->applyAdjustment(_ledBuffer); + { + _ledColorCalibration->applyAdjustment(_ledBuffer); + } if (_ledString.hasDisabled) { - auto ledIter = _ledString.leds().begin(); - for (ColorRgb& color : _ledBuffer) - if (ledIter != _ledString.leds().end()) - { - if ((*ledIter).disabled) - color = ColorRgb::BLACK; - ++ledIter; - } + auto ledsCurrent = _ledString.leds().begin(); + auto ledsEnd = _ledString.leds().end(); + auto colorsCurrent = _ledBuffer.begin(); + auto colorsEnd = _ledBuffer.end(); + for (; colorsCurrent != colorsEnd && ledsCurrent != ledsEnd; ++colorsCurrent, ++ledsCurrent) + if ((*ledsCurrent).disabled) + (*colorsCurrent) = ColorRgb::BLACK; } if (disabledProcessing == 0) - emit rawLedColors(_ledBuffer); + emit SignalRawColorsChanged(_ledBuffer); } - if (_ledString.colorOrder != ColorOrder::ORDER_RGB) + if (_ledString.colorOrder != LedString::ColorOrder::ORDER_RGB) { for (ColorRgb& color : _ledBuffer) { // correct the color byte order switch (_ledString.colorOrder) { - case ColorOrder::ORDER_RGB: + case LedString::ColorOrder::ORDER_RGB: break; - case ColorOrder::ORDER_BGR: + case LedString::ColorOrder::ORDER_BGR: std::swap(color.red, color.blue); break; - case ColorOrder::ORDER_RBG: + case LedString::ColorOrder::ORDER_RBG: std::swap(color.green, color.blue); break; - case ColorOrder::ORDER_GRB: + case LedString::ColorOrder::ORDER_GRB: std::swap(color.red, color.green); break; - case ColorOrder::ORDER_GBR: + case LedString::ColorOrder::ORDER_GBR: std::swap(color.red, color.green); std::swap(color.green, color.blue); break; - case ColorOrder::ORDER_BRG: + case LedString::ColorOrder::ORDER_BRG: std::swap(color.red, color.blue); std::swap(color.green, color.blue); break; @@ -775,21 +795,21 @@ void HyperHdrInstance::updateResult(std::vector _ledBuffer) if (_ledDeviceWrapper->enabled()) { // Smoothing is disabled - if (!_smoothing->enabled()) + if (!_smoothing->isEnabled()) { - emit ledDeviceData(_ledBuffer); + emit SignalUpdateLeds(_ledBuffer); } else { - int priority = _muxer.getCurrentPriority(); - const PriorityMuxer::InputInfo& priorityInfo = _muxer.getInputInfo(priority); + int priority = _muxer->getCurrentPriority(); + const Muxer::InputInfo& priorityInfo = _muxer->getInputInfo(priority); - _smoothing->selectConfig(priorityInfo.smooth_cfg, false); + _smoothing->SelectConfig(priorityInfo.smooth_cfg, false); // feed smoothing in pause mode to maintain a smooth transition back to smooth mode - if (_smoothing->enabled() || _smoothing->pause()) + if (_smoothing->isEnabled() || _smoothing->isPaused()) { - _smoothing->updateLedValues(_ledBuffer); + _smoothing->UpdateLedValues(_ledBuffer); } } } @@ -801,28 +821,23 @@ void HyperHdrInstance::identifyLed(const QJsonObject& params) _ledDeviceWrapper->identifyLed(params); } - -QJsonObject HyperHdrInstance::getJsonInfo(bool full) -{ - QJsonObject info; - +void HyperHdrInstance::putJsonInfo(QJsonObject& info, bool full) +{ uint64_t now = InternalClock::now(); int currentPriority = getCurrentPriority(); - auto priorityMuxer = getMuxerInstance(); - auto allPriorities = priorityMuxer->getInputInfoTable(); //////////////////////////////////// // collect priorities information // //////////////////////////////////// QJsonArray priorities; - for (const PriorityMuxer::InputInfo& priorityInfo : allPriorities) - if (priorityInfo.priority != PriorityMuxer::LOWEST_PRIORITY) + for (const Muxer::InputInfo& priorityInfo : _muxer->getInputInfoTable()) + if (priorityInfo.priority != Muxer::LOWEST_PRIORITY) { QJsonObject item; item["priority"] = priorityInfo.priority; - if (priorityInfo.timeoutTime_ms > 0) - item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now); + if (priorityInfo.timeout > 0) + item["duration_ms"] = int(priorityInfo.timeout - now); // owner has optional informations to the component if (!priorityInfo.owner.isEmpty()) @@ -830,18 +845,18 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) item["componentId"] = QString(hyperhdr::componentToIdString(priorityInfo.componentId)); item["origin"] = priorityInfo.origin; - item["active"] = (priorityInfo.timeoutTime_ms >= -1); + item["active"] = (priorityInfo.timeout >= -1); item["visible"] = (priorityInfo.priority == currentPriority); - if (priorityInfo.componentId == hyperhdr::COMP_COLOR && !priorityInfo.ledColors.empty()) + if (priorityInfo.componentId == hyperhdr::COMP_COLOR) { QJsonObject LEDcolor; // add RGB Value to Array QJsonArray RGBValue; - RGBValue.append(priorityInfo.ledColors.begin()->red); - RGBValue.append(priorityInfo.ledColors.begin()->green); - RGBValue.append(priorityInfo.ledColors.begin()->blue); + RGBValue.append(priorityInfo.staticColor.red); + RGBValue.append(priorityInfo.staticColor.green); + RGBValue.append(priorityInfo.staticColor.blue); LEDcolor.insert("RGB", RGBValue); uint16_t Hue; @@ -849,9 +864,9 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) // add HSL Value to Array QJsonArray HSLValue; - ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red, - priorityInfo.ledColors.begin()->green, - priorityInfo.ledColors.begin()->blue, + ColorSys::rgb2hsl(priorityInfo.staticColor.red, + priorityInfo.staticColor.green, + priorityInfo.staticColor.blue, Hue, Saturation, Luminace); HSLValue.append(Hue); @@ -872,59 +887,14 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) info["priorities_autoselect"] = sourceAutoSelectEnabled(); if (!full) - return info; + return; //////////////////////////////////// // collect adjustment information // //////////////////////////////////// - QJsonArray adjustmentArray; - for (const QString& adjustmentId : getAdjustmentIds()) - { - const ColorAdjustment* colorAdjustment = getAdjustment(adjustmentId); - if (colorAdjustment == nullptr) - { - Error(_log, "Incorrect color adjustment id: %s", QSTRING_CSTR(adjustmentId)); - continue; - } - - QJsonObject adjustment; - adjustment["id"] = adjustmentId; - - QList> calibColors; - calibColors.append(qMakePair(QString("white"), &(colorAdjustment->_rgbWhiteAdjustment))); - calibColors.append(qMakePair(QString("red"), &(colorAdjustment->_rgbRedAdjustment))); - calibColors.append(qMakePair(QString("green"), &(colorAdjustment->_rgbGreenAdjustment))); - calibColors.append(qMakePair(QString("blue"), &(colorAdjustment->_rgbBlueAdjustment))); - calibColors.append(qMakePair(QString("cyan"), &(colorAdjustment->_rgbCyanAdjustment))); - calibColors.append(qMakePair(QString("magenta"),&(colorAdjustment->_rgbMagentaAdjustment))); - calibColors.append(qMakePair(QString("yellow"), &(colorAdjustment->_rgbYellowAdjustment))); - - for (const QPair& myElemCalib : calibColors) - { - QJsonArray adj; - adj.append(myElemCalib.second->getAdjustmentR()); - adj.append(myElemCalib.second->getAdjustmentG()); - adj.append(myElemCalib.second->getAdjustmentB()); - adjustment.insert(myElemCalib.first, adj); - } - - adjustment["backlightThreshold"] = colorAdjustment->_rgbTransform.getBacklightThreshold(); - adjustment["backlightColored"] = colorAdjustment->_rgbTransform.getBacklightColored(); - adjustment["brightness"] = colorAdjustment->_rgbTransform.getBrightness(); - adjustment["brightnessCompensation"] = colorAdjustment->_rgbTransform.getBrightnessCompensation(); - adjustment["gammaRed"] = colorAdjustment->_rgbTransform.getGammaR(); - adjustment["gammaGreen"] = colorAdjustment->_rgbTransform.getGammaG(); - adjustment["gammaBlue"] = colorAdjustment->_rgbTransform.getGammaB(); - adjustment["temperatureRed"] = colorAdjustment->_rgbRedAdjustment.getCorrection(); - adjustment["temperatureGreen"] = colorAdjustment->_rgbGreenAdjustment.getCorrection(); - adjustment["temperatureBlue"] = colorAdjustment->_rgbBlueAdjustment.getCorrection(); - adjustment["saturationGain"] = colorAdjustment->_rgbTransform.getSaturationGain(); - adjustment["luminanceGain"] = colorAdjustment->_rgbTransform.getLuminanceGain(); - adjustment["classic_config"] = colorAdjustment->_rgbTransform.getClassicConfig(); - - adjustmentArray.append(adjustment); - } + QJsonArray adjustmentArray = _ledColorCalibration->getAdjustmentState(); + info["adjustment"] = adjustmentArray; //////////////////////////////////// @@ -938,7 +908,6 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) { QJsonObject effect; effect["name"] = effectDefinition.name; - effect["args"] = effectDefinition.args; effects.append(effect); } info["effects"] = effects; @@ -948,17 +917,16 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) //////////////////////////////////// QJsonArray activeEffects; - std::list activeEffectDefinitionList = getActiveEffects(); + std::list activeEffectDefinitionList = _effectEngine->getActiveEffects(); for (const ActiveEffectDefinition& activeEffectDefinition : activeEffectDefinitionList) { - if (activeEffectDefinition.priority != PriorityMuxer::LOWEST_PRIORITY - 1) + if (activeEffectDefinition.priority != Muxer::LOWEST_PRIORITY - 1) { QJsonObject activeEffect; activeEffect["name"] = activeEffectDefinition.name; activeEffect["priority"] = activeEffectDefinition.priority; activeEffect["timeout"] = activeEffectDefinition.timeout; - activeEffect["args"] = activeEffectDefinition.args; activeEffects.append(activeEffect); } } @@ -969,23 +937,23 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) //////////////////////////////////// QJsonArray activeLedColors; - const HyperHdrInstance::InputInfo& priorityInfo = getPriorityInfo(currentPriority); - if (priorityInfo.componentId == hyperhdr::COMP_COLOR && !priorityInfo.ledColors.empty()) + const Muxer::InputInfo& priorityInfo = _muxer->getInputInfo(_muxer->getCurrentPriority()); + if (priorityInfo.componentId == hyperhdr::COMP_COLOR) { QJsonObject LEDcolor; // check if LED Color not Black (0,0,0) - if ((priorityInfo.ledColors.begin()->red + - priorityInfo.ledColors.begin()->green + - priorityInfo.ledColors.begin()->blue != + if ((priorityInfo.staticColor.red + + priorityInfo.staticColor.green + + priorityInfo.staticColor.blue != 0)) { QJsonObject LEDcolor; // add RGB Value to Array QJsonArray RGBValue; - RGBValue.append(priorityInfo.ledColors.begin()->red); - RGBValue.append(priorityInfo.ledColors.begin()->green); - RGBValue.append(priorityInfo.ledColors.begin()->blue); + RGBValue.append(priorityInfo.staticColor.red); + RGBValue.append(priorityInfo.staticColor.green); + RGBValue.append(priorityInfo.staticColor.blue); LEDcolor.insert("RGB Value", RGBValue); uint16_t Hue; @@ -993,9 +961,9 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) // add HSL Value to Array QJsonArray HSLValue; - ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red, - priorityInfo.ledColors.begin()->green, - priorityInfo.ledColors.begin()->blue, + ColorSys::rgb2hsl(priorityInfo.staticColor.red, + priorityInfo.staticColor.green, + priorityInfo.staticColor.blue, Hue, Saturation, Luminace); HSLValue.append(Hue); @@ -1013,8 +981,7 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) /////////////////////////////////////// QJsonArray component; - std::map components = getComponentRegister().getRegister(); - for (auto comp : components) + for (const auto& comp : _componentController->getComponentsState()) { QJsonObject item; item["name"] = QString::fromStdString(hyperhdr::componentToIdString(comp.first)); @@ -1028,11 +995,29 @@ QJsonObject HyperHdrInstance::getJsonInfo(bool full) // MISC // //////////////// - // add leds configs - info["leds"] = getSetting(settings::type::LEDS).array(); + info["videomodehdr"] = (_componentController->isComponentEnabled(hyperhdr::Components::COMP_HDR)) ? 1 : 0; + info["leds"] = getSetting(settings::type::LEDS).array(); + info["imageToLedMappingType"] = ImageToLedManager::mappingTypeToStr(_imageProcessor->getLedMappingType()); +} - // mapping type - info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(getLedMappingType()); +void HyperHdrInstance::setSignalStateByCEC(bool enable) +{ + if (_systemControl != nullptr && _systemControl->isCEC()) + { + _systemControl->setSysCaptureEnable(enable); + } + if (_videoControl != nullptr && _videoControl->isCEC()) + { + _videoControl->setUsbCaptureEnable(enable); + } +} - return info; +int HyperHdrInstance::hasLedClock() +{ + return _ledDeviceWrapper->hasLedClock(); +} + +bool HyperHdrInstance::getScanParameters(size_t led, double& hscanBegin, double& hscanEnd, double& vscanBegin, double& vscanEnd) const +{ + return _imageProcessor->getScanParameters(led, hscanBegin, hscanEnd, vscanBegin, vscanEnd); } diff --git a/sources/base/HyperHdrManager.cpp b/sources/base/HyperHdrManager.cpp new file mode 100644 index 000000000..540f4b464 --- /dev/null +++ b/sources/base/HyperHdrManager.cpp @@ -0,0 +1,362 @@ +/* HyperHdrManager.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include +#endif + +#include +#include +#include +#include +#include +#include + +QString HyperHdrManager::getRootPath() +{ + return _rootPath; +} + +HyperHdrManager::HyperHdrManager(const QString& rootPath, bool readonlyMode) + : _log(Logger::getInstance("HYPER_MANAGER")) + , _instanceTable(new InstanceTable(readonlyMode)) + , _rootPath(rootPath) + , _readonlyMode(readonlyMode) + , _fireStarter(0) +{ + qRegisterMetaType("InstanceState"); + connect(this, &HyperHdrManager::SignalInstanceStateChanged, this, &HyperHdrManager::handleInstanceStateChange); +} + +HyperHdrManager::~HyperHdrManager() +{ + Debug(_log, "HyperHdrManager has been removed"); +} + +void HyperHdrManager::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name) +{ + switch (state) + { + case InstanceState::START: + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(true, PerformanceReportType::INSTANCE, instance, name); + break; + case InstanceState::STOP: + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(false, PerformanceReportType::INSTANCE, instance); + emit GlobalSignals::getInstance()->SignalPerformanceStateChanged(false, PerformanceReportType::LED, instance); + break; + default: + break; + } +} + + +std::shared_ptr HyperHdrManager::getHyperHdrInstance(quint8 instance) +{ + if (_runningInstances.contains(instance)) + return _runningInstances.value(instance); + + Warning(_log, "The requested instance index '%d' with name '%s' isn't running, return main instance", instance, QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); + return _runningInstances.value(0); +} + +QVector HyperHdrManager::getInstanceData() const +{ + QVector instances = _instanceTable->getAllInstances(); + for (auto& entry : instances) + { + // add running state + entry["running"] = _runningInstances.contains(entry["instance"].toInt()); + } + return instances; +} + +bool HyperHdrManager::areInstancesReady() +{ + if (_fireStarter <= 0) + return false; + + return (--_fireStarter == 0); +} + +void HyperHdrManager::startAll() +{ + auto instanceList = _instanceTable->getAllInstances(true); + + _fireStarter = instanceList.count(); + + for (const auto& entry : instanceList) + { + startInstance(entry["instance"].toInt()); + } +} + +void HyperHdrManager::stopAllonExit() +{ + disconnect(this, nullptr, nullptr, nullptr); + + Warning(_log, "Running instances: %i, starting instances: %i" + , (int)_runningInstances.size() + , (int)_startingInstances.size()); + + _runningInstances.clear(); + _startingInstances.clear(); + + while (HyperHdrInstance::getTotalRunningCount()) + { + Warning(_log, "Waiting for instances: %i", (int)HyperHdrInstance::getTotalRunningCount()); + QThread::msleep(25); + } + Info(_log, "All instances are closed now"); +} + +void HyperHdrManager::setSmoothing(int time) +{ + for (const auto& instance : _runningInstances) + QUEUE_CALL_1(instance.get(), setSmoothing, int, time); +} + +QJsonObject HyperHdrManager::getAverageColor(quint8 index) +{ + HyperHdrInstance* instance = HyperHdrManager::getHyperHdrInstance(index).get(); + QJsonObject res; + + SAFE_CALL_0_RET(instance, getAverageColor, QJsonObject, res); + + return res; +} + +void HyperHdrManager::setSignalStateByCEC(bool enable) +{ + for (const auto& instance : _runningInstances) + { + QUEUE_CALL_1(instance.get(), setSignalStateByCEC, bool, enable); + } +} + +void HyperHdrManager::toggleStateAllInstances(bool pause) +{ + for (const auto& instance : _runningInstances) + { + emit instance->SignalRequestComponent(hyperhdr::COMP_ALL, pause); + } +} + +void HyperHdrManager::hibernate(bool wakeUp) +{ + if (!wakeUp) + { + Warning(_log, "The system is going to sleep"); + toggleStateAllInstances(false); + } + else + { + Warning(_log, "The system is going to wake up"); + QTimer::singleShot(3000, [this]() { toggleStateAllInstances(true); }); + } +} + +bool HyperHdrManager::startInstance(quint8 inst, QObject* caller, int tan) +{ + if (_instanceTable->instanceExist(inst)) + { + if (!_runningInstances.contains(inst) && !_startingInstances.contains(inst)) + { + QThread* hyperhdrThread = new QThread(); + hyperhdrThread->setObjectName("HyperHdrThread"); + + auto hyperhdr = std::shared_ptr( + new HyperHdrInstance(inst, + _readonlyMode, + _instanceTable->getNamebyIndex(inst)), + [](HyperHdrInstance* oldInstance) { + THREAD_REMOVER(QString("HyperHDR instance at index = %1").arg(oldInstance->getInstanceIndex()), + oldInstance->thread(), oldInstance); + } + ); + + hyperhdr->moveToThread(hyperhdrThread); + + connect(hyperhdrThread, &QThread::started, hyperhdr.get(), &HyperHdrInstance::start); + connect(hyperhdr.get(), &HyperHdrInstance::SignalInstanceJustStarted, this, &HyperHdrManager::handleInstanceJustStarted); + connect(hyperhdr.get(), &HyperHdrInstance::SignalInstancePauseChanged, this, &HyperHdrManager::SignalInstancePauseChanged); + connect(hyperhdr.get(), &HyperHdrInstance::SignalInstanceSettingsChanged, this, &HyperHdrManager::SignalSettingsChanged); + connect(hyperhdr.get(), &HyperHdrInstance::SignalRequestComponent, this, &HyperHdrManager::SignalCompStateChangeRequest); + connect(this, &HyperHdrManager::SignalSetNewComponentStateToAllInstances, hyperhdr.get(), &HyperHdrInstance::setNewComponentState); + + _startingInstances.insert(inst, hyperhdr); + + hyperhdrThread->start(); + + _instanceTable->setLastUse(inst); + _instanceTable->setEnable(inst, true); + + if (!_pendingRequests.contains(inst) && caller != nullptr) + { + PendingRequests newDef{ caller, tan }; + _pendingRequests[inst] = newDef; + } + + return true; + } + Debug(_log, "Can't start Hyperhdr instance index '%d' with name '%s' it's already running or queued for start", inst, QSTRING_CSTR(_instanceTable->getNamebyIndex(inst))); + return false; + } + Debug(_log, "Can't start Hyperhdr instance index '%d' it doesn't exist in DB", inst); + return false; +} + +bool HyperHdrManager::stopInstance(quint8 instance) +{ + // inst 0 can't be stopped + if (!isInstAllowed(instance)) + return false; + + if (_instanceTable->instanceExist(instance)) + { + if (_runningInstances.contains(instance)) + { + disconnect(this, nullptr, _runningInstances.value(instance).get(), nullptr); + + _instanceTable->setEnable(instance, false); + + _runningInstances.remove(instance); + + emit SignalInstanceStateChanged(InstanceState::STOP, instance); + emit SignalInstancesListChanged(); + + return true; + } + Debug(_log, "Can't stop HyperHDR instance index '%d' with name '%s' it's not running'", instance, QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); + return false; + } + Debug(_log, "Can't stop HyperHDR instance index '%d' it doesn't exist in DB", instance); + return false; +} + +bool HyperHdrManager::createInstance(const QString& name, bool start) +{ + quint8 inst; + if (_instanceTable->createInstance(name, inst)) + { + Info(_log, "New HyperHDR instance created with name '%s'", QSTRING_CSTR(name)); + + if (start) + startInstance(inst); + else + emit SignalInstancesListChanged(); + + return true; + } + return false; +} + +bool HyperHdrManager::deleteInstance(quint8 inst) +{ + // inst 0 can't be deleted + if (!isInstAllowed(inst)) + return false; + + // stop it if required as blocking and wait + bool stopped = stopInstance(inst); + + if (_instanceTable->deleteInstance(inst)) + { + Info(_log, "Hyperhdr instance with index '%d' has been deleted", inst); + + if (!stopped) + emit SignalInstancesListChanged(); + + return true; + } + return false; +} + +bool HyperHdrManager::saveName(quint8 inst, const QString& name) +{ + if (_instanceTable->saveName(inst, name)) + { + emit SignalInstancesListChanged(); + return true; + } + return false; +} + +void HyperHdrManager::handleInstanceJustStarted() +{ + HyperHdrInstance* hyperhdr = qobject_cast(sender()); + quint8 instance = hyperhdr->getInstanceIndex(); + + if (_startingInstances.contains(instance)) + { + Info(_log, "HyperHDR instance '%s' has been started", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); + + std::shared_ptr runningInstance = _startingInstances.value(instance); + _runningInstances.insert(instance, runningInstance); + _startingInstances.remove(instance); + + emit SignalInstanceStateChanged(InstanceState::START, instance, _instanceTable->getNamebyIndex(instance)); + emit SignalInstancesListChanged(); + + if (_pendingRequests.contains(instance)) + { + PendingRequests def = _pendingRequests.take(instance); + emit SignalStartInstanceResponse(def.caller, def.tan); + _pendingRequests.remove(instance); + } + } + else + Error(_log, "Could not find instance '%s (index: %i)' in the starting list", + QSTRING_CSTR(_instanceTable->getNamebyIndex(instance)), instance); +} + +const QJsonObject HyperHdrManager::getBackup() +{ + return _instanceTable->getBackup(); +} + + +QString HyperHdrManager::restoreBackup(const QJsonObject& message) +{ + QString error("Empty instance table manager"); + + if (_instanceTable != nullptr) + { + error = _instanceTable->restoreBackup(message); + } + + return error; +} + +void HyperHdrManager::saveCalibration(QString saveData) +{ + HyperHdrInstance* instance = HyperHdrManager::getHyperHdrInstance(0).get(); + + if (instance != nullptr) + QUEUE_CALL_1(instance, saveCalibration, QString, saveData) + else + Error(_log, "Hyperhdr instance is not running...can't save the calibration data"); +} diff --git a/sources/base/ImageToLedsMap.cpp b/sources/base/ImageColorAveraging.cpp similarity index 70% rename from sources/base/ImageToLedsMap.cpp rename to sources/base/ImageColorAveraging.cpp index 7d149f6a3..fef6e6ed9 100644 --- a/sources/base/ImageToLedsMap.cpp +++ b/sources/base/ImageColorAveraging.cpp @@ -1,11 +1,38 @@ -#include -#include +/* ImageColorAveraging.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include #define push_back_index(list, index) list.push_back((index) * 3) using namespace hyperhdr; -ImageToLedsMap::ImageToLedsMap( +ImageColorAveraging::ImageColorAveraging( Logger* _log, const int mappingType, const bool sparseProcessing, @@ -14,7 +41,7 @@ ImageToLedsMap::ImageToLedsMap( const unsigned horizontalBorder, const unsigned verticalBorder, const quint8 instanceIndex, - const std::vector& leds) + const std::vector& leds) : _width(width) , _height(height) , _horizontalBorder(horizontalBorder) @@ -24,7 +51,6 @@ ImageToLedsMap::ImageToLedsMap( , _groupMin(-1) , _groupMax(-1) { - // Sanity check of the size of the borders (and width and height) Q_ASSERT(_width > 2 * _verticalBorder); Q_ASSERT(_height > 2 * _horizontalBorder); Q_ASSERT(_width < 10000); @@ -32,7 +58,6 @@ ImageToLedsMap::ImageToLedsMap( _mappingType = mappingType; - // Reserve enough space in the map for the leds _colorsMap.reserve(leds.size()); const int32_t xOffset = _verticalBorder; @@ -44,24 +69,21 @@ ImageToLedsMap::ImageToLedsMap( size_t totalCapasity = 0; int ledCounter = 0; - for (const Led& led : leds) + for (const LedString::Led& led : leds) { ledCounter++; - // skip leds without area if ((led.maxX_frac - led.minX_frac) < 1e-6 || (led.maxY_frac - led.minY_frac) < 1e-6) { _colorsMap.emplace_back(); continue; } - // Compute the index boundaries for this led int32_t minX_idx = xOffset + int32_t(qRound(actualWidth * led.minX_frac)); int32_t maxX_idx = xOffset + int32_t(qRound(actualWidth * led.maxX_frac)); int32_t minY_idx = yOffset + int32_t(qRound(actualHeight * led.minY_frac)); int32_t maxY_idx = yOffset + int32_t(qRound(actualHeight * led.maxY_frac)); - // make sure that the area is at least a single led large minX_idx = qMin(minX_idx, xOffset + actualWidth - 1); if (minX_idx == maxX_idx) { @@ -73,7 +95,6 @@ ImageToLedsMap::ImageToLedsMap( maxY_idx++; } - // Add all the indices in the above defined rectangle to the indices for this led const int32_t maxYLedCount = qMin(maxY_idx, yOffset + actualHeight); const int32_t maxXLedCount = qMin(maxX_idx, xOffset + actualWidth); const int32_t realYLedCount = qAbs(maxYLedCount - minY_idx); @@ -93,7 +114,7 @@ ImageToLedsMap::ImageToLedsMap( std::vector ledColor; ledColor.reserve((sparseIndexes) ? ((static_cast(realYLedCount / 2)) + (realYLedCount % 2)) * ((realXLedCount / 2) + (realXLedCount % 2)) : totalSize); - if (ImageProcessor::mappingTypeToInt(QString("weighted")) == _mappingType) + if (ImageToLedManager::mappingTypeToInt(QString("weighted")) == _mappingType) { bool left = led.minX_frac == 0; bool right = led.maxX_frac == 1; @@ -171,7 +192,6 @@ ImageToLedsMap::ImageToLedsMap( } } - // Add the constructed vector to the map _colorsMap.push_back(ledColor); _colorsGroups.push_back(led.group); if (_groupMin == -1 || led.group < _groupMin) @@ -186,35 +206,34 @@ ImageToLedsMap::ImageToLedsMap( totalCount, totalCapasity, (sparseProcessing) ? "enabled" : "disabled", width, height, leds.size()); } -unsigned ImageToLedsMap::width() const +unsigned ImageColorAveraging::width() const { return _width; } -unsigned ImageToLedsMap::height() const +unsigned ImageColorAveraging::height() const { return _height; } -unsigned ImageToLedsMap::horizontalBorder() const +unsigned ImageColorAveraging::horizontalBorder() const { return _horizontalBorder; } -unsigned ImageToLedsMap::verticalBorder() const { +unsigned ImageColorAveraging::verticalBorder() const { return _verticalBorder; } -std::vector ImageToLedsMap::Process(const Image& image, uint16_t* advanced) +void ImageColorAveraging::Process(std::vector& colors, const Image& image, uint16_t* advanced) { - std::vector colors; switch (_mappingType) { case 3: - case 2: colors = getMeanAdvLedColor(image, advanced); break; - case 1: colors = getUniLedColor(image); break; - default: colors = getMeanLedColor(image); + case 2: getMeanAdvLedColor(colors, image, advanced); break; + case 1: getUniLedColor(colors, image); break; + default: getMeanLedColor(colors, image); } if (_groupMax > 0 && _mappingType != 1) @@ -224,7 +243,7 @@ std::vector ImageToLedsMap::Process(const Image& image, uint int32_t r = 0, g = 0, b = 0, c = 0; auto groupIn = _colorsGroups.begin(); - for (auto _rgb = colors.begin(); _rgb != colors.end(); _rgb++, groupIn++) + for (auto _rgb = colors.begin(); _rgb != colors.end(); ++_rgb, ++groupIn) if (*groupIn == i) { r += (*_rgb).red; @@ -241,7 +260,7 @@ std::vector ImageToLedsMap::Process(const Image& image, uint } auto groupOut = _colorsGroups.begin(); - for (auto _rgb = colors.begin(); _rgb != colors.end(); _rgb++, groupOut++) + for (auto _rgb = colors.begin(); _rgb != colors.end(); ++_rgb, ++groupOut) if (*groupOut == i) { (*_rgb).red = r; @@ -250,77 +269,31 @@ std::vector ImageToLedsMap::Process(const Image& image, uint } } } - - return colors; } -std::vector ImageToLedsMap::getMeanLedColor(const Image& image) const +void ImageColorAveraging::getMeanLedColor(std::vector& ledColors, const Image& image) const { - std::vector ledColors(_colorsMap.size(), ColorRgb{ 0,0,0 }); - - // Sanity check for the number of leds - //assert(_colorsMap.size() == ledColors.size()); - if (_colorsMap.size() != ledColors.size()) - { - Debug(Logger::getInstance("HYPERHDR"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size()); - return ledColors; - } - - // Iterate each led and compute the mean - auto led = ledColors.begin(); - for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led) + for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors) { - const ColorRgb color = calcMeanColor(image, *colors); - *led = color; + ledColors.push_back(calcMeanColor(image, *colors)); } - - return ledColors; } -std::vector ImageToLedsMap::getUniLedColor(const Image& image) const +void ImageColorAveraging::getUniLedColor(std::vector& ledColors, const Image& image) const { - std::vector ledColors(_colorsMap.size(), ColorRgb{ 0,0,0 }); - - // Sanity check for the number of leds - // assert(_colorsMap.size() == ledColors.size()); - if (_colorsMap.size() != ledColors.size()) - { - Debug(Logger::getInstance("HYPERHDR"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size()); - return ledColors; - } - - // calculate uni color - const ColorRgb color = calcMeanColor(image); - std::fill(ledColors.begin(), ledColors.end(), color); - - return ledColors; + ledColors.resize(_colorsMap.size(), calcMeanColor(image)); } -std::vector ImageToLedsMap::getMeanAdvLedColor(const Image& image, uint16_t* lut) const +void ImageColorAveraging::getMeanAdvLedColor(std::vector& ledColors, const Image& image, uint16_t* lut) const { - std::vector ledColors(_colorsMap.size(), ColorRgb{ 0,0,0 }); - - // Sanity check for the number of leds - //assert(_colorsMap.size() == ledColors.size()); - if (_colorsMap.size() != ledColors.size()) + for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors) { - Debug(Logger::getInstance("HYPERHDR"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size()); - return ledColors; + ledColors.push_back(calcMeanAdvColor(image, *colors, lut)); } - - // Iterate each led and compute the mean - auto led = ledColors.begin(); - for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led) - { - const ColorRgb color = calcMeanAdvColor(image, *colors, lut); - *led = color; - } - - return ledColors; } -ColorRgb ImageToLedsMap::calcMeanColor(const Image& image, const std::vector& colors) const +ColorRgb ImageColorAveraging::calcMeanColor(const Image& image, const std::vector& colors) const { const auto colorVecSize = colors.size(); @@ -329,7 +302,6 @@ ColorRgb ImageToLedsMap::calcMeanColor(const Image& image, const std:: return ColorRgb::BLACK; } - // Accumulate the sum of each separate color channel uint_fast32_t sumRed = 0; uint_fast32_t sumGreen = 0; uint_fast32_t sumBlue = 0; @@ -342,16 +314,14 @@ ColorRgb ImageToLedsMap::calcMeanColor(const Image& image, const std:: sumBlue += imgData[colorOffset + 2]; } - // Compute the average of each color channel const uint8_t avgRed = uint8_t(sumRed / colorVecSize); const uint8_t avgGreen = uint8_t(sumGreen / colorVecSize); const uint8_t avgBlue = uint8_t(sumBlue / colorVecSize); - // Return the computed color return { avgRed, avgGreen, avgBlue }; } -ColorRgb ImageToLedsMap::calcMeanAdvColor(const Image& image, const std::vector& colors, uint16_t* lut) const +ColorRgb ImageColorAveraging::calcMeanAdvColor(const Image& image, const std::vector& colors, uint16_t* lut) const { const auto colorVecSize = colors.size(); @@ -360,7 +330,6 @@ ColorRgb ImageToLedsMap::calcMeanAdvColor(const Image& image, const st return ColorRgb::BLACK; } - // Accumulate the sum of each seperate color channel uint_fast64_t sum1 = 0; uint_fast64_t sumRed1 = 0; uint_fast64_t sumGreen1 = 0; @@ -408,9 +377,8 @@ ColorRgb ImageToLedsMap::calcMeanAdvColor(const Image& image, const st } } -ColorRgb ImageToLedsMap::calcMeanColor(const Image& image) const +ColorRgb ImageColorAveraging::calcMeanColor(const Image& image) const { - // Accumulate the sum of each separate color channel uint_fast32_t sumRed = 0; uint_fast32_t sumGreen = 0; uint_fast32_t sumBlue = 0; @@ -425,11 +393,9 @@ ColorRgb ImageToLedsMap::calcMeanColor(const Image& image) const sumBlue += imgData[idx + 2]; } - // Compute the average of each color channel const uint8_t avgRed = uint8_t(sumRed / (imageSize/3)); const uint8_t avgGreen = uint8_t(sumGreen / (imageSize/3)); const uint8_t avgBlue = uint8_t(sumBlue / (imageSize/3)); - // Return the computed color return { avgRed, avgGreen, avgBlue }; } diff --git a/sources/base/ImageProcessingUnit.cpp b/sources/base/ImageProcessingUnit.cpp deleted file mode 100644 index aeeb6aebf..000000000 --- a/sources/base/ImageProcessingUnit.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* ImageProcessingUnit.cpp -* -* MIT License -* -* Copyright (c) 2023 awawa-dev -* -* Project homesite: https://github.com/awawa-dev/HyperHDR -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. - -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -using namespace hyperhdr; - - -ImageProcessingUnit::ImageProcessingUnit(HyperHdrInstance* hyperhdr) - : QObject(hyperhdr), - _priority(-1), - _hyperhdr(hyperhdr) -{ - - connect(this, &ImageProcessingUnit::processImageSignal, this, &ImageProcessingUnit::processImage, Qt::ConnectionType::QueuedConnection); - connect(this, &ImageProcessingUnit::clearQueueImageSignal, this, &ImageProcessingUnit::clearQueueImage); - connect(this, &ImageProcessingUnit::queueImageSignal, this, &ImageProcessingUnit::queueImage); -} - -ImageProcessingUnit::~ImageProcessingUnit() -{ - clearQueueImage(); -} - -void ImageProcessingUnit::queueImage(int priority, const Image& image) -{ - if (image.width() != 1 || image.height() != 1) - { - _frameBuffer = image; - _priority = priority; - - emit processImageSignal(); - } -} - -void ImageProcessingUnit::clearQueueImage() -{ - _frameBuffer = Image(); - _priority = -1; -} - - -void ImageProcessingUnit::processImage() -{ - if (_priority < 0 || (_frameBuffer.width() == 1 && _frameBuffer.height() == 1)) - return; - - ImageProcessor* imageProcessor = _hyperhdr->getImageProcessor(); - - if (imageProcessor != nullptr) - { - imageProcessor->setSize(_frameBuffer);; - imageProcessor->verifyBorder(_frameBuffer); - - std::shared_ptr image2leds = imageProcessor->_imageToLedColors; - - if (image2leds != nullptr && image2leds->width() == _frameBuffer.width() && image2leds->height() == _frameBuffer.height()) - { - std::vector colors = image2leds->Process(_frameBuffer, imageProcessor->advanced); - - _hyperhdr->updateLedsValues(_priority, colors); - emit dataReadySignal(colors); - emit _hyperhdr->onCurrentImage(); - } - } - - clearQueueImage(); -} diff --git a/sources/base/ImageProcessor.cpp b/sources/base/ImageProcessor.cpp deleted file mode 100644 index 716d8c780..000000000 --- a/sources/base/ImageProcessor.cpp +++ /dev/null @@ -1,221 +0,0 @@ - - -#include -#include -#include - -// Blacborder includes -#include - -using namespace hyperhdr; - -void ImageProcessor::registerProcessingUnit( - const unsigned width, - const unsigned height, - const unsigned horizontalBorder, - const unsigned verticalBorder) -{ - if (width > 0 && height > 0) - _imageToLedColors = std::make_shared( - _log, - _mappingType, - _sparseProcessing, - width, - height, - horizontalBorder, - verticalBorder, - _instanceIndex, - _ledString.leds()); - else - _imageToLedColors = nullptr; -} - - -// global transform method -int ImageProcessor::mappingTypeToInt(const QString& mappingType) -{ - if (mappingType == "weighted") - return 3; - - if (mappingType == "advanced") - return 2; - - if (mappingType == "unicolor_mean") - return 1; - - return 0; -} -// global transform method -QString ImageProcessor::mappingTypeToStr(int mappingType) -{ - if (mappingType == 3) - return "weighted"; - - if (mappingType == 2) - return "advanced"; - - if (mappingType == 1) - return "unicolor_mean"; - - return "multicolor_mean"; -} - -ImageProcessor::ImageProcessor(const LedString& ledString, HyperHdrInstance* hyperhdr) - : QObject(hyperhdr) - , _log(Logger::getInstance(QString("IMAGETOLED%1").arg(hyperhdr->getInstanceIndex()))) - , _ledString(ledString) - , _borderProcessor(new BlackBorderProcessor(hyperhdr, this)) - , _imageToLedColors(nullptr) - , _mappingType(0) - , _sparseProcessing(false) - , _instanceIndex(hyperhdr->getInstanceIndex()) -{ - // init - handleSettingsUpdate(settings::type::COLOR, hyperhdr->getSetting(settings::type::COLOR)); - // listen for changes in color - ledmapping - connect(hyperhdr, &HyperHdrInstance::settingsChanged, this, &ImageProcessor::handleSettingsUpdate); - - for (int i = 0; i < 256; i++) - advanced[i] = i * i; -} - -ImageProcessor::~ImageProcessor() -{ -} - -void ImageProcessor::handleSettingsUpdate(settings::type type, const QJsonDocument& config) -{ - if (type == settings::type::COLOR) - { - const QJsonObject& obj = config.object(); - int newType = mappingTypeToInt(obj["imageToLedMappingType"].toString()); - if (_mappingType != newType) - { - setLedMappingType(newType); - } - - bool newSparse = obj["sparse_processing"].toBool(false); - setSparseProcessing(newSparse); - } -} - -void ImageProcessor::setSize(unsigned width, unsigned height) -{ - // Check if the existing buffer-image is already the correct dimensions - if (_imageToLedColors !=nullptr && _imageToLedColors->width() == width && _imageToLedColors->height() == height) - { - return; - } - - // Construct a new buffer and mapping - registerProcessingUnit(width, height, 0, 0); -} - -void ImageProcessor::setLedString(const LedString& ledString) -{ - if (_imageToLedColors != nullptr) - { - _ledString = ledString; - - // get current width/height - unsigned width = _imageToLedColors->width(); - unsigned height = _imageToLedColors->height(); - - // Construct a new buffer and mapping - registerProcessingUnit(width, height, 0, 0); - } -} - -void ImageProcessor::setBlackbarDetectDisable(bool enable) -{ - _borderProcessor->setHardDisable(enable); -} - -bool ImageProcessor::blackBorderDetectorEnabled() const -{ - return _borderProcessor->enabled(); -} - -void ImageProcessor::setSparseProcessing(bool sparseProcessing) -{ - bool _orgmappingType = _sparseProcessing; - - _sparseProcessing = sparseProcessing; - - Debug(_log, "setSparseProcessing to %d", _sparseProcessing); - if (_orgmappingType != _sparseProcessing && _imageToLedColors != nullptr) - { - unsigned width = _imageToLedColors->width(); - unsigned height = _imageToLedColors->height(); - - registerProcessingUnit(width, height, 0, 0); - } -} - -void ImageProcessor::setLedMappingType(int mapType) -{ - int _orgmappingType = _mappingType; - - _mappingType = mapType; - - Debug(_log, "Set LED mapping type to %s", QSTRING_CSTR(mappingTypeToStr(mapType))); - - if (_orgmappingType != _mappingType && _imageToLedColors != nullptr) - { - unsigned width = _imageToLedColors->width(); - unsigned height = _imageToLedColors->height(); - - registerProcessingUnit(width, height, 0, 0); - } -} - -bool ImageProcessor::getScanParameters(size_t led, double& hscanBegin, double& hscanEnd, double& vscanBegin, double& vscanEnd) const -{ - if (led < _ledString.leds().size()) - { - const Led& l = _ledString.leds()[led]; - hscanBegin = l.minX_frac; - hscanEnd = l.maxX_frac; - vscanBegin = l.minY_frac; - vscanEnd = l.maxY_frac; - } - - return false; -} - -void ImageProcessor::verifyBorder(const Image& image) -{ - if (!_borderProcessor->enabled() && (_imageToLedColors->horizontalBorder() != 0 || _imageToLedColors->verticalBorder() != 0)) - { - Debug(_log, "Reset border"); - _borderProcessor->process(image); - - registerProcessingUnit(image.width(), image.height(), 0, 0); - } - - if (_borderProcessor->enabled() && _borderProcessor->process(image)) - { - const hyperhdr::BlackBorder border = _borderProcessor->getCurrentBorder(); - - if (border.unknown) - { - // Construct a new buffer and mapping - registerProcessingUnit(image.width(), image.height(), 0, 0); - } - else - { - // Construct a new buffer and mapping - registerProcessingUnit(image.width(), image.height(), border.horizontalSize, border.verticalSize); - } - } -} - -void ImageProcessor::setSize(const Image& image) -{ - setSize(image.width(), image.height()); -} - -int ImageProcessor::getLedMappingType() const -{ - return _mappingType; -} diff --git a/sources/base/ImageToLedManager.cpp b/sources/base/ImageToLedManager.cpp new file mode 100644 index 000000000..6d2b59860 --- /dev/null +++ b/sources/base/ImageToLedManager.cpp @@ -0,0 +1,252 @@ +/* ImageToLedManager.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include +#include +#include + +using namespace hyperhdr; + +void ImageToLedManager::registerProcessingUnit( + const unsigned width, + const unsigned height, + const unsigned horizontalBorder, + const unsigned verticalBorder) +{ + if (width > 0 && height > 0) + _colorAveraging = std::unique_ptr(new ImageColorAveraging( + _log, + _mappingType, + _sparseProcessing, + width, + height, + horizontalBorder, + verticalBorder, + _instanceIndex, + _ledString.leds())); + else + _colorAveraging = nullptr; +} + + +// global transform method +int ImageToLedManager::mappingTypeToInt(const QString& mappingType) +{ + if (mappingType == "weighted") + return 3; + + if (mappingType == "advanced") + return 2; + + if (mappingType == "unicolor_mean") + return 1; + + return 0; +} +// global transform method +QString ImageToLedManager::mappingTypeToStr(int mappingType) +{ + if (mappingType == 3) + return "weighted"; + + if (mappingType == 2) + return "advanced"; + + if (mappingType == 1) + return "unicolor_mean"; + + return "multicolor_mean"; +} + +ImageToLedManager::ImageToLedManager(const LedString& ledString, HyperHdrInstance* hyperhdr) + : QObject(hyperhdr) + , _instanceIndex(hyperhdr->getInstanceIndex()) + , _log(Logger::getInstance(QString("IMAGETOLED_MNG%1").arg(_instanceIndex))) + , _ledString(ledString) + , _borderProcessor(new BlackBorderProcessor(hyperhdr, this)) + , _colorAveraging(nullptr) + , _mappingType(0) + , _sparseProcessing(false) +{ + // init + handleSettingsUpdate(settings::type::COLOR, hyperhdr->getSetting(settings::type::COLOR)); + // listen for changes in color - ledmapping + connect(hyperhdr, &HyperHdrInstance::SignalInstanceSettingsChanged, this, &ImageToLedManager::handleSettingsUpdate); + + for (int i = 0; i < 256; i++) + _advanced[i] = i * i; + Debug(_log, "ImageToLedManager initialized"); +} + +void ImageToLedManager::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +{ + if (type == settings::type::COLOR) + { + const QJsonObject& obj = config.object(); + int newType = mappingTypeToInt(obj["imageToLedMappingType"].toString()); + if (_mappingType != newType) + { + setLedMappingType(newType); + } + + bool newSparse = obj["sparse_processing"].toBool(false); + setSparseProcessing(newSparse); + } +} + +void ImageToLedManager::setSize(unsigned width, unsigned height) +{ + // Check if the existing buffer-image is already the correct dimensions + if (_colorAveraging !=nullptr && _colorAveraging->width() == width && _colorAveraging->height() == height) + { + return; + } + + // Construct a new buffer and mapping + registerProcessingUnit(width, height, 0, 0); +} + +void ImageToLedManager::setLedString(const LedString& ledString) +{ + if (_colorAveraging != nullptr) + { + _ledString = ledString; + + // get current width/height + unsigned width = _colorAveraging->width(); + unsigned height = _colorAveraging->height(); + + // Construct a new buffer and mapping + registerProcessingUnit(width, height, 0, 0); + } +} + +void ImageToLedManager::setBlackbarDetectDisable(bool enable) +{ + _borderProcessor->setHardDisable(enable); +} + +bool ImageToLedManager::blackBorderDetectorEnabled() const +{ + return _borderProcessor->enabled(); +} + +void ImageToLedManager::processFrame(std::vector& colors, const Image& frameBuffer) +{ + setSize(frameBuffer);; + verifyBorder(frameBuffer); + + if (_colorAveraging != nullptr && _colorAveraging->width() == frameBuffer.width() && _colorAveraging->height() == frameBuffer.height()) + { + _colorAveraging->Process(colors, frameBuffer, _advanced); + } +} + +void ImageToLedManager::setSparseProcessing(bool sparseProcessing) +{ + bool _orgmappingType = _sparseProcessing; + + _sparseProcessing = sparseProcessing; + + Debug(_log, "setSparseProcessing to %d", _sparseProcessing); + if (_orgmappingType != _sparseProcessing && _colorAveraging != nullptr) + { + unsigned width = _colorAveraging->width(); + unsigned height = _colorAveraging->height(); + + registerProcessingUnit(width, height, 0, 0); + } +} + +void ImageToLedManager::setLedMappingType(int mapType) +{ + int _orgmappingType = _mappingType; + + _mappingType = mapType; + + Debug(_log, "Set LED mapping type to %s", QSTRING_CSTR(mappingTypeToStr(mapType))); + + if (_orgmappingType != _mappingType && _colorAveraging != nullptr) + { + unsigned width = _colorAveraging->width(); + unsigned height = _colorAveraging->height(); + + registerProcessingUnit(width, height, 0, 0); + } +} + +bool ImageToLedManager::getScanParameters(size_t led, double& hscanBegin, double& hscanEnd, double& vscanBegin, double& vscanEnd) const +{ + if (led < _ledString.leds().size()) + { + const LedString::Led& l = _ledString.leds()[led]; + hscanBegin = l.minX_frac; + hscanEnd = l.maxX_frac; + vscanBegin = l.minY_frac; + vscanEnd = l.maxY_frac; + } + + return false; +} + +void ImageToLedManager::verifyBorder(const Image& image) +{ + if (!_borderProcessor->enabled() && (_colorAveraging->horizontalBorder() != 0 || _colorAveraging->verticalBorder() != 0)) + { + Debug(_log, "Reset border"); + _borderProcessor->process(image); + + registerProcessingUnit(image.width(), image.height(), 0, 0); + } + + if (_borderProcessor->enabled() && _borderProcessor->process(image)) + { + const hyperhdr::BlackBorder border = _borderProcessor->getCurrentBorder(); + + if (border.unknown) + { + // Construct a new buffer and mapping + registerProcessingUnit(image.width(), image.height(), 0, 0); + } + else + { + // Construct a new buffer and mapping + registerProcessingUnit(image.width(), image.height(), border.horizontalSize, border.verticalSize); + } + } +} + +void ImageToLedManager::setSize(const Image& image) +{ + setSize(image.width(), image.height()); +} + +int ImageToLedManager::getLedMappingType() const +{ + return _mappingType; +} diff --git a/sources/base/SettingsManager.cpp b/sources/base/InstanceConfig.cpp similarity index 61% rename from sources/base/SettingsManager.cpp rename to sources/base/InstanceConfig.cpp index eab5dfe3b..bf171a951 100644 --- a/sources/base/SettingsManager.cpp +++ b/sources/base/InstanceConfig.cpp @@ -1,34 +1,38 @@ -// proj -#include +#ifndef PCH_ENABLED + #include +#endif -// util +#include #include #include - -// json schema process #include #include - -// write config to filesystem #include -QJsonObject SettingsManager::schemaJson; +QJsonObject InstanceConfig::_schemaJson; +QMutex InstanceConfig::_lockerSettingsManager; +std::atomic InstanceConfig::_backupMade(false); -SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonlyMode) +InstanceConfig::InstanceConfig(bool master, quint8 instanceIndex, QObject* parent, bool readonlyMode) : QObject(parent) - , _log(Logger::getInstance("SETTINGSMGR")) - , _sTable(new SettingsTable(instance, this)) - , _readonlyMode(readonlyMode) + , _log(Logger::getInstance(QString("INSTANCE_CFG%1").arg((master) ? QString() : QString::number(instanceIndex)))) { + QMutexLocker locker(&_lockerSettingsManager); + + Info(_log, "Loading instance configuration"); + + _sTable = std::unique_ptr(new SettingsTable(instanceIndex)); + _readonlyMode = readonlyMode; + _sTable->setReadonlyMode(_readonlyMode); // get schema - if (schemaJson.isEmpty()) + if (_schemaJson.isEmpty()) { Q_INIT_RESOURCE(resource); try { - schemaJson = QJsonUtils::readSchema(":/hyperhdr-schema"); + _schemaJson = QJsonUtils::readSchema(":/hyperhdr-schema"); } catch (const std::runtime_error& error) { @@ -62,13 +66,18 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly } // fill database with default data if required + bool hasData = false; for (const auto& key : keyList) { QString val = defValueList.takeFirst(); // prevent overwrite if (!_sTable->recordExist(key)) _sTable->createSettingsRecord(key, val); + else + hasData = true; } + if (!hasData && !_backupMade) + _backupMade = true; // need to validate all data in database constuct the entire data object // TODO refactor schemaChecker to accept QJsonArray in validate(); QJsonDocument container? To validate them per entry... @@ -100,18 +109,30 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly // validate full dbconfig against schema, on error we need to rewrite entire table QJsonSchemaChecker schemaChecker; - schemaChecker.setSchema(schemaJson); + schemaChecker.setSchema(_schemaJson); QPair valid = schemaChecker.validate(dbConfig); - // check if our main schema syntax is IO + bool upgradeNeeded = upgradeDB(dbConfig); + if (!valid.second) { for (auto& schemaError : schemaChecker.getMessages()) Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError)); throw std::runtime_error("The config schema has invalid syntax. This should never happen! Go fix it!"); } - if (!valid.first) + if (!valid.first || upgradeNeeded) { Info(_log, "Table upgrade required..."); + if (!_backupMade.exchange(true) && !_readonlyMode) + { + _backupMade = true; + Info(_log, "Creating DB backup first."); + QString resultFile = _sTable->createLocalBackup(); + if (!resultFile.isEmpty()) + Info(_log, "The backup is saved as: %s", QSTRING_CSTR(resultFile)); + else + Warning(_log, "Could not create a backup"); + } + Info(_log, "Upgrading..."); dbConfig = schemaChecker.getAutoCorrectedConfig(dbConfig); for (auto& schemaError : schemaChecker.getMessages()) @@ -125,12 +146,60 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly Info(_log, "Settings database initialized"); } -QJsonDocument SettingsManager::getSetting(settings::type type) const +bool InstanceConfig::upgradeDB(QJsonObject& dbConfig) +{ + auto generalObject = dbConfig["general"].toObject(); + int version = generalObject["version"].toInt(-1); + int orgVersion = version; + + if (version < 0) + { + Warning(_log, "Could not read DB version"); + return false; + } + + // HyperHDR v20 update + if (version < 2) + { + auto colorObject = dbConfig["color"].toObject(); + if (colorObject.contains("channelAdjustment")) + { + QJsonArray adjUpdate = colorObject["channelAdjustment"].toArray(), newArray; + for (auto iter = adjUpdate.begin(); iter != adjUpdate.end(); ++iter) + { + QJsonObject newObject = (iter)->toObject(); + int threshold = newObject["backlightThreshold"].toInt(0); + if (threshold > 1) + { + newObject["backlightThreshold"] = threshold - 1; + } + newArray.append(newObject); + } + colorObject["channelAdjustment"] = newArray; + dbConfig["color"] = colorObject; + /////////////////////////////////////////////////////////// + version = 2; + Info(_log, "DB has been upgraded to version: %i", version); + } + } + + generalObject["version"] = version; + dbConfig["general"] = generalObject; + + return orgVersion != version; +} + +InstanceConfig::~InstanceConfig() +{ + Debug(_log, "InstanceConfig has been deleted"); +} + +QJsonDocument InstanceConfig::getSetting(settings::type type) const { return _sTable->getSettingsRecord(settings::typeToString(type)); } -QJsonObject SettingsManager::getSettings() const +QJsonObject InstanceConfig::getSettings() const { QJsonObject dbConfig; for (const auto& key : _qconfig.keys()) @@ -144,14 +213,10 @@ QJsonObject SettingsManager::getSettings() const return dbConfig; } -bool SettingsManager::saveSettings(QJsonObject config, bool correct) +bool InstanceConfig::saveSettings(QJsonObject config, bool correct) { - // optional data upgrades e.g. imported legacy/older configs - // handleConfigUpgrade(config); - - // we need to validate data against schema QJsonSchemaChecker schemaChecker; - schemaChecker.setSchema(schemaJson); + schemaChecker.setSchema(_schemaJson); if (!schemaChecker.validate(config).first) { if (!correct) @@ -199,14 +264,14 @@ bool SettingsManager::saveSettings(QJsonObject config, bool correct) } else { - emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(data.toUtf8())); + emit SignalInstanceSettingsChanged(settings::stringToType(key), QJsonDocument::fromJson(data.toUtf8())); } } } return rc; } -void SettingsManager::saveSetting(settings::type key, QString saveData) +void InstanceConfig::saveSetting(settings::type key, QString saveData) { if (!_sTable->createSettingsRecord(settings::typeToString(key), saveData)) { @@ -214,6 +279,6 @@ void SettingsManager::saveSetting(settings::type key, QString saveData) } else { - emit settingsChanged(key, QJsonDocument::fromJson(saveData.toUtf8())); + emit SignalInstanceSettingsChanged(key, QJsonDocument::fromJson(saveData.toUtf8())); } } diff --git a/sources/base/LedCalibration.cpp b/sources/base/LedCalibration.cpp new file mode 100644 index 000000000..0097d527f --- /dev/null +++ b/sources/base/LedCalibration.cpp @@ -0,0 +1,164 @@ +/* LedCalibration.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + + #include +#endif + +#include + +LedCalibration::LedCalibration(quint8 instance, int ledNumber, const QJsonObject& colorConfig) + : _perLedConfig(ledNumber, nullptr) + , _log(Logger::getInstance("LED_CALIBRATION" + QString::number(instance))) +{ + int index = 0; + const QRegularExpression overallExp("^([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*$"); + const QJsonArray& adjustmentConfigArray = colorConfig["channelAdjustment"].toArray(); + + for (auto item = adjustmentConfigArray.begin(); item != adjustmentConfigArray.end(); ++item, ++index) + { + const QJsonObject& config = (*item).toObject(); + const QString range = config["leds"].toString("").trimmed(); + std::shared_ptr colorAdjustment = std::make_shared(instance, config); + + _calibrationConfig.push_back(colorAdjustment); + + if (range.compare("*") == 0) + { + setAdjustmentForLed(index, colorAdjustment, 0, ledNumber - 1); + continue; + } + + if (!overallExp.match(range).hasMatch()) + { + Warning(_log, "Unrecognized segment range configuration: %s", QSTRING_CSTR(range)); + continue; + } + + for (const auto& segment : range.split(",")) + { + int first = 0, second = 0; + bool ok = false; + if (segment.contains("-")) + { + QStringList ledIndices = segment.split("-"); + if (ledIndices.size() == 2) + { + first = ledIndices[0].toInt(&ok); + second = (ok) ? ledIndices[1].toInt(&ok) : 0; + } + } + else + { + first = second = segment.toInt(&ok); + } + + if (ok) + setAdjustmentForLed(index, colorAdjustment, first, second); + else + Warning(_log, "Unrecognized segment range configuration: %s", QSTRING_CSTR(segment)); + } + } +} + +LedCalibration::~LedCalibration() +{ + Debug(_log, "LedCalibration is released"); +} + +void LedCalibration::setAdjustmentForLed(int index, std::shared_ptr& adjustment, size_t startLed, size_t endLed) +{ + Debug(_log, "Calibration config '%i' for LED segment: [%d, %d]", index, startLed, endLed); + + for (size_t iLed = startLed; iLed <= endLed; ++iLed) + { + _perLedConfig[iLed] = adjustment; + } +} + +bool LedCalibration::verifyAdjustments() const +{ + bool ok = true; + + for (auto adjustment = _perLedConfig.begin(); adjustment != _perLedConfig.end(); ++adjustment) + if (*adjustment == nullptr) + { + Warning(_log, "No calibration set for led at index: %i", adjustment - _perLedConfig.begin()); + ok = false; + } + + return ok; +} + +void LedCalibration::setBacklightEnabled(bool enable) +{ + for (auto&& adjustment : _calibrationConfig) + { + adjustment->setBackLightEnabled(enable); + } +} + +void LedCalibration::applyAdjustment(std::vector& ledColors) +{ + const size_t totalNumber = std::min(_perLedConfig.size(), ledColors.size()); + for (size_t i = 0; i < totalNumber; ++i) + { + std::shared_ptr& adjustment = _perLedConfig[i]; + + if (adjustment != nullptr) + { + _perLedConfig[i]->calibrate(ledColors[i]); + } + } +} + +QJsonArray LedCalibration::getAdjustmentState() const +{ + QJsonArray adjustmentArray; + + for (auto&& colorAdjustment: _calibrationConfig) + { + QJsonObject adjustment; + colorAdjustment->putCurrentConfig(adjustment); + adjustmentArray.append(adjustment); + } + + return adjustmentArray; +} + +void LedCalibration::updateConfig(const QJsonObject& adjustment) +{ + if (_calibrationConfig.size() > 0) + _calibrationConfig.front()->updateConfig(adjustment); +} + diff --git a/sources/base/LedString.cpp b/sources/base/LedString.cpp index 10de12f91..7809254b0 100644 --- a/sources/base/LedString.cpp +++ b/sources/base/LedString.cpp @@ -1,23 +1,75 @@ -// STL includes -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif #include -std::vector& LedString::leds() +std::vector& LedString::leds() { return mLeds; } -const std::vector& LedString::leds() const +const std::vector& LedString::leds() const { return mLeds; } +QString LedString::colorOrderToString(LedString::ColorOrder colorOrder) +{ + switch (colorOrder) + { + case ColorOrder::ORDER_RGB: + return "rgb"; + case ColorOrder::ORDER_RBG: + return "rbg"; + case ColorOrder::ORDER_GRB: + return "grb"; + case ColorOrder::ORDER_BRG: + return "brg"; + case ColorOrder::ORDER_GBR: + return "gbr"; + case ColorOrder::ORDER_BGR: + return "bgr"; + default: + return "not-a-colororder"; + } +} + +LedString::ColorOrder LedString::stringToColorOrder(const QString& order) +{ + if (order == "rgb") + { + return ColorOrder::ORDER_RGB; + } + else if (order == "bgr") + { + return ColorOrder::ORDER_BGR; + } + else if (order == "rbg") + { + return ColorOrder::ORDER_RBG; + } + else if (order == "brg") + { + return ColorOrder::ORDER_BRG; + } + else if (order == "gbr") + { + return ColorOrder::ORDER_GBR; + } + else if (order == "grb") + { + return ColorOrder::ORDER_GRB; + } + + std::cout << "Unknown color order defined (" << order.toStdString() << "). Using RGB." << std::endl; + return ColorOrder::ORDER_RGB; +} -ColorOrder LedString::createColorOrder(const QJsonObject& deviceConfig) +LedString::ColorOrder LedString::createColorOrder(const QJsonObject& deviceConfig) { - return stringToColorOrder(deviceConfig["colorOrder"].toString("rgb")); + return LedString::stringToColorOrder(deviceConfig["colorOrder"].toString("rgb")); } LedString LedString::createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder) @@ -29,7 +81,7 @@ LedString LedString::createLedString(const QJsonArray& ledConfigArray, const Col for (signed i = 0; i < ledConfigArray.size(); ++i) { const QJsonObject& ledConfig = ledConfigArray[i].toObject(); - Led led; + Led led{}; led.minX_frac = qMax(0.0, qMin(1.0, ledConfig["hmin"].toDouble())); led.maxX_frac = qMax(0.0, qMin(1.0, ledConfig["hmax"].toDouble())); @@ -95,9 +147,9 @@ QSize LedString::getLedLayoutGridSize(const QJsonArray& ledConfigArray) // Correct the grid in case it is malformed in width vs height // Expected is at least 50% of width <-> height - if ((gridSize.width() / gridSize.height()) > 2) + if (gridSize.width() > 2 * gridSize.height()) gridSize.setHeight(qMax(1, gridSize.width() / 2)); - else if ((gridSize.width() / gridSize.height()) < 0.5) + else if (gridSize.width() < 0.5 * gridSize.height()) gridSize.setWidth(qMax(1, gridSize.height() / 2)); // Limit to 80px for performance reasons diff --git a/sources/base/LinearSmoothing.cpp b/sources/base/LinearSmoothing.cpp deleted file mode 100644 index fd7dd1a44..000000000 --- a/sources/base/LinearSmoothing.cpp +++ /dev/null @@ -1,578 +0,0 @@ -// Qt includes -#include -#include - -#include -#include - -#include -#include -#include - -using namespace hyperhdr; - -const int64_t DEFAUL_SETTLINGTIME = 200; // settlingtime in ms -const double DEFAUL_UPDATEFREQUENCY = 25; // updatefrequncy in hz -const double MINIMAL_UPDATEFREQUENCY = 20; - - - -LinearSmoothing::LinearSmoothing(const QJsonDocument& config, HyperHdrInstance* hyperhdr) - : QObject(hyperhdr), - _log(Logger::getInstance(QString("SMOOTHING%1").arg(hyperhdr->getInstanceIndex()))), - _hyperhdr(hyperhdr), - _updateInterval(static_cast(1000 / DEFAUL_UPDATEFREQUENCY)), - _settlingTime(DEFAUL_SETTLINGTIME), - _timer(nullptr), - _continuousOutput(false), - _antiFlickeringTreshold(0), - _antiFlickeringStep(0), - _antiFlickeringTimeout(0), - _flushFrame(false), - _targetTime(0), - _previousTime(0), - _pause(false), - _currentConfigId(0), - _enabled(false), - _directMode(false), - _smoothingType(SmoothingType::Linear), - _infoUpdate(true), - _infoInput(true), - _coolDown(0), - _debugCounter(0) -{ - // timer - _timer = new QTimer(this); - _timer->setTimerType(Qt::PreciseTimer); - - // init cfg 0 (default) - addConfig(DEFAUL_SETTLINGTIME, DEFAUL_UPDATEFREQUENCY); - handleSettingsUpdate(settings::type::SMOOTHING, config); - selectConfig(0, true); - - // add pause on cfg 1 - SmoothingCfg cfg(true, 0, 0, false); - _cfgList.push_back(cfg); - - // listen for comp changes - connect(_hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &LinearSmoothing::componentStateChange); - connect(_timer, &QTimer::timeout, this, &LinearSmoothing::updateLeds); -} - -LinearSmoothing::~LinearSmoothing() -{ - delete _timer; -} - -inline uint8_t LinearSmoothing::clamp(int x) -{ - return (x < 0) ? 0 : ((x > 255) ? 255 : uint8_t(x)); -} - -void LinearSmoothing::clearQueuedColors(bool deviceEnabled, bool restarting) -{ - try - { - Info(_log, "Clearing queued colors before: %s%s", - (deviceEnabled) ? "enabling" : "disabling", - (restarting) ? ". Smoothing configuration changed: restarting timer." : ""); - - if (!deviceEnabled || restarting) - { - _timer->stop(); - } - - _timer->setInterval(_updateInterval); - _previousValues.clear(); - _previousTimeouts.clear(); - _previousTime = 0; - _targetValues.clear(); - _targetTime = 0; - _flushFrame = false; - _infoUpdate = true; - _infoInput = true; - _coolDown = 0; - - if (deviceEnabled) - { - _timer->start(); - } - - Info(_log, "Smoothing queue is cleared"); - } - catch (...) - { - Debug(_log, "Smoothing error detected"); - } -} - -void LinearSmoothing::handleSettingsUpdate(settings::type type, const QJsonDocument& config) -{ - if (type == settings::type::SMOOTHING) - { - if (InternalClock::isPreciseSteady()) - Info(_log, "High resolution clock is steady (good)"); - else - Warning(_log, "High resolution clock is NOT STEADY!"); - - QJsonObject obj = config.object(); - - if (enabled() != obj["enable"].toBool(true)) - { - setEnable(obj["enable"].toBool(true)); - } - - _continuousOutput = obj["continuousOutput"].toBool(true); - - SmoothingCfg cfg(false, - static_cast(obj["time_ms"].toInt(DEFAUL_SETTLINGTIME)), - static_cast(std::max(1000.0 / std::max(obj["updateFrequency"].toDouble(DEFAUL_UPDATEFREQUENCY), MINIMAL_UPDATEFREQUENCY), 5.0)), - false); - - auto smoothingType = obj["type"].toString("linear"); - - if (smoothingType == "alternative") - cfg._type = SmoothingType::Alternative; - else - cfg._type = SmoothingType::Linear; - - cfg._antiFlickeringTreshold = obj["lowLightAntiFlickeringTreshold"].toInt(0); - cfg._antiFlickeringStep = obj["lowLightAntiFlickeringValue"].toInt(0); - cfg._antiFlickeringTimeout = obj["lowLightAntiFlickeringTimeout"].toInt(0); - - Info(_log, "Creating config (%d) => type: %s, dirMode: %s, pause: %s, settlingTime: %ims, interval: %ims (%iHz), antiFlickTres: %i, antiFlickStep: %i, antiFlickTime: %i", - _currentConfigId, QSTRING_CSTR(SmoothingCfg::EnumToString(cfg._type)), (cfg._directMode) ? "true" : "false", (cfg._pause) ? "true" : "false", int(cfg._settlingTime), int(cfg._updateInterval), int(1000.0 / cfg._updateInterval), cfg._antiFlickeringTreshold, cfg._antiFlickeringStep, int(cfg._antiFlickeringTimeout)); - _cfgList[0] = cfg; - - // if current id is 0, we need to apply the settings (forced) - if (_currentConfigId == 0) - { - selectConfig(0, true); - } - } -} - -void LinearSmoothing::Antiflickering() -{ - if (_antiFlickeringTreshold > 0 && _antiFlickeringStep > 0 && _previousValues.size() == _targetValues.size() && _previousValues.size() == _previousTimeouts.size()) - { - int64_t now = InternalClock::nowPrecise(); - - for (size_t i = 0; i < _previousValues.size(); ++i) - { - auto newColor = _targetValues[i]; - auto oldColor = _previousValues[i]; - int64_t& timeout = _previousTimeouts[i]; - - int avVal = (std::min(int(newColor.red), std::min(int(newColor.green), int(newColor.blue))) + - std::max(int(newColor.red), std::max(int(newColor.green), int(newColor.blue)))) / 2; - - if (avVal < _antiFlickeringTreshold) - { - int minR = std::abs(int(newColor.red) - int(oldColor.red)); - int minG = std::abs(int(newColor.green) - int(oldColor.green)); - int minB = std::abs(int(newColor.blue) - int(oldColor.blue)); - - int select = std::max(std::max(minR, minG), minB); - - if (select < _antiFlickeringStep && - (newColor.red != 0 || newColor.green != 0 || newColor.blue != 0) && - (oldColor.red != 0 || oldColor.green != 0 || oldColor.blue != 0)) - { - if (_antiFlickeringTimeout <= 0 || (now - timeout < _antiFlickeringTimeout && now > timeout)) - _targetValues[i] = _previousValues[i]; - else - timeout = now; - } - else - timeout = now; - } - else - timeout = now; - } - } -} - -void LinearSmoothing::updateLedValues(const std::vector& ledValues) -{ - if (!_enabled) - return; - - _coolDown = 1; - - if (_directMode) - { - if (_timer->remainingTime() >= 0) - clearQueuedColors(); - - if (_pause || ledValues.size() == 0) - return; - - queueColors(ledValues); - - return; - } - - try - { - if (_infoInput) - Info(_log, "Using %s smoothing input (%i)", ((_smoothingType == SmoothingType::Alternative) ? "alternative" : "linear"), _currentConfigId); - - _infoInput = false; - LinearSetup(ledValues); - } - catch (...) - { - Debug(_log, "Smoothing error detected"); - } - - return; -} - -void LinearSmoothing::LinearSetup(const std::vector& ledValues) -{ - _targetTime = InternalClock::nowPrecise() + _settlingTime; - _targetValues = ledValues; - - /////////////////////////////////////////////////////////////////!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - if ((!_previousValues.empty() && (_previousValues.size() != _targetValues.size())) || _previousTime > _targetTime) - { - Warning(_log, "Detect %s has changed. Previuos value: %d, new value: %d", (_previousTime > _targetTime) ? "TIME" : "size", _previousValues.size(), _targetValues.size()); - _previousValues.clear(); - _previousTimeouts.clear(); - } - - if (_previousValues.empty()) - { - _previousTime = InternalClock::nowPrecise(); - _previousValues = ledValues; - _previousTimeouts.clear(); - _previousTimeouts.resize(_previousValues.size(), _previousTime); - } - - Antiflickering(); -} - -inline uint8_t LinearSmoothing::computeColor(int64_t k, int color) -{ - int delta = std::abs(color); - auto step = std::min((int)std::max(static_cast((k * delta) >> 8), 1), delta); - - return step; -} - -void LinearSmoothing::setupAdvColor(int64_t deltaTime, float& kOrg, float& kMin, float& kMid, float& kAbove, float& kMax) -{ - kOrg = std::max(1.0f - 1.0f * deltaTime / (_targetTime - _previousTime), 0.0001f); - - kMin = std::min(std::pow(kOrg, 1.0f), 1.0f); - kMid = std::min(std::pow(kOrg, 0.9f), 1.0f); - kAbove = std::min(std::pow(kOrg, 0.75f), 1.0f); - kMax = std::min(std::pow(kOrg, 0.6f), 1.0f); -} - -inline uint8_t LinearSmoothing::computeAdvColor(int limitMin, int limitAverage, int limitMax, float kMin, float kMid, float kAbove, float kMax, int color) -{ - int val = std::abs(color); - if (val < limitMin) - return std::ceil(kMax * val); - else if (val < limitAverage) - return std::ceil(kAbove * val); - else if (val < limitMax) - return std::ceil(kMid * val); - else - return std::ceil(kMin * val); -} - -void LinearSmoothing::updateLeds() -{ - try - { - if (_smoothingType == SmoothingType::Alternative) - { - if (_infoUpdate) - Info(_log, "Using alternative smoothing procedure (%i)", _currentConfigId); - _infoUpdate = false; - - LinearSmoothingProcessing(true); - } - else - { - if (_infoUpdate) - Info(_log, "Using linear smoothing procedure (%i)", _currentConfigId); - _infoUpdate = false; - - LinearSmoothingProcessing(false); - } - } - catch (...) - { - Debug(_log, "Smoothing error detected"); - } -} - -void LinearSmoothing::queueColors(const std::vector& ledColors) -{ - if (!_pause) - { - emit _hyperhdr->ledDeviceData(ledColors); - } -} - -void LinearSmoothing::componentStateChange(hyperhdr::Components component, bool state) -{ - _flushFrame = state; - - if (component == hyperhdr::COMP_LEDDEVICE) - { - clearQueuedColors(state); - } - - if (component == hyperhdr::COMP_SMOOTHING) - { - setEnable(state); - } -} - -void LinearSmoothing::setEnable(bool enable) -{ - _enabled = enable; - - clearQueuedColors(_enabled); - - // update comp register - _hyperhdr->setNewComponentState(hyperhdr::COMP_SMOOTHING, enable); -} - -unsigned LinearSmoothing::addConfig(int settlingTime_ms, double ledUpdateFrequency_hz, bool directMode) -{ - SmoothingCfg cfg(false, settlingTime_ms, int64_t(1000.0 / ledUpdateFrequency_hz), directMode); - _cfgList.push_back(cfg); - - return (unsigned)(_cfgList.size() - 1); -} - -unsigned LinearSmoothing::updateConfig(unsigned cfgID, int settlingTime_ms, double ledUpdateFrequency_hz, bool directMode) -{ - unsigned updatedCfgID = cfgID; - if (cfgID < static_cast(_cfgList.size())) - { - SmoothingCfg cfg(false, settlingTime_ms, int64_t(1000.0 / ledUpdateFrequency_hz), directMode); - _cfgList[updatedCfgID] = cfg; - } - else - { - updatedCfgID = addConfig(settlingTime_ms, ledUpdateFrequency_hz, directMode); - } - - return updatedCfgID; -} - -void LinearSmoothing::updateCurrentConfig(int settlingTime_ms) -{ - _settlingTime = settlingTime_ms; - - Info(_log, "Updating current config (%d) => type: %s, dirMode: %s, pause: %s, settlingTime: %ims, interval: %ims (%iHz), antiFlickTres: %i, antiFlickStep: %i, antiFlickTime: %i", - _currentConfigId, QSTRING_CSTR(SmoothingCfg::EnumToString(_smoothingType)), (_directMode) ? "true" : "false", (_pause) ? "true" : "false", int(_settlingTime), - int(_updateInterval), int(1000.0 / _updateInterval), _antiFlickeringTreshold, _antiFlickeringStep, int(_antiFlickeringTimeout)); -} - -bool LinearSmoothing::selectConfig(unsigned cfg, bool force) -{ - //WATCH - if (_currentConfigId == cfg && !force) - { - return true; - } - - if (cfg < (unsigned)_cfgList.size()) - { - _settlingTime = _cfgList[cfg]._settlingTime; - _pause = _cfgList[cfg]._pause; - _directMode = _cfgList[cfg]._directMode; - _antiFlickeringTreshold = _cfgList[cfg]._antiFlickeringTreshold; - _antiFlickeringStep = _cfgList[cfg]._antiFlickeringStep; - _antiFlickeringTimeout = _cfgList[cfg]._antiFlickeringTimeout; - - int64_t newUpdateInterval = std::max(_cfgList[cfg]._updateInterval, (int64_t)5); - - if (newUpdateInterval != _updateInterval || _cfgList[cfg]._type != _smoothingType) - { - _updateInterval = newUpdateInterval; - _smoothingType = _cfgList[cfg]._type; - - clearQueuedColors(!_pause && _enabled, true); - } - - _currentConfigId = cfg; - - Info(_log, "Selecting config (%d) => type: %s, dirMode: %s, pause: %s, settlingTime: %ims, interval: %ims (%iHz), antiFlickTres: %i, antiFlickStep: %i, antiFlickTime: %i", - _currentConfigId, QSTRING_CSTR(SmoothingCfg::EnumToString(_smoothingType)), (_directMode) ? "true" : "false", (_pause) ? "true" : "false", int(_settlingTime), - int(_updateInterval), int(1000.0 / _updateInterval), _antiFlickeringTreshold, _antiFlickeringStep, int(_antiFlickeringTimeout)); - - return true; - } - - // reset to default - _currentConfigId = 0; - return false; -} - -bool LinearSmoothing::pause() const -{ - return _pause; -} - -bool LinearSmoothing::enabled() const -{ - return _enabled && !_pause; -} - -LinearSmoothing::SmoothingCfg::SmoothingCfg() : - _pause(false), - _settlingTime(200), - _updateInterval(25), - _directMode(false), - _type(SmoothingType::Linear), - _antiFlickeringTreshold(0), - _antiFlickeringStep(0), - _antiFlickeringTimeout(0) -{ -} - -LinearSmoothing::SmoothingCfg::SmoothingCfg(bool pause, int64_t settlingTime, int64_t updateInterval, bool directMode, SmoothingType type, int antiFlickeringTreshold, int antiFlickeringStep, int64_t antiFlickeringTimeout) : - _pause(pause), - _settlingTime(settlingTime), - _updateInterval(updateInterval), - _directMode(directMode), - _type(type), - _antiFlickeringTreshold(antiFlickeringTreshold), - _antiFlickeringStep(antiFlickeringStep), - _antiFlickeringTimeout(antiFlickeringTimeout) -{ -} - -QString LinearSmoothing::SmoothingCfg::EnumToString(SmoothingType type) -{ - if (type == SmoothingType::Linear) - return QString("Linear"); - else if (type == SmoothingType::Alternative) - return QString("Alternative"); - - return QString("Unknown"); -} - -void LinearSmoothing::LinearSmoothingProcessing(bool correction) -{ - float kOrg, kMin, kMid, kAbove, kMax; - int64_t now = InternalClock::nowPrecise(); - int64_t deltaTime = _targetTime - now; - int64_t k; - - int aspectLow = 16; - int aspectMid = 32; - int aspectHigh = 60; - - - if (deltaTime <= 0 || _targetTime <= _previousTime) - { - _previousValues = _targetValues; - _previousTimeouts.clear(); - _previousTimeouts.resize(_previousValues.size(), now); - - _previousTime = now; - - if (_flushFrame) - queueColors(_previousValues); - - if (!_continuousOutput && _coolDown > 0) - { - _coolDown--; - _flushFrame = true; - } - else - _flushFrame = _continuousOutput; - } - else - { - _flushFrame = true; - - if (correction) - setupAdvColor(deltaTime, kOrg, kMin, kMid, kAbove, kMax); - else - k = std::max((1 << 8) - (deltaTime << 8) / (_targetTime - _previousTime), static_cast(1)); - - int redDiff = 0, greenDiff = 0, blueDiff = 0; - - if (_previousValues.size() != _targetValues.size()) - { - Error(_log, "Detect abnormal state. Previuos value: %d, new value: %d", _previousValues.size(), _targetValues.size()); - } - else - { - for (size_t i = 0; i < _previousValues.size(); i++) - { - ColorRgb& prev = _previousValues[i]; - ColorRgb& target = _targetValues[i]; - - redDiff = target.red - prev.red; - greenDiff = target.green - prev.green; - blueDiff = target.blue - prev.blue; - - if (redDiff != 0) - prev.red += (redDiff < 0 ? -1 : 1) * - ((correction) ? computeAdvColor(aspectLow, aspectMid, aspectHigh, kMin, kMid, kAbove, kMax, redDiff) : computeColor(k, redDiff)); - if (greenDiff != 0) - prev.green += (greenDiff < 0 ? -1 : 1) * - ((correction) ? computeAdvColor(aspectLow, aspectMid, aspectHigh, kMin, kMid, kAbove, kMax, greenDiff) : computeColor(k, greenDiff)); - if (blueDiff != 0) - prev.blue += (blueDiff < 0 ? -1 : 1) * - ((correction) ? computeAdvColor(aspectLow, aspectMid, aspectHigh, kMin, kMid, kAbove, kMax, blueDiff) : computeColor(k, blueDiff)); - } - } - _previousTime = now; - - queueColors(_previousValues); - } -} - -void LinearSmoothing::DebugOutput() -{ - /*_debugCounter = std::min(_debugCounter+1,900); - if (_targetValues.size() > 0 && _debugCounter < 900) - { - if (_debugCounter < 100 || (_debugCounter > 200 && _debugCounter < 300) || (_debugCounter > 400 && _debugCounter < 500) || (_debugCounter > 600 && _debugCounter < 700) || _debugCounter>800) - { - _targetValues[0].red = 0; - _targetValues[0].green = 0; - _targetValues[0].blue = 0; - } - else if (_debugCounter >= 100 && _debugCounter <= 200) - { - _targetValues[0].red = 255; - _targetValues[0].green = 255; - _targetValues[0].blue = 255; - } - else if (_debugCounter >= 300 && _debugCounter <= 400) - { - _targetValues[0].red = 255; - _targetValues[0].green = 0; - _targetValues[0].blue = 0; - } - else if (_debugCounter >= 500 && _debugCounter <= 600) - { - _targetValues[0].red = 0; - _targetValues[0].green = 255; - _targetValues[0].blue = 0; - } - else if (_debugCounter >= 700 && _debugCounter <= 800) - { - _targetValues[0].red = 0; - _targetValues[0].green = 0; - _targetValues[0].blue = 255; - } - }*/ -} - - diff --git a/sources/base/MessageForwarder.cpp b/sources/base/MessageForwarder.cpp deleted file mode 100644 index c327b82b3..000000000 --- a/sources/base/MessageForwarder.cpp +++ /dev/null @@ -1,378 +0,0 @@ -// STL includes -#include - -// project includes -#include - - -#include - -// utils includes -#include - -// qt includes -#include -#include -#include - -#include - -MessageForwarder::MessageForwarder(HyperHdrInstance* hyperhdr) - : QObject() - , _hyperhdr(hyperhdr) - , _log(Logger::getInstance("NETFORWARDER")) - , _muxer(_hyperhdr->getMuxerInstance()) - , _forwarder_enabled(true) - , _priority(140) - , _messageForwarderHelper(nullptr) -{ - // get settings updates - connect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &MessageForwarder::handleSettingsUpdate); - - // component changes - connect(_hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &MessageForwarder::handleCompStateChangeRequest); - - // connect with Muxer visible priority changes - connect(_muxer, &PriorityMuxer::visiblePriorityChanged, this, &MessageForwarder::handlePriorityChanges); - - // init - handleSettingsUpdate(settings::type::NETFORWARD, _hyperhdr->getSetting(settings::type::NETFORWARD)); -} - -MessageForwarder::~MessageForwarder() -{ - disconnect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, 0, 0); - disconnect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, 0, 0); - - if (_messageForwarderHelper != nullptr) - { - delete _messageForwarderHelper; - _messageForwarderHelper = nullptr; - } -} - -MessageForwarderHelper::MessageForwarderHelper() -{ - _free = true; - - QThread* mainThread = new QThread(); - mainThread->setObjectName("ForwarderHelperThread"); - this->moveToThread(mainThread); - mainThread->start(); - - connect(this, &MessageForwarderHelper::addClient, this, &MessageForwarderHelper::addClientHandler); - connect(this, &MessageForwarderHelper::clearClients, this, &MessageForwarderHelper::clearClientsHandler); -} - -MessageForwarderHelper::~MessageForwarderHelper() -{ - while (!_forwardClients.isEmpty()) - _forwardClients.takeFirst()->deleteLater(); - - QThread* oldThread = this->thread(); - disconnect(oldThread, nullptr, nullptr, nullptr); - oldThread->quit(); - oldThread->wait(); - delete oldThread; -} - -void MessageForwarderHelper::addClientHandler(const QString& origin, const QString& address, int priority, bool skipReply) -{ - FlatBufferConnection* flatbuf = new FlatBufferConnection("Forwarder", address, priority, false); - _forwardClients << flatbuf; -} - -void MessageForwarderHelper::clearClientsHandler() -{ - while (!_forwardClients.isEmpty()) - delete _forwardClients.takeFirst(); -} - -void MessageForwarder::handleSettingsUpdate(settings::type type, const QJsonDocument& config) -{ - if (type == settings::type::NETFORWARD) - { - // clear the current targets - _jsonSlaves.clear(); - _flatSlaves.clear(); - - // build new one - const QJsonObject& obj = config.object(); - - if (_messageForwarderHelper == nullptr) - { - if (obj["enable"].toBool()) - _messageForwarderHelper = new MessageForwarderHelper(); - } - else - emit _messageForwarderHelper->clearClients(); - - if (!obj["json"].isNull()) - { - const QJsonArray& addr = obj["json"].toArray(); - for (auto&& entry : addr) - { - addJsonSlave(entry.toString()); - } - } - - if (!obj["flat"].isNull()) - { - const QJsonArray& addr = obj["flat"].toArray(); - for (auto&& entry : addr) - { - addFlatbufferSlave(entry.toString()); - } - } - - if (!_jsonSlaves.isEmpty() && obj["enable"].toBool() && _forwarder_enabled) - { - InfoIf(obj["enable"].toBool(true), _log, "Forward now to json targets '%s'", QSTRING_CSTR(_jsonSlaves.join(", "))); - connect(_hyperhdr, &HyperHdrInstance::forwardJsonMessage, this, &MessageForwarder::forwardJsonMessage, Qt::UniqueConnection); - } - else if (_jsonSlaves.isEmpty() || !obj["enable"].toBool() || !_forwarder_enabled) - disconnect(_hyperhdr, &HyperHdrInstance::forwardJsonMessage, 0, 0); - - if (!_flatSlaves.isEmpty() && obj["enable"].toBool() && _forwarder_enabled) - { - InfoIf(obj["enable"].toBool(true), _log, "Forward now to flatbuffer targets '%s'", QSTRING_CSTR(_flatSlaves.join(", "))); - - hyperhdr::Components activeCompId = _hyperhdr->getPriorityInfo(_hyperhdr->getCurrentPriority()).componentId; - - disconnect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, 0, 0); - disconnect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, 0, 0); - - if (activeCompId == hyperhdr::COMP_SYSTEMGRABBER) - { - connect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); - } - else if (activeCompId == hyperhdr::COMP_VIDEOGRABBER) - { - connect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); - } - } - else if (_flatSlaves.isEmpty() || !obj["enable"].toBool() || !_forwarder_enabled) - { - disconnect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, 0, 0); - disconnect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, 0, 0); - } - - // update comp state - _hyperhdr->setNewComponentState(hyperhdr::COMP_FORWARDER, obj["enable"].toBool(true)); - } -} - -void MessageForwarder::handleCompStateChangeRequest(hyperhdr::Components component, bool enable) -{ - if (component == hyperhdr::COMP_FORWARDER && _forwarder_enabled != enable) - { - _forwarder_enabled = enable; - handleSettingsUpdate(settings::type::NETFORWARD, _hyperhdr->getSetting(settings::type::NETFORWARD)); - Info(_log, "Forwarder change state to %s", (_forwarder_enabled ? "enabled" : "disabled")); - _hyperhdr->setNewComponentState(component, _forwarder_enabled); - } -} - -void MessageForwarder::handlePriorityChanges(quint8 priority) -{ - const QJsonObject obj = _hyperhdr->getSetting(settings::type::NETFORWARD).object(); - if (priority != 0 && _forwarder_enabled && obj["enable"].toBool()) - { - //_flatSlaves.clear(); - //while (!_forwardClients.isEmpty()) - // delete _forwardClients.takeFirst(); - - hyperhdr::Components activeCompId = _hyperhdr->getPriorityInfo(priority).componentId; - if (activeCompId == hyperhdr::COMP_SYSTEMGRABBER || activeCompId == hyperhdr::COMP_VIDEOGRABBER) - { - if (!obj["flat"].isNull()) - { - const QJsonArray& addr = obj["flat"].toArray(); - for (auto&& entry : addr) - { - addFlatbufferSlave(entry.toString()); - } - } - - switch (activeCompId) - { - case hyperhdr::COMP_SYSTEMGRABBER: - { - disconnect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, 0, 0); - connect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); - } - break; - case hyperhdr::COMP_VIDEOGRABBER: - { - disconnect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, 0, 0); - connect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); - } - break; - default: - { - disconnect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, 0, 0); - disconnect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, 0, 0); - } - } - } - else - { - disconnect(_hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage, 0, 0); - disconnect(_hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage, 0, 0); - } - } -} - -void MessageForwarder::addJsonSlave(const QString& slave) -{ - QStringList parts = slave.split(":"); - if (parts.size() != 2) - { - Error(_log, "Unable to parse address (%s)", QSTRING_CSTR(slave)); - return; - } - - bool ok; - parts[1].toUShort(&ok); - if (!ok) - { - Error(_log, "Unable to parse port number (%s)", QSTRING_CSTR(parts[1])); - return; - } - - // verify loop with jsonserver - const QJsonObject& obj = _hyperhdr->getSetting(settings::type::JSONSERVER).object(); - if (QHostAddress(parts[0]) == QHostAddress::LocalHost && parts[1].toInt() == obj["port"].toInt()) - { - Error(_log, "Loop between JsonServer and Forwarder! (%s)", QSTRING_CSTR(slave)); - return; - } - - if (_forwarder_enabled && !_jsonSlaves.contains(slave)) - _jsonSlaves << slave; -} - -void MessageForwarder::addFlatbufferSlave(const QString& slave) -{ - if (slave != HYPERHDR_DOMAIN_SERVER) - { - QStringList parts = slave.split(":"); - if (parts.size() != 2) - { - Error(_log, "Unable to parse address (%s)", QSTRING_CSTR(slave)); - return; - } - - bool ok; - parts[1].toUShort(&ok); - if (!ok) - { - Error(_log, "Unable to parse port number (%s)", QSTRING_CSTR(parts[1])); - return; - } - - // verify loop with flatbufserver - const QJsonObject& obj = _hyperhdr->getSetting(settings::type::FLATBUFSERVER).object(); - if (QHostAddress(parts[0]) == QHostAddress::LocalHost && parts[1].toInt() == obj["port"].toInt()) - { - Error(_log, "Loop between Flatbuffer Server and Forwarder! (%s)", QSTRING_CSTR(slave)); - return; - } - } - - if (_forwarder_enabled && !_flatSlaves.contains(slave)) - { - _flatSlaves << slave; - if (_messageForwarderHelper != nullptr) - emit _messageForwarderHelper->addClient("Forwarder", slave.toLocal8Bit().constData(), _priority, false); - } -} - -void MessageForwarder::forwardJsonMessage(const QJsonObject& message) -{ - if (_forwarder_enabled) - { - QTcpSocket client; - for (int i = 0; i < _jsonSlaves.size(); i++) - { - QStringList parts = _jsonSlaves.at(i).split(":"); - client.connectToHost(QHostAddress(parts[0]), parts[1].toUShort()); - if (client.waitForConnected(500)) - { - sendJsonMessage(message, &client); - client.close(); - } - } - } -} - -void MessageForwarder::forwardFlatbufferMessage(const QString& name, const Image& image) -{ - if (_messageForwarderHelper != nullptr) - { - bool isfree = _messageForwarderHelper->isFree(); - - if (isfree && _forwarder_enabled) - QUEUE_CALL_1(_messageForwarderHelper, forwardImage, Image, image); - } -} - -bool MessageForwarderHelper::isFree() -{ - return _free; -} - -void MessageForwarderHelper::forwardImage(const Image& image) -{ - _free = false; - - for (int i = 0; i < _forwardClients.size(); i++) - { - _forwardClients.at(i)->setImage(image); - } - - _free = true; -} - -void MessageForwarder::sendJsonMessage(const QJsonObject& message, QTcpSocket* socket) -{ - QJsonObject jsonMessage = message; - if (jsonMessage.contains("tan") && jsonMessage["tan"].isNull()) - jsonMessage["tan"] = 100; - - // serialize message - QJsonDocument writer(jsonMessage); - QByteArray serializedMessage = writer.toJson(QJsonDocument::Compact) + "\n"; - - // write message - socket->write(serializedMessage); - if (!socket->waitForBytesWritten()) - { - Debug(_log, "Error while writing data to host"); - return; - } - - // read reply data - QByteArray serializedReply; - while (!serializedReply.contains('\n')) - { - // receive reply - if (!socket->waitForReadyRead()) - { - Debug(_log, "Error while writing data from host"); - return; - } - - serializedReply += socket->readAll(); - } - - // parse reply data - QJsonParseError error; - QJsonDocument reply = QJsonDocument::fromJson(serializedReply, &error); - - if (error.error != QJsonParseError::NoError) - { - Error(_log, "Error while parsing reply: invalid json"); - return; - } -} diff --git a/sources/base/MultiColorAdjustment.cpp b/sources/base/MultiColorAdjustment.cpp deleted file mode 100644 index bcd8147ff..000000000 --- a/sources/base/MultiColorAdjustment.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include -#include -#include -#include -#include -#include - -MultiColorAdjustment::MultiColorAdjustment(quint8 instance, int ledCnt) - : _ledAdjustments(ledCnt, nullptr) - , _log(Logger::getInstance("ADJUSTMENT" + QString::number(instance))) -{ -} - -MultiColorAdjustment::~MultiColorAdjustment() -{ - // Clean up all the transforms - for (ColorAdjustment* adjustment : _adjustment) - { - delete adjustment; - // BUG: Calling pop_back while iterating is invalid - _adjustment.pop_back(); - } -} - -void MultiColorAdjustment::addAdjustment(ColorAdjustment* adjustment) -{ - _adjustmentIds.push_back(adjustment->_id); - _adjustment.push_back(adjustment); -} - -void MultiColorAdjustment::setAdjustmentForLed(const QString& id, int startLed, int endLed) -{ - // abort - if (startLed > endLed) - { - Error(_log, "startLed > endLed -> %d > %d", startLed, endLed); - return; - } - // catch wrong values - if (endLed > static_cast(_ledAdjustments.size() - 1)) - { - Warning(_log, "The color calibration 'LED index' field has LEDs specified which aren't part of your led layout"); - endLed = static_cast(_ledAdjustments.size() - 1); - } - - // Get the identified adjustment (don't care if is nullptr) - ColorAdjustment* adjustment = getAdjustment(id); - - //Debug(_log,"ColorAdjustment Profile [%s], startLed[%d], endLed[%d]", QSTRING_CSTR(id), startLed, endLed); - for (int iLed = startLed; iLed <= endLed; ++iLed) - { - //Debug(_log,"_ledAdjustments [%d] -> [%p]", iLed, adjustment); - _ledAdjustments[iLed] = adjustment; - } -} - -bool MultiColorAdjustment::verifyAdjustments() const -{ - bool ok = true; - for (unsigned iLed = 0; iLed < _ledAdjustments.size(); ++iLed) - { - ColorAdjustment* adjustment = _ledAdjustments[iLed]; - - if (adjustment == nullptr) - { - Warning(_log, "No calibration set for led %d", iLed); - ok = false; - } - } - return ok; -} - -QStringList MultiColorAdjustment::getAdjustmentIds() const -{ - return _adjustmentIds; -} - -ColorAdjustment* MultiColorAdjustment::getAdjustment(const QString& id) -{ - // Iterate through the unique adjustments until we find the one with the given id - for (ColorAdjustment* adjustment : _adjustment) - { - if (adjustment->_id == id) - { - return adjustment; - } - } - - // The ColorAdjustment was not found - return nullptr; -} - -void MultiColorAdjustment::setBacklightEnabled(bool enable) -{ - for (ColorAdjustment* adjustment : _adjustment) - { - adjustment->_rgbTransform.setBackLightEnabled(enable); - } -} - -void MultiColorAdjustment::applyAdjustment(std::vector& ledColors) -{ - const size_t itCnt = qMin(_ledAdjustments.size(), ledColors.size()); - for (size_t i = 0; i < itCnt; ++i) - { - ColorAdjustment* adjustment = _ledAdjustments[i]; - if (adjustment == nullptr) - { - //std::cout << "MultiColorAdjustment::applyAdjustment() - No transform set for this led : " << i << std::endl; - // No transform set for this led (do nothing) - continue; - } - ColorRgb& color = ledColors[i]; - - if (adjustment->_rgbTransform._classic_config) - { - uint8_t ored = color.red; - uint8_t ogreen = color.green; - uint8_t oblue = color.blue; - - adjustment->_rgbTransform.transformSatLum(ored, ogreen, oblue); - adjustment->_rgbTransform.transform(ored, ogreen, oblue); - - color.red = ored; - color.green = ogreen; - color.blue = oblue; - - int RR = adjustment->_rgbRedAdjustment.adjustmentR(color.red); - int RG = color.red > color.green ? adjustment->_rgbRedAdjustment.adjustmentG(color.red - color.green) : 0; - int RB = color.red > color.blue ? adjustment->_rgbRedAdjustment.adjustmentB(color.red - color.blue) : 0; - - int GR = color.green > color.red ? adjustment->_rgbGreenAdjustment.adjustmentR(color.green - color.red) : 0; - int GG = adjustment->_rgbGreenAdjustment.adjustmentG(color.green); - int GB = color.green > color.blue ? adjustment->_rgbGreenAdjustment.adjustmentB(color.green - color.blue) : 0; - - int BR = color.blue > color.red ? adjustment->_rgbBlueAdjustment.adjustmentR(color.blue - color.red) : 0; - int BG = color.blue > color.green ? adjustment->_rgbBlueAdjustment.adjustmentG(color.blue - color.green) : 0; - int BB = adjustment->_rgbBlueAdjustment.adjustmentB(color.blue); - - int ledR = RR + GR + BR; - int maxR = (int)adjustment->_rgbRedAdjustment.getAdjustmentR(); - int ledG = RG + GG + BG; - int maxG = (int)adjustment->_rgbGreenAdjustment.getAdjustmentG(); - int ledB = RB + GB + BB; - int maxB = (int)adjustment->_rgbBlueAdjustment.getAdjustmentB(); - - if (ledR > maxR) - color.red = (uint8_t)maxR; - else - color.red = (uint8_t)ledR; - - if (ledG > maxG) - color.green = (uint8_t)maxG; - else - color.green = (uint8_t)ledG; - - if (ledB > maxB) - color.blue = (uint8_t)maxB; - else - color.blue = (uint8_t)ledB; - - // temperature - color.red = adjustment->_rgbRedAdjustment.correction(color.red); - color.green = adjustment->_rgbGreenAdjustment.correction(color.green); - color.blue = adjustment->_rgbBlueAdjustment.correction(color.blue); - } - else - { - uint8_t ored = color.red; - uint8_t ogreen = color.green; - uint8_t oblue = color.blue; - uint8_t B_RGB = 0, B_CMY = 0, B_W = 0; - - - adjustment->_rgbTransform.transform(ored, ogreen, oblue); - adjustment->_rgbTransform.getBrightnessComponents(B_RGB, B_CMY, B_W); - - if (!adjustment->_rgbBlackAdjustment.isEnabled() && - !adjustment->_rgbRedAdjustment.isEnabled() && - !adjustment->_rgbGreenAdjustment.isEnabled() && - !adjustment->_rgbBlueAdjustment.isEnabled() && - !adjustment->_rgbCyanAdjustment.isEnabled() && - !adjustment->_rgbMagentaAdjustment.isEnabled() && - !adjustment->_rgbYellowAdjustment.isEnabled() && - !adjustment->_rgbWhiteAdjustment.isEnabled()) - { - color.red = ored; - color.green = ogreen; - color.blue = oblue; - if (B_RGB != 255) - { - color.red = ((uint32_t)color.red * B_RGB)/255; - color.green = ((uint32_t)color.green * B_RGB) / 255; - color.blue = ((uint32_t)color.blue * B_RGB) / 255; - } - continue; - } - - uint32_t nrng = (uint32_t)(255 - ored) * (255 - ogreen); - uint32_t rng = (uint32_t)(ored) * (255 - ogreen); - uint32_t nrg = (uint32_t)(255 - ored) * (ogreen); - uint32_t rg = (uint32_t)(ored) * (ogreen); - - uint8_t black = nrng * (255 - oblue) / 65025; - uint8_t red = rng * (255 - oblue) / 65025; - uint8_t green = nrg * (255 - oblue) / 65025; - uint8_t blue = nrng * (oblue) / 65025; - uint8_t cyan = nrg * (oblue) / 65025; - uint8_t magenta = rng * (oblue) / 65025; - uint8_t yellow = rg * (255 - oblue) / 65025; - uint8_t white = rg * (oblue) / 65025; - - uint8_t OR, OG, OB, RR, RG, RB, GR, GG, GB, BR, BG, BB; - uint8_t CR, CG, CB, MR, MG, MB, YR, YG, YB, WR, WG, WB; - - adjustment->_rgbBlackAdjustment.apply(black, 255, OR, OG, OB); - adjustment->_rgbRedAdjustment.apply(red, B_RGB, RR, RG, RB); - adjustment->_rgbGreenAdjustment.apply(green, B_RGB, GR, GG, GB); - adjustment->_rgbBlueAdjustment.apply(blue, B_RGB, BR, BG, BB); - adjustment->_rgbCyanAdjustment.apply(cyan, B_CMY, CR, CG, CB); - adjustment->_rgbMagentaAdjustment.apply(magenta, B_CMY, MR, MG, MB); - adjustment->_rgbYellowAdjustment.apply(yellow, B_CMY, YR, YG, YB); - adjustment->_rgbWhiteAdjustment.apply(white, B_W, WR, WG, WB); - - color.red = OR + RR + GR + BR + CR + MR + YR + WR; - color.green = OG + RG + GG + BG + CG + MG + YG + WG; - color.blue = OB + RB + GB + BB + CB + MB + YB + WB; - } - } -} - -MultiColorAdjustment* MultiColorAdjustment::createLedColorsAdjustment(quint8 instance, int ledCnt, const QJsonObject& colorConfig) -{ - - // Create the result, the transforms are added to this - MultiColorAdjustment* adjustment = new MultiColorAdjustment(instance, ledCnt); - - const QJsonValue adjustmentConfig = colorConfig["channelAdjustment"]; - const QRegularExpression overallExp("^([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*$"); - - const QJsonArray& adjustmentConfigArray = adjustmentConfig.toArray(); - for (signed i = 0; i < adjustmentConfigArray.size(); ++i) - { - const QJsonObject& config = adjustmentConfigArray.at(i).toObject(); - ColorAdjustment* colorAdjustment = ColorAdjustment::createColorAdjustment(instance, config); - adjustment->addAdjustment(colorAdjustment); - - const QString ledIndicesStr = config["leds"].toString("").trimmed(); - if (ledIndicesStr.compare("*") == 0) - { - // Special case for indices '*' => all leds - adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt - 1); - continue; - } - - if (!overallExp.match(ledIndicesStr).hasMatch()) - { - continue; - } - - std::stringstream ss; - const QStringList ledIndexList = ledIndicesStr.split(","); - for (int i = 0; i < ledIndexList.size(); ++i) { - if (i > 0) - { - ss << ", "; - } - if (ledIndexList[i].contains("-")) - { - QStringList ledIndices = ledIndexList[i].split("-"); - int startInd = ledIndices[0].toInt(); - int endInd = ledIndices[1].toInt(); - - adjustment->setAdjustmentForLed(colorAdjustment->_id, startInd, endInd); - ss << startInd << "-" << endInd; - } - else - { - int index = ledIndexList[i].toInt(); - adjustment->setAdjustmentForLed(colorAdjustment->_id, index, index); - ss << index; - } - } - } - - return adjustment; -} diff --git a/sources/base/PriorityMuxer.cpp b/sources/base/Muxer.cpp similarity index 54% rename from sources/base/PriorityMuxer.cpp rename to sources/base/Muxer.cpp index ac1210572..127c4a102 100644 --- a/sources/base/PriorityMuxer.cpp +++ b/sources/base/Muxer.cpp @@ -1,25 +1,25 @@ -// STL includes -#include -#include +#ifndef PCH_ENABLED + #include + #include -// qt incl -#include -#include + #include + #include -#include + #include +#endif -// utils -#include +#include -const int PriorityMuxer::LOWEST_PRIORITY = std::numeric_limits::max(); +#define TIMEOUT_INACTIVE -100 -const int PriorityMuxer::HIGHEST_EFFECT_PRIORITY = 0; -const int PriorityMuxer::LOWEST_EFFECT_PRIORITY = 254; +const int Muxer::LOWEST_PRIORITY = std::numeric_limits::max(); +const int Muxer::HIGHEST_EFFECT_PRIORITY = 0; +const int Muxer::LOWEST_EFFECT_PRIORITY = 254; -PriorityMuxer::PriorityMuxer(int instanceIndex, int ledCount, QObject* parent) +Muxer::Muxer(int instanceIndex, int ledCount, QObject* parent) : QObject(parent) , _log(Logger::getInstance(QString("MUXER%1").arg(instanceIndex))) - , _currentPriority(PriorityMuxer::LOWEST_PRIORITY) + , _currentPriority(Muxer::LOWEST_PRIORITY) , _previousPriority(_currentPriority) , _manualSelectedPriority(256) , _activeInputs() @@ -30,43 +30,48 @@ PriorityMuxer::PriorityMuxer(int instanceIndex, int ledCount, QObject* parent) , _blockTimer(new QTimer(this)) { // init lowest priority info - _lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY; - _lowestPriorityInfo.timeoutTime_ms = -1; - _lowestPriorityInfo.ledColors = std::vector(ledCount, { 0, 0, 0 }); + _lowestPriorityInfo.priority = Muxer::LOWEST_PRIORITY; + _lowestPriorityInfo.timeout = -1; + _lowestPriorityInfo.staticColor = ColorRgb::BLACK; _lowestPriorityInfo.componentId = hyperhdr::COMP_COLOR; _lowestPriorityInfo.origin = "System"; _lowestPriorityInfo.owner = ""; - _activeInputs[PriorityMuxer::LOWEST_PRIORITY] = _lowestPriorityInfo; + _activeInputs[Muxer::LOWEST_PRIORITY] = _lowestPriorityInfo; // adapt to 1s interval for COLOR and EFFECT timeouts > -1 - connect(_timer, &QTimer::timeout, this, &PriorityMuxer::timeTrigger); + connect(_timer, &QTimer::timeout, this, &Muxer::timeTrigger); _timer->setSingleShot(true); _blockTimer->setSingleShot(true); // forward timeRunner signal to prioritiesChanged signal & threading workaround - connect(this, &PriorityMuxer::timeRunner, this, &PriorityMuxer::prioritiesChanged); - connect(this, &PriorityMuxer::signalTimeTrigger, this, &PriorityMuxer::timeTrigger); + connect(this, &Muxer::SignalTimeRunner_Internal, this, &Muxer::SignalPrioritiesChanged); + connect(this, &Muxer::SignalTimeTrigger_Internal, this, &Muxer::timeTrigger); // start muxer timer - connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::setCurrentTime); + connect(_updateTimer, &QTimer::timeout, this, &Muxer::setCurrentTime); _updateTimer->setInterval(250); _updateTimer->start(); + + Debug(_log, "Muxer initialized"); } -PriorityMuxer::~PriorityMuxer() +Muxer::~Muxer() { + delete _updateTimer; + delete _timer; + delete _blockTimer; } -void PriorityMuxer::setEnable(bool enable) +void Muxer::setEnable(bool enable) { enable ? _updateTimer->start() : _updateTimer->stop(); } -bool PriorityMuxer::setInputImage(int priority, const Image& image, int64_t timeout_ms) +bool Muxer::setInput(int priority, int64_t timeout_ms) { if (!_activeInputs.contains(priority)) { - Error(_log, "setInputImage() used without registerInput() for priority '%d', probably the priority reached timeout", priority); + Error(_log, "setInput() used without registerInput() for priority '%d', probably the priority reached timeout", priority); return false; } @@ -78,34 +83,32 @@ bool PriorityMuxer::setInputImage(int priority, const Image& image, in // detect active <-> inactive changes bool activeChange = false, active = true; - if (input.timeoutTime_ms == -100 && timeout_ms != -100) + if (input.timeout == TIMEOUT_INACTIVE && timeout_ms != TIMEOUT_INACTIVE) { activeChange = true; } - else if (timeout_ms == -100 && input.timeoutTime_ms != -100) + else if (timeout_ms == TIMEOUT_INACTIVE && input.timeout != TIMEOUT_INACTIVE) { active = false; activeChange = true; } // update input - input.timeoutTime_ms = timeout_ms; - input.image = image; - input.ledColors.clear(); + input.timeout = timeout_ms; // emit active change if (activeChange) { Info(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); if (_currentPriority < priority) - emit prioritiesChanged(); + emit SignalPrioritiesChanged(); setCurrentTime(); } return true; } -bool PriorityMuxer::setSourceAutoSelectEnabled(bool enable, bool update) +bool Muxer::setSourceAutoSelectEnabled(bool enable, bool update) { if (_sourceAutoSelectEnabled != enable) { @@ -128,7 +131,7 @@ bool PriorityMuxer::setSourceAutoSelectEnabled(bool enable, bool update) return false; } -bool PriorityMuxer::setPriority(int priority) +bool Muxer::setPriority(int priority) { if (_activeInputs.contains(priority)) { @@ -140,34 +143,22 @@ bool PriorityMuxer::setPriority(int priority) return false; } -void PriorityMuxer::updateLedColorsLength(int ledCount) -{ - for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();) - { - if (infoIt->ledColors.size() >= 1) - { - infoIt->ledColors.resize(ledCount, infoIt->ledColors.at(0)); - } - ++infoIt; - } -} - -QList PriorityMuxer::getPriorities() const +QList Muxer::getPriorities() const { return _activeInputs.keys(); } -bool PriorityMuxer::hasPriority(int priority) const +bool Muxer::hasPriority(int priority) const { - return (priority == PriorityMuxer::LOWEST_PRIORITY) ? true : _activeInputs.contains(priority); + return (priority == Muxer::LOWEST_PRIORITY) ? true : _activeInputs.contains(priority); } -const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(int priority) const +const Muxer::InputInfo& Muxer::getInputInfo(int priority) const { auto elemIt = _activeInputs.find(priority); if (elemIt == _activeInputs.end()) { - elemIt = _activeInputs.find(PriorityMuxer::LOWEST_PRIORITY); + elemIt = _activeInputs.find(Muxer::LOWEST_PRIORITY); if (elemIt == _activeInputs.end()) { // fallback @@ -177,72 +168,17 @@ const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(int priority) const return elemIt.value(); } -const QMap& PriorityMuxer::getInputInfoTable() const +const QMap& Muxer::getInputInfoTable() const { return _activeInputs; } -hyperhdr::Components PriorityMuxer::getComponentOfPriority(int priority) const +hyperhdr::Components Muxer::getComponentOfPriority(int priority) const { return _activeInputs[priority].componentId; } -void PriorityMuxer::updateLedsValues(int priority, const std::vector& ledColors) -{ - if (!_activeInputs.contains(priority)) - { - return; - } - - _activeInputs[priority].ledColors = ledColors; -} - -bool PriorityMuxer::setInput(int priority, const std::vector& ledColors, int64_t timeout_ms) -{ - if (!_activeInputs.contains(priority)) - { - Error(_log, "setInput() used without registerInput() for priority '%d', probably the priority reached timeout", priority); - return false; - } - - // calc final timeout - if (timeout_ms > 0) - timeout_ms = InternalClock::now() + timeout_ms; - - InputInfo& input = _activeInputs[priority]; - - // detect active <-> inactive changes - bool activeChange = false, active = true; - if (input.timeoutTime_ms == -100 && timeout_ms != -100) - { - activeChange = true; - } - else if (timeout_ms == -100 && input.timeoutTime_ms != -100) - { - active = false; - activeChange = true; - } - - // update input - input.timeoutTime_ms = timeout_ms; - input.ledColors = ledColors; - input.image = Image(); - - // emit active change - if (activeChange) - { - Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); - if (_currentPriority < priority) - { - emit prioritiesChanged(); - } - setCurrentTime(); - } - - return true; -} - -void PriorityMuxer::registerInput(int priority, hyperhdr::Components component, const QString& origin, const QString& owner, unsigned smooth_cfg) +void Muxer::registerInput(int priority, hyperhdr::Components component, const QString& origin, const ColorRgb& staticColor, unsigned smooth_cfg, const QString& owner) { // detect new registers bool newInput = false; @@ -254,36 +190,36 @@ void PriorityMuxer::registerInput(int priority, hyperhdr::Components component, InputInfo& input = _activeInputs[priority]; input.priority = priority; - input.timeoutTime_ms = newInput ? -100 : input.timeoutTime_ms; + input.timeout = newInput ? TIMEOUT_INACTIVE : input.timeout; input.componentId = component; input.origin = origin; input.smooth_cfg = smooth_cfg; input.owner = owner; + input.staticColor = staticColor; if (newInput) { Info(_log, "Register new input '%s/%s' with priority %d as inactive", QSTRING_CSTR(origin), hyperhdr::componentToIdString(component), priority); // emit 'prioritiesChanged' only if _sourceAutoSelectEnabled is false if (!_sourceAutoSelectEnabled) - emit prioritiesChanged(); + emit SignalPrioritiesChanged(); return; } if (reusedInput && component!= hyperhdr::Components::COMP_FLATBUFSERVER) { - emit timeRunner(); + emit SignalTimeRunner_Internal(); } } -bool PriorityMuxer::setInputInactive(int priority) +bool Muxer::setInputInactive(int priority) { - Image image; - return setInputImage(priority, image, -100); + return setInput(priority, TIMEOUT_INACTIVE); } -bool PriorityMuxer::clearInput(int priority) +bool Muxer::clearInput(int priority) { - if (priority < PriorityMuxer::LOWEST_PRIORITY) + if (priority < Muxer::LOWEST_PRIORITY) { if (_activeInputs.remove(priority)) { @@ -292,19 +228,19 @@ bool PriorityMuxer::clearInput(int priority) setCurrentTime(); } if (!_sourceAutoSelectEnabled || _currentPriority < priority) - emit prioritiesChanged(); + emit SignalPrioritiesChanged(); return true; } return false; } -void PriorityMuxer::clearAll(bool forceClearAll) +void Muxer::clearAll(bool forceClearAll) { if (forceClearAll) { _previousPriority = _currentPriority; _activeInputs.clear(); - _currentPriority = PriorityMuxer::LOWEST_PRIORITY; + _currentPriority = Muxer::LOWEST_PRIORITY; _activeInputs[_currentPriority] = _lowestPriorityInfo; } else @@ -312,7 +248,7 @@ void PriorityMuxer::clearAll(bool forceClearAll) for (auto key : _activeInputs.keys()) { const InputInfo& info = getInputInfo(key); - if ((info.componentId == hyperhdr::COMP_COLOR || info.componentId == hyperhdr::COMP_EFFECT || info.componentId == hyperhdr::COMP_IMAGE) && key < PriorityMuxer::LOWEST_PRIORITY - 1) + if ((info.componentId == hyperhdr::COMP_COLOR || info.componentId == hyperhdr::COMP_EFFECT || info.componentId == hyperhdr::COMP_IMAGE) && key < Muxer::LOWEST_PRIORITY - 1) { clearInput(key); } @@ -320,32 +256,31 @@ void PriorityMuxer::clearAll(bool forceClearAll) } } -void PriorityMuxer::setCurrentTime() +void Muxer::setCurrentTime() { const int64_t now = InternalClock::now(); - int newPriority; - _activeInputs.contains(0) ? newPriority = 0 : newPriority = PriorityMuxer::LOWEST_PRIORITY; + int newPriority = Muxer::LOWEST_PRIORITY; for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();) { - if (infoIt->timeoutTime_ms > 0 && infoIt->timeoutTime_ms <= now) + if (infoIt->timeout > 0 && infoIt->timeout <= now) { int tPrio = infoIt->priority; infoIt = _activeInputs.erase(infoIt); Info(_log, "Timeout clear for priority %d", tPrio); - emit prioritiesChanged(); + emit SignalPrioritiesChanged(); } else { // timeoutTime of -100 is awaiting data (inactive); skip - if (infoIt->timeoutTime_ms > -100) + if (infoIt->timeout > TIMEOUT_INACTIVE) newPriority = qMin(newPriority, infoIt->priority); // call timeTrigger when effect or color is running with timeout > 0, blacklist prio 255 - if (infoIt->priority < PriorityMuxer::LOWEST_EFFECT_PRIORITY && - infoIt->timeoutTime_ms > 0 && + if (infoIt->priority < Muxer::LOWEST_EFFECT_PRIORITY && + infoIt->timeout > 0 && (infoIt->componentId == hyperhdr::COMP_EFFECT || infoIt->componentId == hyperhdr::COMP_COLOR || infoIt->componentId == hyperhdr::COMP_IMAGE)) - emit signalTimeTrigger(); // as signal to prevent Threading issues + emit SignalTimeTrigger_Internal(); // as signal to prevent Threading issues ++infoIt; } @@ -371,18 +306,18 @@ void PriorityMuxer::setCurrentTime() _previousPriority = _currentPriority; _currentPriority = newPriority; Info(_log, "Set visible priority to %d", newPriority); - emit visiblePriorityChanged(newPriority); + emit SignalVisiblePriorityChanged(newPriority); // check for visible comp change if (comp != _prevVisComp) { _prevVisComp = comp; - emit visibleComponentChanged(comp); + emit SignalVisibleComponentChanged(comp); } - emit prioritiesChanged(); + emit SignalPrioritiesChanged(); } } -void PriorityMuxer::timeTrigger() +void Muxer::timeTrigger() { if (_blockTimer->isActive()) { @@ -390,7 +325,7 @@ void PriorityMuxer::timeTrigger() } else { - emit timeRunner(); + emit SignalTimeRunner_Internal(); _blockTimer->start(1000); } } diff --git a/sources/base/NetworkForwarder.cpp b/sources/base/NetworkForwarder.cpp new file mode 100644 index 000000000..410f4d4f5 --- /dev/null +++ b/sources/base/NetworkForwarder.cpp @@ -0,0 +1,320 @@ +/* NetworkForwarder.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include + + #include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +NetworkForwarder::NetworkForwarder() + : QObject(nullptr) + , _log(Logger::getInstance("NETFORWARDER")) + , _forwarderEnabled(false) + , _priority(140) + , _hasImage(false) + , _image() +{ +} + +void NetworkForwarder::startedHandler() +{ + std::shared_ptr instanceManager; + emit GlobalSignals::getInstance()->SignalGetInstanceManager(instanceManager); + if (instanceManager == nullptr) + { + Error(_log, "Instance manager is already removed"); + return; + } + + std::shared_ptr hyperhdr; + SAFE_CALL_1_RET(instanceManager.get(), getHyperHdrInstance, std::shared_ptr, hyperhdr, quint8, 0); + if (hyperhdr == nullptr) + { + Error(_log, "Could not get first instance"); + return; + } + + _instanceZero = hyperhdr; + + connect(this, &NetworkForwarder::SignalForwardImage, this, &NetworkForwarder::signalForwardImageHandler, Qt::QueuedConnection); + connect(hyperhdr.get(), &HyperHdrInstance::SignalInstanceSettingsChanged, this, &NetworkForwarder::handleSettingsUpdate); + connect(hyperhdr.get(), &HyperHdrInstance::SignalRequestComponent, this, &NetworkForwarder::handleCompStateChangeRequest); + + handleCompStateChangeRequest(hyperhdr::COMP_FORWARDER, true); +} + +NetworkForwarder::~NetworkForwarder() +{ + auto hyperhdr = _instanceZero.lock(); + if (hyperhdr != nullptr) + { + disconnect(hyperhdr.get(), nullptr, this, nullptr); + } + + Debug(_log, "NetworkForwarder has been removed"); +} + +void NetworkForwarder::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +{ + if (type == settings::type::NETFORWARD) + { + auto hyperhdr = _instanceZero.lock(); + if (hyperhdr == nullptr) + return; + + _jsonSlaves.clear(); + _flatSlaves.clear(); + while (!_forwardClients.isEmpty()) + _forwardClients.takeFirst()->deleteLater(); + + disconnect(hyperhdr.get(), &HyperHdrInstance::SignalForwardJsonMessage, this, nullptr); + disconnect(hyperhdr.get(), &HyperHdrInstance::SignalInstanceImageUpdated, this, nullptr); + disconnect(GlobalSignals::getInstance(), nullptr, this, nullptr); + + _hasImage = false; + _image = Image(); + + const QJsonObject& obj = config.object(); + + if (!obj["enable"].toBool() || !_forwarderEnabled) + { + _forwarderEnabled = false; + QUEUE_CALL_2(hyperhdr.get(), setNewComponentState,hyperhdr::Components, hyperhdr::COMP_FORWARDER, bool, _forwarderEnabled); + return; + } + + if (!obj["json"].isNull()) + { + QJsonDocument jsonConf; + SAFE_CALL_1_RET(hyperhdr.get(), getSetting, QJsonDocument, jsonConf, settings::type, settings::type::JSONSERVER); + for (auto&& entry : obj["json"].toArray()) + { + addJsonSlave(entry.toString(), jsonConf.object()); + } + } + + if (!obj["flat"].isNull()) + { + QJsonDocument flatConf; + SAFE_CALL_1_RET(hyperhdr.get(), getSetting, QJsonDocument, flatConf, settings::type, settings::type::FLATBUFSERVER); + for (auto&& entry : obj["flat"].toArray()) + { + addFlatbufferSlave(entry.toString(), flatConf.object()); + } + } + + if (!_jsonSlaves.isEmpty()) + { + Info(_log, "Forward now to json targets '%s'", QSTRING_CSTR(_jsonSlaves.join(", "))); + connect(hyperhdr.get(), &HyperHdrInstance::SignalForwardJsonMessage, this, &NetworkForwarder::forwardJsonMessage, Qt::UniqueConnection); + } + + if (!_flatSlaves.isEmpty()) + { + connect(hyperhdr.get(), &HyperHdrInstance::SignalInstanceImageUpdated, this, &NetworkForwarder::handlerInstanceImageUpdated, Qt::DirectConnection); + } + + QUEUE_CALL_2(hyperhdr.get(), setNewComponentState, hyperhdr::Components, hyperhdr::COMP_FORWARDER, bool, _forwarderEnabled); + } +} + +void NetworkForwarder::handleCompStateChangeRequest(hyperhdr::Components component, bool enable) +{ + if (component == hyperhdr::COMP_FORWARDER && _forwarderEnabled != enable) + { + auto hyperhdr = _instanceZero.lock(); + if (hyperhdr == nullptr) + return; + + QJsonDocument netForConf; + SAFE_CALL_1_RET(hyperhdr.get(), getSetting, QJsonDocument, netForConf, settings::type, settings::type::NETFORWARD); + + _forwarderEnabled = enable; + handleSettingsUpdate(settings::type::NETFORWARD, netForConf); + Info(_log, "Forwarder has changed state to %s", (_forwarderEnabled ? "enabled" : "disabled")); + } +} + +void NetworkForwarder::addJsonSlave(const QString& slave, const QJsonObject& obj) +{ + QStringList parts = slave.split(":"); + if (parts.size() != 2) + { + Error(_log, "Unable to parse address (%s)", QSTRING_CSTR(slave)); + return; + } + + bool ok; + parts[1].toUShort(&ok); + if (!ok) + { + Error(_log, "Unable to parse port number (%s)", QSTRING_CSTR(parts[1])); + return; + } + + // verify loop with jsonserver + if (QHostAddress(parts[0]) == QHostAddress::LocalHost && parts[1].toInt() == obj["port"].toInt()) + { + Error(_log, "Loop between JsonServer and Forwarder! (%s)", QSTRING_CSTR(slave)); + return; + } + + if (_forwarderEnabled && !_jsonSlaves.contains(slave)) + _jsonSlaves << slave; +} + +void NetworkForwarder::addFlatbufferSlave(const QString& slave, const QJsonObject& obj) +{ + if (slave != HYPERHDR_DOMAIN_SERVER) + { + QStringList parts = slave.split(":"); + if (parts.size() != 2) + { + Error(_log, "Unable to parse address (%s)", QSTRING_CSTR(slave)); + return; + } + + bool ok; + parts[1].toUShort(&ok); + if (!ok) + { + Error(_log, "Unable to parse port number (%s)", QSTRING_CSTR(parts[1])); + return; + } + + // verify loop with flatbufserver + if (QHostAddress(parts[0]) == QHostAddress::LocalHost && parts[1].toInt() == obj["port"].toInt()) + { + Error(_log, "Loop between Flatbuffer Server and Forwarder! (%s)", QSTRING_CSTR(slave)); + return; + } + } + + if (_forwarderEnabled && !_flatSlaves.contains(slave)) + { + _flatSlaves << slave; + + FlatBufferConnection* flatbuf = new FlatBufferConnection(this, "Forwarder", slave, _priority, false); + _forwardClients << flatbuf; + } +} + +void NetworkForwarder::forwardJsonMessage(const QJsonObject& message) +{ + if (_forwarderEnabled) + { + QTcpSocket client; + for (int i = 0; i < _jsonSlaves.size(); i++) + { + QStringList parts = _jsonSlaves.at(i).split(":"); + client.connectToHost(QHostAddress(parts[0]), parts[1].toUShort()); + if (client.waitForConnected(500)) + { + sendJsonMessage(message, &client); + client.close(); + } + } + } +} + +void NetworkForwarder::handlerInstanceImageUpdated(const Image& image) +{ + if (_hasImage.exchange(true)) + return; + + _image = image; + + emit SignalForwardImage(); +} + +void NetworkForwarder::signalForwardImageHandler() +{ + if (!_hasImage.exchange(false)) + return; + + for (int i = 0; i < _forwardClients.size(); i++) + { + emit _forwardClients.at(i)->SignalImageToSend(_image); + } + + _image = Image(); +} + +void NetworkForwarder::sendJsonMessage(const QJsonObject& message, QTcpSocket* socket) +{ + QJsonObject jsonMessage = message; + if (jsonMessage.contains("tan") && jsonMessage["tan"].isNull()) + jsonMessage["tan"] = 100; + + // serialize message + QJsonDocument writer(jsonMessage); + QByteArray serializedMessage = writer.toJson(QJsonDocument::Compact) + "\n"; + + // write message + socket->write(serializedMessage); + if (!socket->waitForBytesWritten()) + { + Debug(_log, "Error while writing data to host"); + return; + } + + // read reply data + QByteArray serializedReply; + while (!serializedReply.contains('\n')) + { + // receive reply + if (!socket->waitForReadyRead()) + { + Debug(_log, "Error while writing data from host"); + return; + } + + serializedReply += socket->readAll(); + } + + // parse reply data + QJsonParseError error; + QJsonDocument reply = QJsonDocument::fromJson(serializedReply, &error); + + if (error.error != QJsonParseError::NoError) + { + Error(_log, "Error while parsing reply: invalid json"); + return; + } +} diff --git a/sources/utils/RawUdpServer.cpp b/sources/base/RawUdpServer.cpp similarity index 83% rename from sources/utils/RawUdpServer.cpp rename to sources/base/RawUdpServer.cpp index 40bef629c..3245c0cf6 100644 --- a/sources/utils/RawUdpServer.cpp +++ b/sources/base/RawUdpServer.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,27 +25,28 @@ * SOFTWARE. */ -// util -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif -// qt -#include #include -#include #include -#include -RawUdpServer::RawUdpServer(HyperHdrInstance* hyperhdr, const QJsonDocument& config, QObject* parent) - : QObject(parent) +#include +#include +#include +#include + +RawUdpServer::RawUdpServer(HyperHdrInstance* ownerInstance, const QJsonDocument& config) + : QObject(ownerInstance) , _server(new QUdpSocket(this)) , _log(Logger::getInstance("RAW_UDP_SERVER")) , _port(0) , _priority(0) , _initialized(false) - , _hyperhdr(hyperhdr) + , _ownerInstance(ownerInstance) , _config(config) , _inactiveTimer(new QTimer(this)) { @@ -56,7 +57,7 @@ RawUdpServer::~RawUdpServer() { stopServer(); - delete _server; + std::cout << "RawUdpServer exists now" << std::endl; } void RawUdpServer::initServer() @@ -76,7 +77,7 @@ void RawUdpServer::initServer() void RawUdpServer::dataTimeout() { - _hyperhdr->setInputInactive(_priority); + _ownerInstance->setInputInactive(_priority); } @@ -105,7 +106,7 @@ void RawUdpServer::handleSettingsUpdate(settings::type type, const QJsonDocument else { if (_initialized) - _hyperhdr->setInputInactive(_priority); + _ownerInstance->setInputInactive(_priority); _inactiveTimer->stop(); @@ -124,8 +125,8 @@ void RawUdpServer::readPendingDatagrams() if (dataLen % 3 > 0 || dataLen > 1500 || dataLen == 0) continue; - if (_hyperhdr->getPriorityInfo(_priority).componentId != hyperhdr::COMP_RAWUDPSERVER) - _hyperhdr->registerInput(_priority, hyperhdr::COMP_RAWUDPSERVER, QString("%1").arg(datagram.senderAddress().toString())); + if (_ownerInstance->getComponentForPriority(_priority) != hyperhdr::COMP_RAWUDPSERVER) + _ownerInstance->registerInput(_priority, hyperhdr::COMP_RAWUDPSERVER, QString("%1").arg(datagram.senderAddress().toString())); std::vector _ledColors; @@ -138,7 +139,7 @@ void RawUdpServer::readPendingDatagrams() _ledColors.push_back(c); } - _hyperhdr->setInput(_priority, _ledColors); + _ownerInstance->setInputLeds(_priority, _ledColors); _inactiveTimer->start(); } diff --git a/sources/base/Smoothing.cpp b/sources/base/Smoothing.cpp new file mode 100644 index 000000000..1259058f3 --- /dev/null +++ b/sources/base/Smoothing.cpp @@ -0,0 +1,634 @@ +/* Smoothing.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +#ifndef PCH_ENABLED + #include + #include + + #include + #include + #include +#endif + +#include + +#include +#include + +using namespace hyperhdr; + +namespace { + const int64_t DEFAUL_SETTLINGTIME = 200; // settlingtime in ms + const double DEFAUL_UPDATEFREQUENCY = 25; // updatefrequncy in hz + const double MINIMAL_UPDATEFREQUENCY = 20; +} + +Smoothing::Smoothing(const QJsonDocument& config, HyperHdrInstance* hyperhdr) + : QObject(hyperhdr), + _log(Logger::getInstance(QString("SMOOTHING%1").arg(hyperhdr->getInstanceIndex()))), + _hyperhdr(hyperhdr), + _updateInterval(static_cast(1000 / DEFAUL_UPDATEFREQUENCY)), + _settlingTime(DEFAUL_SETTLINGTIME), + _continuousOutput(false), + _antiFlickeringTreshold(0), + _antiFlickeringStep(0), + _antiFlickeringTimeout(0), + _flushFrame(false), + _targetTimeUnsafe(0), + _targetTimeCopy(0), + _previousTime(0), + _pause(false), + _currentConfigId(0), + _enabled(false), + _hasData(false), + _connected(false), + _directMode(false), + _smoothingType(SmoothingType::Linear), + _infoUpdate(true), + _infoInput(true), + _coolDown(0) +{ + // init cfg 0 (default) + addConfig(DEFAUL_SETTLINGTIME, DEFAUL_UPDATEFREQUENCY); + handleSettingsUpdate(settings::type::SMOOTHING, config); + SelectConfig(0, true); + + // add pause on cfg 1 + _configurations.push_back(std::unique_ptr(new SmoothingConfig(true, 0, 0, false))); + + // listen for comp changes + connect(_hyperhdr, &HyperHdrInstance::SignalRequestComponent, this, &Smoothing::componentStateChange); +} + +inline uint8_t Smoothing::clamp(int x) +{ + return (x < 0) ? 0 : ((x > 255) ? 255 : uint8_t(x)); +} + +void Smoothing::clearQueuedColors(bool deviceEnabled, bool restarting) +{ + QMutexLocker locker(&_setupSynchro); + + try + { + Info(_log, "Clearing queued colors before: %s%s", + (deviceEnabled) ? "enabling" : "disabling", + (restarting) ? ". Smoothing configuration changed: restarting timer." : ""); + + if ((!deviceEnabled || restarting) && _connected) + { + _connected = false; + disconnect(this, &Smoothing::SignalMasterClockTick, this, &Smoothing::updateLeds); + } + + _currentColors.clear(); + _currentTimeouts.clear(); + _previousTime = 0; + _targetColorsUnsafe.clear(); + _targetColorsCopy.clear(); + _targetTimeUnsafe = 0; + _targetTimeCopy = 0; + _flushFrame = false; + _infoUpdate = true; + _infoInput = true; + _coolDown = 0; + + if (deviceEnabled && !_connected) + { + _connected = true; + connect(this, &Smoothing::SignalMasterClockTick, this, &Smoothing::updateLeds, Qt::DirectConnection); + } + + emit _hyperhdr->SignalSmoothingRestarted(this->GetSuggestedInterval()); + + Info(_log, "Smoothing queue is cleared"); + } + catch (...) + { + Debug(_log, "Smoothing error detected"); + } +} + +void Smoothing::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +{ + if (type == settings::type::SMOOTHING) + { + if (InternalClock::isPreciseSteady()) + Info(_log, "High resolution clock is steady (good)"); + else + Warning(_log, "High resolution clock is NOT STEADY!"); + + QJsonObject obj = config.object(); + + if (isEnabled() != obj["enable"].toBool(true)) + { + SetEnable(obj["enable"].toBool(true)); + } + + _continuousOutput = obj["continuousOutput"].toBool(true); + + _configurations[0] = std::unique_ptr(new SmoothingConfig( + false, + static_cast(obj["time_ms"].toInt(DEFAUL_SETTLINGTIME)), + static_cast(std::round(std::max(1000.0 / std::max(obj["updateFrequency"].toDouble(DEFAUL_UPDATEFREQUENCY), MINIMAL_UPDATEFREQUENCY), 5.0))), + false)); + + SmoothingConfig* cfg = _configurations[0].get(); + + if (obj["type"].toString("linear") == "alternative") + cfg->type = SmoothingType::Alternative; + else + cfg->type = SmoothingType::Linear; + + cfg->antiFlickeringTreshold = obj["lowLightAntiFlickeringTreshold"].toInt(0); + cfg->antiFlickeringStep = obj["lowLightAntiFlickeringValue"].toInt(0); + cfg->antiFlickeringTimeout = obj["lowLightAntiFlickeringTimeout"].toInt(0); + + Info(_log, "Creating config (%d) => type: %s, dirMode: %s, pause: %s, settlingTime: %ims, interval: %ims (%iHz), antiFlickTres: %i, antiFlickStep: %i, antiFlickTime: %i", + _currentConfigId, QSTRING_CSTR(SmoothingConfig::EnumToString(cfg->type)), (cfg->directMode) ? "true" : "false", (cfg->pause) ? "true" : "false", int(cfg->settlingTime), int(cfg->updateInterval), int(1000.0 / cfg->updateInterval), cfg->antiFlickeringTreshold, cfg->antiFlickeringStep, int(cfg->antiFlickeringTimeout)); + + // if current id is 0, we need to apply the settings (forced) + if (_currentConfigId == 0) + { + if (_currentColors.size() > 0 && isEnabled()) + QUEUE_CALL_0(_hyperhdr, update); + SelectConfig(0, true); + } + } +} + +void Smoothing::antiflickering(std::vector& newTarget) +{ + int64_t now = InternalClock::nowPrecise(); + + if (_antiFlickeringTreshold > 0 && _antiFlickeringStep > 0) + { + for (size_t i = 0; i < _targetColorsUnsafe.size(); ++i) + { + ColorRgb& newColor = newTarget[i]; + const ColorRgb& oldColor = _targetColorsUnsafe[i]; + int64_t& timeout = _currentTimeouts[i]; + bool setNewColor = true; + + int averageNewColor = + (static_cast(std::min(newColor.red, std::min(newColor.green, newColor.blue))) + + static_cast(std::max(newColor.red, std::max(newColor.green, newColor.blue)))) / 2; + + if (_antiFlickeringTimeout > 0 && averageNewColor < _antiFlickeringTreshold && + averageNewColor && oldColor.hasColor()) + { + int minR = std::abs(int(newColor.red) - int(oldColor.red)); + int minG = std::abs(int(newColor.green) - int(oldColor.green)); + int minB = std::abs(int(newColor.blue) - int(oldColor.blue)); + int select = std::max(std::max(minR, minG), minB); + + if (select > 0 && select < _antiFlickeringStep) + { + if (now > timeout && now - timeout < _antiFlickeringTimeout) + { + setNewColor = false; + } + } + } + + if (!setNewColor) + newColor = oldColor; + else + timeout = now; + } + } +} + +void Smoothing::UpdateLedValues(const std::vector& ledValues) +{ + if (!_enabled) + return; + + _coolDown = 1; + + if (_directMode) + { + if (_connected) + clearQueuedColors(); + + if (_pause || ledValues.size() == 0) + return; + + queueColors(ledValues); + + return; + } + + try + { + if (_infoInput) + Info(_log, "Using %s smoothing input (%i)", ((_smoothingType == SmoothingType::Alternative) ? "alternative" : "linear"), _currentConfigId); + + _infoInput = false; + linearSetup(ledValues); + } + catch (...) + { + Debug(_log, "Smoothing error detected"); + } + + return; +} + +void Smoothing::linearSetup(const std::vector& ledValues) +{ + std::vector newTarget = ledValues; + qint64 newTime = InternalClock::nowPrecise() + _settlingTime; + + if (newTarget.size() != _currentTimeouts.size()) + { + _currentTimeouts = std::vector(newTarget.size(), InternalClock::nowPrecise()); + } + else + { + antiflickering(newTarget); + } + + _dataSynchro.lock(); + _hasData = true; + _targetColorsUnsafe.swap(newTarget); + _targetTimeUnsafe = newTime; + _dataSynchro.unlock(); +} + +inline uint8_t Smoothing::computeColor(int64_t k, int color) +{ + int delta = std::abs(color); + auto step = std::min((int)std::max(static_cast((k * delta) >> 8), 1), delta); + + return step; +} + +void Smoothing::setupAdvColor(int64_t deltaTime, float& kOrg, float& kMin, float& kMid, float& kAbove, float& kMax) +{ + kOrg = std::max(1.0f - 1.0f * deltaTime / (_targetTimeCopy - _previousTime), 0.0001f); + + kMin = std::min(std::pow(kOrg, 1.0f), 1.0f); + kMid = std::min(std::pow(kOrg, 0.9f), 1.0f); + kAbove = std::min(std::pow(kOrg, 0.75f), 1.0f); + kMax = std::min(std::pow(kOrg, 0.6f), 1.0f); +} + +inline uint8_t Smoothing::computeAdvColor(int limitMin, int limitAverage, int limitMax, float kMin, float kMid, float kAbove, float kMax, int color) +{ + int val = std::abs(color); + if (val < limitMin) + return std::ceil(kMax * val); + else if (val < limitAverage) + return std::ceil(kAbove * val); + else if (val < limitMax) + return std::ceil(kMid * val); + else + return std::ceil(kMin * val); +} + +void Smoothing::updateLeds() +{ + if (!_enabled || !_setupSynchro.tryLock()) + return; + + try + { + if (_hasData && _dataSynchro.tryLock()) + { + _hasData = false; + _targetColorsCopy = _targetColorsUnsafe; + _targetTimeCopy = _targetTimeUnsafe; + _dataSynchro.unlock(); + } + + if (_currentColors.empty() || _currentColors.size() != _targetColorsCopy.size()) + { + if (!_currentColors.empty()) + Warning(_log, "Detect buffer size has changed. Previous buffer size: %d, new size: %d.", _currentColors.size(), _targetColorsCopy.size()); + + _previousTime = InternalClock::nowPrecise(); + _currentColors = _targetColorsCopy; + } + else if (_smoothingType == SmoothingType::Alternative) + { + if (_infoUpdate) + Info(_log, "Using alternative smoothing procedure (%i)", _currentConfigId); + _infoUpdate = false; + + linearSmoothingProcessing(true); + } + else + { + if (_infoUpdate) + Info(_log, "Using linear smoothing procedure (%i)", _currentConfigId); + _infoUpdate = false; + + linearSmoothingProcessing(false); + } + + if (_hasData) + { + _dataSynchro.lock(); + _hasData = false; + _targetColorsCopy = _targetColorsUnsafe; + _targetTimeCopy = _targetTimeUnsafe; + _dataSynchro.unlock(); + } + } + catch (...) + { + Debug(_log, "Smoothing error detected"); + } + + _setupSynchro.unlock(); +} + +void Smoothing::queueColors(const std::vector& ledColors) +{ + if (!_pause) + { + emit _hyperhdr->SignalUpdateLeds(ledColors); + } +} + +void Smoothing::componentStateChange(hyperhdr::Components component, bool state) +{ + _flushFrame = state; + + if (component == hyperhdr::COMP_LEDDEVICE) + { + clearQueuedColors(state); + } + + if (component == hyperhdr::COMP_SMOOTHING) + { + SetEnable(state); + } +} + +void Smoothing::SetEnable(bool enable) +{ + _enabled = enable; + + clearQueuedColors(_enabled); + + // update comp register + _hyperhdr->setNewComponentState(hyperhdr::COMP_SMOOTHING, enable); +} + +unsigned Smoothing::addConfig(int settlingTime_ms, double ledUpdateFrequency_hz, bool directMode) +{ + _configurations.push_back(std::unique_ptr( + new SmoothingConfig(false, settlingTime_ms, int64_t(1000.0 / ledUpdateFrequency_hz), directMode))); + + return static_cast(_configurations.size() - 1); +} + +unsigned Smoothing::UpdateConfig(unsigned cfgID, int settlingTime_ms, double ledUpdateFrequency_hz, bool directMode) +{ + int64_t interval = static_cast(1000.0 / ledUpdateFrequency_hz); + + if (cfgID < static_cast(_configurations.size())) + { + _configurations[cfgID] = std::unique_ptr(new SmoothingConfig(false, settlingTime_ms, interval, directMode)); + return cfgID; + } + else + { + unsigned currentCfgID = 0; + for (auto&& element : _configurations) + { + if (element->settlingTime == settlingTime_ms && + element->updateInterval == interval && + element->directMode == directMode) + return currentCfgID; + + currentCfgID++; + } + return addConfig(settlingTime_ms, ledUpdateFrequency_hz, directMode); + } +} + +void Smoothing::UpdateCurrentConfig(int settlingTime_ms) +{ + _settlingTime = settlingTime_ms; + + Info(_log, "Updating current config (%d) => type: %s, dirMode: %s, pause: %s, settlingTime: %ims, interval: %ims (%iHz), antiFlickTres: %i, antiFlickStep: %i, antiFlickTime: %i", + _currentConfigId, QSTRING_CSTR(SmoothingConfig::EnumToString(_smoothingType)), (_directMode) ? "true" : "false", (_pause) ? "true" : "false", int(_settlingTime), + int(_updateInterval), int(1000.0 / _updateInterval), _antiFlickeringTreshold, _antiFlickeringStep, int(_antiFlickeringTimeout)); +} + +int Smoothing::GetSuggestedInterval() +{ + if (_enabled && !_directMode) + { + return static_cast(_updateInterval); + } + + return 0; +} + +bool Smoothing::SelectConfig(unsigned cfg, bool force) +{ + //WATCH + if (_currentConfigId == cfg && !force) + { + return true; + } + + if (cfg < (unsigned)_configurations.size()) + { + _settlingTime = _configurations[cfg]->settlingTime; + _pause = _configurations[cfg]->pause; + _directMode = _configurations[cfg]->directMode; + _antiFlickeringTreshold = _configurations[cfg]->antiFlickeringTreshold; + _antiFlickeringStep = _configurations[cfg]->antiFlickeringStep; + _antiFlickeringTimeout = _configurations[cfg]->antiFlickeringTimeout; + + int64_t newUpdateInterval = std::max(_configurations[cfg]->updateInterval, (int64_t)5); + + if (newUpdateInterval != _updateInterval || _configurations[cfg]->type != _smoothingType) + { + _updateInterval = newUpdateInterval; + _smoothingType = _configurations[cfg]->type; + + clearQueuedColors(!_pause && _enabled, true); + } + + _currentConfigId = cfg; + + Info(_log, "Selecting config (%d) => type: %s, directMode: %s, pause: %s, settlingTime: %ims, interval: %ims (%iHz), antiFlickTres: %i, antiFlickStep: %i, antiFlickTime: %i", + _currentConfigId, QSTRING_CSTR(SmoothingConfig::EnumToString(_smoothingType)), (_directMode) ? "true" : "false", (_pause) ? "true" : "false", int(_settlingTime), + int(_updateInterval), int(1000.0 / _updateInterval), _antiFlickeringTreshold, _antiFlickeringStep, int(_antiFlickeringTimeout)); + + return true; + } + + // reset to default + _currentConfigId = 0; + return false; +} + +bool Smoothing::isPaused() const +{ + return _pause; +} + +bool Smoothing::isEnabled() const +{ + return _enabled && !_pause; +} + +Smoothing::SmoothingConfig::SmoothingConfig(bool __pause, int64_t __settlingTime, int64_t __updateInterval, + bool __directMode, SmoothingType __type, int __antiFlickeringTreshold, int __antiFlickeringStep, + int64_t __antiFlickeringTimeout) : + pause(__pause), + settlingTime(__settlingTime), + updateInterval(__updateInterval), + directMode(__directMode), + type(__type), + antiFlickeringTreshold(__antiFlickeringTreshold), + antiFlickeringStep(__antiFlickeringStep), + antiFlickeringTimeout(__antiFlickeringTimeout) +{ +} + +QString Smoothing::SmoothingConfig::EnumToString(SmoothingType type) +{ + if (type == SmoothingType::Linear) + return QString("Linear"); + else if (type == SmoothingType::Alternative) + return QString("Alternative"); + + return QString("Unknown"); +} + +void Smoothing::linearSmoothingProcessing(bool correction) +{ + float kOrg, kMin, kMid, kAbove, kMax; + int64_t now = InternalClock::nowPrecise(); + int64_t deltaTime = _targetTimeCopy - now; + int64_t k; + + int aspectLow = 16; + int aspectMid = 32; + int aspectHigh = 60; + + + if (deltaTime <= 0 || _targetTimeCopy <= _previousTime) + { + _currentColors = _targetColorsCopy; + _previousTime = now; + + if (_flushFrame) + queueColors(_currentColors); + + if (!_continuousOutput && _coolDown > 0) + { + _coolDown--; + _flushFrame = true; + } + else + _flushFrame = _continuousOutput; + } + else + { + _flushFrame = true; + + if (correction) + setupAdvColor(deltaTime, kOrg, kMin, kMid, kAbove, kMax); + else + k = std::max((static_cast(1) << 8) - (deltaTime << 8) / (_targetTimeCopy - _previousTime), static_cast(1)); + + + for (size_t i = 0; i < _currentColors.size(); i++) + { + ColorRgb& prev = _currentColors[i]; + ColorRgb& target = _targetColorsCopy[i]; + + int redDiff = target.red - prev.red; + int greenDiff = target.green - prev.green; + int blueDiff = target.blue - prev.blue; + + if (redDiff != 0) + prev.red += (redDiff < 0 ? -1 : 1) * + ((correction) ? computeAdvColor(aspectLow, aspectMid, aspectHigh, kMin, kMid, kAbove, kMax, redDiff) : computeColor(k, redDiff)); + if (greenDiff != 0) + prev.green += (greenDiff < 0 ? -1 : 1) * + ((correction) ? computeAdvColor(aspectLow, aspectMid, aspectHigh, kMin, kMid, kAbove, kMax, greenDiff) : computeColor(k, greenDiff)); + if (blueDiff != 0) + prev.blue += (blueDiff < 0 ? -1 : 1) * + ((correction) ? computeAdvColor(aspectLow, aspectMid, aspectHigh, kMin, kMid, kAbove, kMax, blueDiff) : computeColor(k, blueDiff)); + } + + _previousTime = now; + + queueColors(_currentColors); + } +} + +void Smoothing::debugOutput(std::vector& _targetValues) +{ + static int _debugCounter = 0; + _debugCounter = std::min(_debugCounter+1,900); + if (_targetValues.size() > 0 && _debugCounter < 900) + { + if (_debugCounter < 100 || (_debugCounter > 200 && _debugCounter < 300) || (_debugCounter > 400 && _debugCounter < 500) || (_debugCounter > 600 && _debugCounter < 700) || _debugCounter>800) + { + _targetValues[0].red = 0; + _targetValues[0].green = 0; + _targetValues[0].blue = 0; + } + else if (_debugCounter >= 100 && _debugCounter <= 200) + { + _targetValues[0].red = 255; + _targetValues[0].green = 255; + _targetValues[0].blue = 255; + } + else if (_debugCounter >= 300 && _debugCounter <= 400) + { + _targetValues[0].red = 255; + _targetValues[0].green = 0; + _targetValues[0].blue = 0; + } + else if (_debugCounter >= 500 && _debugCounter <= 600) + { + _targetValues[0].red = 0; + _targetValues[0].green = 255; + _targetValues[0].blue = 0; + } + else if (_debugCounter >= 700 && _debugCounter <= 800) + { + _targetValues[0].red = 0; + _targetValues[0].green = 0; + _targetValues[0].blue = 255; + } + } +} + + diff --git a/sources/base/SoundCapture.cpp b/sources/base/SoundCapture.cpp index 8ea817145..975d1498c 100644 --- a/sources/base/SoundCapture.cpp +++ b/sources/base/SoundCapture.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,34 +25,47 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include + + #include +#endif + +#include + +#include #include -#include -#include -#include +#include + +namespace +{ + SoundCaptureResult resultFFT; + int16_t imgBuffer[SOUNDCAP_N_WAVE * 2] = {}; +} uint32_t SoundCapture::_noSoundCounter = 0; bool SoundCapture::_noSoundWarning = false; bool SoundCapture::_soundDetectedInfo = false; -QSemaphore SoundCapture::_semaphore(1); -bool SoundCapture::_isRunning = false; -SoundCapture* SoundCapture::_soundInstance = NULL; -uint32_t SoundCapture::_resultIndex = 0; -int16_t SoundCapture::_imgBuffer[SOUNDCAP_N_WAVE * 2]; -SoundCaptureResult SoundCapture::_resultFFT; + +namespace +{ + QMutex soundBufferGuard; +} SoundCapture::SoundCapture(const QJsonDocument& effectConfig, QObject* parent) : _isActive(false), _selectedDevice(""), + _isRunning(false), _maxInstance(0) { - _soundInstance = this; - handleSettingsUpdate(settings::type::SNDEFFECT, effectConfig); + _logger = Logger::getInstance("SOUND_GRABBER"); + settingsChangedHandler(settings::type::SNDEFFECT, effectConfig); qRegisterMetaType("uint32_t"); } SoundCapture::~SoundCapture() { - _soundInstance = NULL; + Info(_logger, "SoundGrabber is released"); } QJsonObject SoundCapture::getJsonInfo() @@ -76,11 +89,6 @@ QString SoundCapture::getSelectedDevice() const return _selectedDevice; } -SoundCapture* SoundCapture::getInstance() -{ - return _soundInstance; -} - QList SoundCapture::getDevices() const { return _availableDevices; @@ -92,7 +100,7 @@ bool SoundCapture::getActive() const } -void SoundCapture::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +void SoundCapture::settingsChangedHandler(settings::type type, const QJsonDocument& config) { if (type == settings::type::SNDEFFECT) { @@ -108,195 +116,82 @@ void SoundCapture::handleSettingsUpdate(settings::type type, const QJsonDocument { _selectedDevice = dev; _isActive = true; - Info(Logger::getInstance("HYPERHDR"), "Sound device '%s' is selected for activation", QSTRING_CSTR(_selectedDevice)); + Info(_logger, "Sound device '%s' is selected for activation", QSTRING_CSTR(_selectedDevice)); } } if (!_isActive) - Info(Logger::getInstance("HYPERHDR"), "Sound device is disabled"); + Info(_logger, "Sound device is disabled"); } } -uint32_t SoundCapture::getCaptureInstance() +uint32_t SoundCapture::open() { - uint32_t ret = 0; + uint32_t instance = 0; try { - _semaphore.acquire(); - - ret = ++_maxInstance; - _instances.append(ret); + instance = ++_maxInstance; + _instances.append(instance); if (!_isRunning) { if (!_isActive) - Error(Logger::getInstance("HYPERHDR"), "Sound device is not configured"); + Error(_logger, "Sound device is not configured"); else { - Info(Logger::getInstance("HYPERHDR"), "Sound device is starting"); - Start(); - Info(Logger::getInstance("HYPERHDR"), "Sound device has started"); + Info(_logger, "Sound device is starting"); + + resultFFT.ResetData(); + _noSoundCounter = 0; + _noSoundWarning = false; + _soundDetectedInfo = false; + + start(); + Info(_logger, "Sound device has started (handle: %i)", instance); } } else - Info(Logger::getInstance("HYPERHDR"), "Sound device is already running"); + Info(_logger, "Sound device is already running"); } catch (...) { } - _semaphore.release(); - - return ret; + return instance; } -void SoundCapture::releaseCaptureInstance(uint32_t instance) +void SoundCapture::close(uint32_t instance) { try { - _semaphore.acquire(); + Info(_logger, "Releasing sound grabber (handle: %i)", instance); if (_instances.contains(instance)) + { _instances.removeOne(instance); - if (_instances.count() == 0) - { - Info(Logger::getInstance("HYPERHDR"), "Sound device is stopping"); - Stop(); - _resultFFT.ResetData(); - _resultIndex = 0; - _noSoundCounter = 0; - _noSoundWarning = false; - _soundDetectedInfo = false; - Info(Logger::getInstance("HYPERHDR"), "Sound device has stopped"); + if (_instances.count() == 0) + { + Info(_logger, "The sound device stops"); + stop(); + Info(_logger, "The sound device has been stopped"); + } } } catch (...) { } - - _semaphore.release(); -} - -void SoundCapture::ForcedClose() -{ - try - { - _semaphore.acquire(); - - _instances.clear(); - - Info(Logger::getInstance("HYPERHDR"), "Sound device is stopping (forced)"); - Stop(); - _resultFFT.ResetData(); - _resultIndex = 0; - _noSoundCounter = 0; - _noSoundWarning = false; - _soundDetectedInfo = false; - Info(Logger::getInstance("HYPERHDR"), "Sound device has stopped (forced)"); - } - catch (...) - { - - } - - _semaphore.release(); -} - -#define Limit(x,y) std::min(std::max(x, 0), y) -#define LimitMod(x,y) ((x+y) % y) -#define Drive(x,y) ((x == 0) ? x : ((x>=0 && x < y)? 1 : ( (x<0 && -x < y) ? -1: (x/y) ) )) - -int32_t SoundCaptureResult::getValue(int isMulti) -{ - if (isMulti == 2) - { - return Limit((_oldScaledAverage + _scaledAverage) / 2, 255); - } - else if (isMulti == 1) - { - return _scaledAverage; - } - - return -1; -} - -int32_t SoundCaptureResult::getValue3Step(int isMulti) -{ - if (isMulti == 2) - { - return Limit((_scaledAverage - _oldScaledAverage) / 3 + _oldScaledAverage, 255); - } - else if (isMulti == 1) - { - return Limit(((_scaledAverage - _oldScaledAverage) * 2) / 3 + _oldScaledAverage, 255); - } - else { - return _scaledAverage; - } -} - -SoundCaptureResult* SoundCapture::hasResult(uint32_t& lastIndex) -{ - if (lastIndex != _resultIndex) - { - lastIndex = _resultIndex; - return &_resultFFT; - } - - return NULL; } -SoundCaptureResult* SoundCapture::hasResult(AnimationBaseMusic* effect, uint32_t& lastIndex, bool* newAverage, bool* newSlow, bool* newFast, int* isMulti) +bool SoundCapture::analyzeSpectrum(int16_t soundBuffer[], int sizeP) { - if (lastIndex != _resultIndex || !_semaphore.tryAcquire()) - { - if (lastIndex != _resultIndex) - effect->store(&_resultFFT.mtInternal); - - lastIndex = _resultIndex; - - *isMulti = 2; - - if (newAverage != NULL) - *newAverage = true; - if (newSlow != NULL) - *newSlow = true; - if (newFast != NULL) - *newFast = true; - - return &_resultFFT; - } - - effect->restore(&_resultFFT.mtWorking); - - if (*isMulti <= 0) - { - _semaphore.release(); - return NULL; - } - - (*isMulti)--; - - if (newAverage != NULL) - *newAverage = _resultFFT.hasMiddleAverage(*isMulti); - - if (newSlow != NULL) - *newSlow = _resultFFT.hasMiddleSlow(*isMulti); - - if (newFast != NULL) - *newFast = _resultFFT.hasMiddleFast(*isMulti); - - effect->store(&_resultFFT.mtWorking); + if (!_isRunning) + return false; - _semaphore.release(); - return &_resultFFT; -} + QMutexLocker locker(&soundBufferGuard); -bool SoundCapture::AnaliseSpectrum(int16_t soundBuffer[], int sizeP) -{ int16_t resolutionP = 10; int16_t sec[SOUNDCAP_RESULT_RES] = { 2 * 2, // 0-86 @@ -322,19 +217,19 @@ bool SoundCapture::AnaliseSpectrum(int16_t soundBuffer[], int sizeP) if ((1 << sizeP) > SOUNDCAP_N_WAVE) return false; - memset(_imgBuffer, 0, sizeof(_imgBuffer)); - _resultFFT.ClearResult(); + memset(imgBuffer, 0, sizeof(imgBuffer)); + resultFFT.ClearResult(); if (noSound == 0) { - if (fix_fft(soundBuffer, _imgBuffer, resolutionP, 0) < 0) + if (fix_fft(soundBuffer, imgBuffer, resolutionP, 0) < 0) return false; for (int i = 0, limit = pow(2, resolutionP - 1), samplerIndex = 0, samplerCount = 0; i < limit; i++) { - uint32_t res = std::sqrt(((int32_t)soundBuffer[i]) * soundBuffer[i] + ((int32_t)_imgBuffer[i]) * _imgBuffer[i]); + uint32_t res = std::sqrt(((int32_t)soundBuffer[i]) * soundBuffer[i] + ((int32_t)imgBuffer[i]) * imgBuffer[i]); - _resultFFT.AddResult(samplerIndex, res); + resultFFT.AddResult(samplerIndex, res); samplerCount++; if (samplerIndex < SOUNDCAP_RESULT_RES - 1) @@ -348,390 +243,69 @@ bool SoundCapture::AnaliseSpectrum(int16_t soundBuffer[], int sizeP) } } - if (_isRunning && noSound == 1 && !_noSoundWarning && _noSoundCounter++ > 20) + if (noSound == 1 && !_noSoundWarning && _noSoundCounter++ > 20) { _noSoundWarning = true; _soundDetectedInfo = false; - Warning(Logger::getInstance("HYPERHDR"), "Sound stream: captured audio data but it's silence."); + Warning(_logger, "Audio Stream: audio data captured, but it is silent."); } - if (_isRunning && noSound == 0 && !_soundDetectedInfo) + if (noSound == 0 && !_soundDetectedInfo) { _noSoundCounter = 0; _noSoundWarning = false; _soundDetectedInfo = true; - Info(Logger::getInstance("HYPERHDR"), "Sound stream: succesfully captured audio data and the sound is detected."); - } - - - if (_semaphore.tryAcquire()) - { - _resultFFT.Smooth(); - _semaphore.release(); + Info(_logger, "Audio Stream: audio data successfully captured and sound detected."); } - if (_resultFFT.isDataValid()) - _resultIndex++; + resultFFT.Smooth(); return true; } -SoundCaptureResult::SoundCaptureResult() -{ - ResetData(); - - color[0] = QColor(255, 0, 0); - color[1] = QColor(190, 0, 0); - color[2] = QColor(160, 160, 0); - color[3] = QColor(50, 190, 20); - color[4] = QColor(20, 255, 50); - color[5] = QColor(0, 212, 160); - color[6] = QColor(0, 120, 190); - color[7] = QColor(0, 0, 255); - -} - -QColor SoundCaptureResult::getRangeColor(uint8_t index) const -{ - if (index < SOUNDCAP_RESULT_RES) - return color[index]; - return color[0]; -} - -void SoundCaptureResult::ClearResult() -{ - memset(pureResult, 0, sizeof(pureResult)); -} - -void SoundCaptureResult::AddResult(int samplerIndex, uint32_t val) -{ - pureResult[samplerIndex] += val; -} - - - -void SoundCaptureResult::Smooth() -{ - int32_t currentAverage = 0, distance = 0, red = 0, green = 0, blue = 0; - - MovingTarget res = mtInternal; - - _validData = true; - - for (int i = 0; i < SOUNDCAP_RESULT_RES; i++) - { - int32_t buffResult = (pureResult[i] + lastResult[i]) / 2; - - if (maxResult[i] <= buffResult && buffResult > 400) - { - if (maxResult[i] == 0) - _validData = false; - - maxResult[i] = buffResult; - - for (int j = 0; j < SOUNDCAP_RESULT_RES; j++) - if (i != j && maxResult[j] < buffResult / 4 && maxResult[j] < 400) - maxResult[j] = buffResult / 4; - } - - - if (_validData && maxResult[i] > 0) - { - int32_t sr = (pureResult[i] * 255) / maxResult[i]; - - if (sr > 255) - sr = 255; - - pureScaledResult[i] = (uint8_t)sr; - - if (pureScaledResult[i] >= buffScaledResult[i] || buffScaledResult[i] - deltas[i] < pureScaledResult[i]) - { - buffScaledResult[i] = pureScaledResult[i]; - deltas[i] = std::max(pureScaledResult[i] / 10, 3); - } - else - { - buffScaledResult[i] -= deltas[i]; - deltas[i] = std::min((deltas[i] * 3) / 2, 255); - } - } - - lastResult[i] = pureResult[i]; - } - - for (int j = 0; j < SOUNDCAP_RESULT_RES; j++) - { - int32_t max = maxResult[j]; - if (max > 2000) - maxResult[j] = max - 2; - - currentAverage += buffScaledResult[j]; - } - - if (_validData) - { - if (currentAverage > _maxAverage) - _maxAverage = currentAverage; - - if (_maxAverage > 0) - { - _currentMax = 0; - for (int i = 0; i < SOUNDCAP_RESULT_RES; i++) - if (buffScaledResult[i] > _currentMax) - _currentMax = buffScaledResult[i]; - - currentAverage = std::min((currentAverage * 255) / _maxAverage, 255); - currentAverage = std::min((currentAverage + _currentMax) / 2, 255); - - _oldScaledAverage = _scaledAverage; - - if (currentAverage >= _scaledAverage || _scaledAverage - averageDelta < currentAverage) - { - _scaledAverage = currentAverage; - averageDelta = std::max(currentAverage / 14, 3); - } - else - { - _scaledAverage -= averageDelta; - averageDelta = std::min(averageDelta * 2, 255); - } - - - - int32_t r = 0, g = 0, b = 0; - for (int i = 0; i < SOUNDCAP_RESULT_RES && _currentMax > 0; i++) - { - int rr = 0, gg = 0, bb = 0; - - color[i].getRgb(&rr, &gg, &bb); - - - r += std::max((rr * (_currentMax - 5 * (_currentMax - buffScaledResult[i]))) / _currentMax, 0); - g += std::max((gg * (_currentMax - 5 * (_currentMax - buffScaledResult[i]))) / _currentMax, 0); - b += std::max((bb * (_currentMax - 5 * (_currentMax - buffScaledResult[i]))) / _currentMax, 0); - } - - int32_t ccScale = std::max(std::max(r, g), b); - if (ccScale != 0) - { - r = (Limit((r * 255) / ccScale + 32, 255) * _scaledAverage) / 255; - g = (Limit((g * 255) / ccScale + 32, 255) * _scaledAverage) / 255; - b = (Limit((b * 255) / ccScale + 32, 255) * _scaledAverage) / 255; - } - QColor currentColor(r, g, b); - - int av_r, av_g, av_b; - CalculateRgbDelta(currentColor, _lastPrevColor, res._averageColor, av_r, av_g, av_b); - - int sv_r, sv_g, sv_b; - CalculateRgbDelta(currentColor, _lastPrevColor, res._slowColor, sv_r, sv_g, sv_b); - - int fv_r, fv_g, fv_b; - CalculateRgbDelta(currentColor, _lastPrevColor, res._fastColor, fv_r, fv_g, fv_b); - - - _lastPrevColor = currentColor; - - if (res._targetFastCounter-- <= 0) - { - distance = Limit((int32_t)std::sqrt(fv_r * fv_r + fv_g * fv_g + fv_b * fv_b), 255); - distance = std::max(distance, 30) / 15; - - res._targetFastR = Drive(fv_r, distance); - res._targetFastG = Drive(fv_g, distance); - res._targetFastB = Drive(fv_b, distance); - - if (std::abs(res._targetFastR) >= 2 || std::abs(res._targetFastG) >= 2 || std::abs(res._targetFastB) >= 2) - res._targetFastCounter = 1; - else - res._targetFastCounter = 0; - } - - - red = Limit(res._targetFastR + res._fastColor.red(), 255); - green = Limit(res._targetFastG + res._fastColor.green(), 255); - blue = Limit(res._targetFastB + res._fastColor.blue(), 255); - res._fastColor = QColor::fromRgb(red, green, blue); - - if (res._targetSlowCounter-- <= 0) - { - distance = Limit((int32_t)std::sqrt(sv_r * sv_r + sv_g * sv_g + sv_b * sv_b), 255); - distance = std::max(distance, 16) / 8; - - res._targetSlowR = Drive(sv_r, distance); - res._targetSlowG = Drive(sv_g, distance); - res._targetSlowB = Drive(sv_b, distance); - - if (std::abs(res._targetSlowR) >= 3 || std::abs(res._targetSlowG) >= 3 || std::abs(res._targetSlowB) >= 3) - res._targetSlowCounter = 3; - else if (std::abs(res._targetSlowR) >= 2 || std::abs(res._targetSlowG) >= 2 || std::abs(res._targetSlowB) >= 2) - res._targetSlowCounter = 1; - else - res._targetSlowCounter = 0; - } - - - red = Limit(res._targetSlowR + res._slowColor.red(), 255); - green = Limit(res._targetSlowG + res._slowColor.green(), 255); - blue = Limit(res._targetSlowB + res._slowColor.blue(), 255); - res._slowColor = QColor::fromRgb(red, green, blue); - - - if (res._targetAverageCounter-- <= 0) - { - distance = Limit((int32_t)std::sqrt(av_r * av_r + av_g * av_g + av_b * av_b), 255); - distance = std::max(distance, 22) / 11; - - res._targetAverageR = Drive(av_r, distance); - res._targetAverageG = Drive(av_g, distance); - res._targetAverageB = Drive(av_b, distance); - - if (std::abs(res._targetAverageR) >= 3 || std::abs(res._targetAverageG) >= 3 || std::abs(res._targetAverageB) >= 3) - res._targetAverageCounter = 3; - else if (std::abs(res._targetAverageR) >= 2 || std::abs(res._targetAverageG) >= 2 || std::abs(res._targetAverageB) >= 2) - res._targetAverageCounter = 1; - else - res._targetAverageCounter = 0; - } - - - red = Limit(res._targetAverageR + res._averageColor.red(), 255); - green = Limit(res._targetAverageG + res._averageColor.green(), 255); - blue = Limit(res._targetAverageB + res._averageColor.blue(), 255); - res._averageColor = QColor::fromRgb(red, green, blue); - - } - - if (_maxAverage > 1024) - _maxAverage--; - } - - mtInternal = res; - mtWorking = res; -} - -void SoundCaptureResult::CalculateRgbDelta(QColor currentColor, QColor prevColor, QColor selcolor, int& ab_r, int& ab_g, int& ab_b) +SoundCaptureResult* SoundCapture::hasResult(AnimationBaseMusic* effect, uint32_t& lastIndex, bool* newAverage, bool* newSlow, bool* newFast, int* isMulti) { - ab_r = ((currentColor.red() * 2 + prevColor.red()) / 3) - selcolor.red(); - ab_g = ((currentColor.green() * 2 + prevColor.green()) / 3) - selcolor.green(); - ab_b = ((currentColor.blue() * 2 + prevColor.blue()) / 3) - selcolor.blue(); -} + QMutexLocker locker(&soundBufferGuard); -bool SoundCaptureResult::hasMiddleAverage(int middle) -{ - if (mtWorking._targetAverageCounter <= 0) + if (lastIndex != resultFFT.getResultIndex()) { - return false; - } - - int red = Limit(mtWorking._targetAverageR / 2 + mtWorking._averageColor.red(), 255); - int green = Limit(mtWorking._targetAverageG / 2 + mtWorking._averageColor.green(), 255); - int blue = Limit(mtWorking._targetAverageB / 2 + mtWorking._averageColor.blue(), 255); + lastIndex = resultFFT.getResultIndex(); - mtWorking._averageColor = QColor::fromRgb(red, green, blue); + effect->store(&resultFFT.mtInternal); - if (middle == 0) - mtWorking._targetAverageCounter--; + *isMulti = 2; - return true; -} + if (newAverage != nullptr) + *newAverage = true; + if (newSlow != nullptr) + *newSlow = true; + if (newFast != nullptr) + *newFast = true; -bool SoundCaptureResult::hasMiddleSlow(int middle) -{ - if (mtWorking._targetSlowCounter <= 0) - { - return false; + return &resultFFT; } - int red = Limit(mtWorking._targetSlowR / 2 + mtWorking._slowColor.red(), 255); - int green = Limit(mtWorking._targetSlowG / 2 + mtWorking._slowColor.green(), 255); - int blue = Limit(mtWorking._targetSlowB / 2 + mtWorking._slowColor.blue(), 255); - - mtWorking._slowColor = QColor::fromRgb(red, green, blue); + effect->restore(&resultFFT.mtWorking); - if (middle == 0) - mtWorking._targetSlowCounter--; - - return true; -} - -bool SoundCaptureResult::hasMiddleFast(int middle) -{ - if (mtWorking._targetFastCounter <= 0) + if (*isMulti <= 0) { - return false; + return nullptr; } - int red = Limit(mtWorking._targetFastR / 2 + mtWorking._fastColor.red(), 255); - int green = Limit(mtWorking._targetFastG / 2 + mtWorking._fastColor.green(), 255); - int blue = Limit(mtWorking._targetFastB / 2 + mtWorking._fastColor.blue(), 255); - - mtWorking._fastColor = QColor::fromRgb(red, green, blue); - - if (middle == 0) - mtWorking._targetFastCounter--; - - return true; -} - - -void SoundCaptureResult::RestoreFullLum(QColor& color, int scale) -{ - int a, b, v; - color.getHsv(&a, &b, &v); - - if (v < scale) - color = QColor::fromHsv(a, b, scale); -} - -bool SoundCaptureResult::isDataValid() -{ - return _validData; -} - -bool SoundCaptureResult::GetStats(uint32_t& scaledAverage, uint32_t& currentMax, QColor& averageColor, QColor* fastColor, QColor* slowColor) -{ - scaledAverage = _scaledAverage; - averageColor = mtWorking._averageColor; - currentMax = _currentMax; - if (fastColor != NULL) - *fastColor = mtWorking._fastColor; - if (slowColor != NULL) - *slowColor = mtWorking._slowColor; - return _validData; -} - -void SoundCaptureResult::ResetData() -{ - _validData = false; - _maxAverage = 0; - _scaledAverage = 0; - _oldScaledAverage = 0; - - memset(pureResult, 0, sizeof(pureResult)); - memset(lastResult, 0, sizeof(lastResult)); - - memset(pureScaledResult, 0, sizeof(pureScaledResult)); - memset(buffScaledResult, 0, sizeof(buffScaledResult)); - memset(maxResult, 0, sizeof(maxResult)); - - for (int i = 0; i < SOUNDCAP_RESULT_RES; i++) - deltas[i] = 10; - - averageDelta = 10; + (*isMulti)--; + if (newAverage != nullptr) + *newAverage = resultFFT.hasMiddleAverage(*isMulti); - _lastPrevColor = QColor(0, 0, 0); - _currentMax = 0; + if (newSlow != nullptr) + *newSlow = resultFFT.hasMiddleSlow(*isMulti); + if (newFast != nullptr) + *newFast = resultFFT.hasMiddleFast(*isMulti); - mtWorking.Clear(); - mtInternal.Clear(); -} + effect->store(&resultFFT.mtWorking); -void SoundCaptureResult::GetBufResult(uint8_t* dest, size_t size) { - size_t c = std::min(size, sizeof(buffScaledResult)); - memcpy(dest, buffScaledResult, c); + return &resultFFT; } /* Code below based on work: @@ -775,103 +349,107 @@ void SoundCaptureResult::GetBufResult(uint8_t* dest, size_t size) { Since we only use 3/4 of N_WAVE, we define only this many samples, in order to conserve data space. */ -int16_t SoundCapture::_lutSin[SOUNDCAP_N_WAVE - SOUNDCAP_N_WAVE / 4] = { - 0, 201, 402, 603, 804, 1005, 1206, 1406, - 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, - 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, - 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, - 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, - 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, - 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, - 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, - 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, - 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, - 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, - 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, - 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, - 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, - 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, - 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, - 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, - 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, - 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, - 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, - 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, - 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, - 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, - 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, - 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, - 30851, 30918, 30984, 31049, 31113, 31175, 31236, 31297, - 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, - 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, - 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, - 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, - 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, - 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, - 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, - 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, - 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, - 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, - 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, - 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, - 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, - 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, - 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, - 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, - 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, - 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, - 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, - 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, - 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, - 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, - 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, - 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, - 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, - 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, - 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, - 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, - 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, - 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, - 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, - 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, - 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, - 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, - 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, - 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, - 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, - 1607, 1406, 1206, 1005, 804, 603, 402, 201, - 0, -201, -402, -603, -804, -1005, -1206, -1406, - -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, - -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, - -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, - -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, - -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, - -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, - -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, - -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, - -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, - -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, - -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, - -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, - -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, - -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, - -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, - -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, - -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, - -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, - -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, - -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, - -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, - -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, - -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, - -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, - -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, - -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, - -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, - -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, - -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, - -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, - -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, + + +namespace { + int16_t lutSin[SOUNDCAP_N_WAVE - SOUNDCAP_N_WAVE / 4] = { + 0, 201, 402, 603, 804, 1005, 1206, 1406, + 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, + 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, + 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, + 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, + 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, + 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, + 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, + 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, + 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, + 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, + 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, + 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, + 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, + 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, + 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, + 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, + 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, + 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, + 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, + 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, + 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, + 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, + 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, + 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, + 30851, 30918, 30984, 31049, 31113, 31175, 31236, 31297, + 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, + 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, + 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, + 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, + 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, + 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, + 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, + 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, + 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, + 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, + 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, + 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, + 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, + 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, + 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, + 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, + 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, + 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, + 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, + 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, + 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, + 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, + 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, + 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, + 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, + 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, + 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, + 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, + 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, + 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, + 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, + 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, + 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, + 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, + 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, + 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, + 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, + 1607, 1406, 1206, 1005, 804, 603, 402, 201, + 0, -201, -402, -603, -804, -1005, -1206, -1406, + -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, + -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, + -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, + -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, + -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, + -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, + -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, + -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, + -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, + -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, + -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, + -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, + -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, + -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, + -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, + -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, + -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, + -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, + -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, + -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, + -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, + -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, + -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, + -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, + -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, + -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, + -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, + -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, + -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, + -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, + -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, + }; }; /* @@ -976,8 +554,8 @@ int32_t SoundCapture::fix_fft(int16_t fr[], int16_t fi[], int16_t exp, bool inv { j = m << k; /* 0 <= j < N_WAVE/2 */ - wr = _lutSin[j + SOUNDCAP_N_WAVE / 4]; - wi = -_lutSin[j]; + wr = lutSin[j + SOUNDCAP_N_WAVE / 4]; + wi = -lutSin[j]; if (inverse) wi = -wi; diff --git a/sources/base/SoundCaptureResult.cpp b/sources/base/SoundCaptureResult.cpp new file mode 100644 index 000000000..0f3b8875b --- /dev/null +++ b/sources/base/SoundCaptureResult.cpp @@ -0,0 +1,400 @@ +#ifndef PCH_ENABLED + #include +#endif + +#include + +#define Limit(x,y) std::min(std::max(x, 0), y) +#define LimitMod(x,y) ((x+y) % y) +#define Drive(x,y) ((x == 0) ? x : ((x>=0 && x < y)? 1 : ( (x<0 && -x < y) ? -1: (x/y) ) )) + +int32_t SoundCaptureResult::getValue(int isMulti) +{ + if (isMulti == 2) + { + return Limit((_oldScaledAverage + _scaledAverage) / 2, 255); + } + else if (isMulti == 1) + { + return _scaledAverage; + } + + return -1; +} + +int32_t SoundCaptureResult::getValue3Step(int isMulti) +{ + if (isMulti == 2) + { + return Limit((_scaledAverage - _oldScaledAverage) / 3 + _oldScaledAverage, 255); + } + else if (isMulti == 1) + { + return Limit(((_scaledAverage - _oldScaledAverage) * 2) / 3 + _oldScaledAverage, 255); + } + else { + return _scaledAverage; + } +} + +SoundCaptureResult::SoundCaptureResult() +{ + ResetData(); + + color[0] = QColor(255, 0, 0); + color[1] = QColor(190, 0, 0); + color[2] = QColor(160, 160, 0); + color[3] = QColor(50, 190, 20); + color[4] = QColor(20, 255, 50); + color[5] = QColor(0, 212, 160); + color[6] = QColor(0, 120, 190); + color[7] = QColor(0, 0, 255); + +} + +QColor SoundCaptureResult::getRangeColor(uint8_t index) const +{ + if (index < SOUNDCAP_RESULT_RES) + return color[index]; + return color[0]; +} + +void SoundCaptureResult::ClearResult() +{ + memset(pureResult, 0, sizeof(pureResult)); +} + +void SoundCaptureResult::AddResult(int samplerIndex, uint32_t val) +{ + pureResult[samplerIndex] += val; +} + + + +void SoundCaptureResult::Smooth() +{ + int32_t currentAverage = 0, distance = 0, red = 0, green = 0, blue = 0; + + MovingTarget res = mtInternal; + + _validData = true; + + for (int i = 0; i < SOUNDCAP_RESULT_RES; i++) + { + int32_t buffResult = (pureResult[i] + lastResult[i]) / 2; + + if (maxResult[i] <= buffResult && buffResult > 400) + { + if (maxResult[i] == 0) + _validData = false; + + maxResult[i] = buffResult; + + for (int j = 0; j < SOUNDCAP_RESULT_RES; j++) + if (i != j && maxResult[j] < buffResult / 4 && maxResult[j] < 400) + maxResult[j] = buffResult / 4; + } + + + if (_validData && maxResult[i] > 0) + { + int32_t sr = (pureResult[i] * 255) / maxResult[i]; + + if (sr > 255) + sr = 255; + + pureScaledResult[i] = (uint8_t)sr; + + if (pureScaledResult[i] >= buffScaledResult[i] || buffScaledResult[i] - deltas[i] < pureScaledResult[i]) + { + buffScaledResult[i] = pureScaledResult[i]; + deltas[i] = std::max(pureScaledResult[i] / 10, 3); + } + else + { + buffScaledResult[i] -= deltas[i]; + deltas[i] = std::min((deltas[i] * 3) / 2, 255); + } + } + + lastResult[i] = pureResult[i]; + } + + for (int j = 0; j < SOUNDCAP_RESULT_RES; j++) + { + int32_t max = maxResult[j]; + if (max > 2000) + maxResult[j] = max - 2; + + currentAverage += buffScaledResult[j]; + } + + if (_validData) + { + if (currentAverage > _maxAverage) + _maxAverage = currentAverage; + + if (_maxAverage > 0) + { + _currentMax = 0; + for (int i = 0; i < SOUNDCAP_RESULT_RES; i++) + if (buffScaledResult[i] > _currentMax) + _currentMax = buffScaledResult[i]; + + currentAverage = std::min((currentAverage * 255) / _maxAverage, 255); + currentAverage = std::min((currentAverage + _currentMax) / 2, 255); + + _oldScaledAverage = _scaledAverage; + + if (currentAverage >= _scaledAverage || _scaledAverage - averageDelta < currentAverage) + { + _scaledAverage = currentAverage; + averageDelta = std::max(currentAverage / 14, 3); + } + else + { + _scaledAverage -= averageDelta; + averageDelta = std::min(averageDelta * 2, 255); + } + + + + int32_t r = 0, g = 0, b = 0; + for (int i = 0; i < SOUNDCAP_RESULT_RES && _currentMax > 0; i++) + { + int rr = 0, gg = 0, bb = 0; + + color[i].getRgb(&rr, &gg, &bb); + + + r += std::max((rr * (_currentMax - 5 * (_currentMax - buffScaledResult[i]))) / _currentMax, 0); + g += std::max((gg * (_currentMax - 5 * (_currentMax - buffScaledResult[i]))) / _currentMax, 0); + b += std::max((bb * (_currentMax - 5 * (_currentMax - buffScaledResult[i]))) / _currentMax, 0); + } + + int32_t ccScale = std::max(std::max(r, g), b); + if (ccScale != 0) + { + r = (Limit((r * 255) / ccScale + 32, 255) * _scaledAverage) / 255; + g = (Limit((g * 255) / ccScale + 32, 255) * _scaledAverage) / 255; + b = (Limit((b * 255) / ccScale + 32, 255) * _scaledAverage) / 255; + } + QColor currentColor(r, g, b); + + int av_r, av_g, av_b; + CalculateRgbDelta(currentColor, _lastPrevColor, res._averageColor, av_r, av_g, av_b); + + int sv_r, sv_g, sv_b; + CalculateRgbDelta(currentColor, _lastPrevColor, res._slowColor, sv_r, sv_g, sv_b); + + int fv_r, fv_g, fv_b; + CalculateRgbDelta(currentColor, _lastPrevColor, res._fastColor, fv_r, fv_g, fv_b); + + + _lastPrevColor = currentColor; + + if (res._targetFastCounter-- <= 0) + { + distance = Limit((int32_t)std::sqrt(fv_r * fv_r + fv_g * fv_g + fv_b * fv_b), 255); + distance = std::max(distance, 30) / 15; + + res._targetFastR = Drive(fv_r, distance); + res._targetFastG = Drive(fv_g, distance); + res._targetFastB = Drive(fv_b, distance); + + if (std::abs(res._targetFastR) >= 2 || std::abs(res._targetFastG) >= 2 || std::abs(res._targetFastB) >= 2) + res._targetFastCounter = 1; + else + res._targetFastCounter = 0; + } + + + red = Limit(res._targetFastR + res._fastColor.red(), 255); + green = Limit(res._targetFastG + res._fastColor.green(), 255); + blue = Limit(res._targetFastB + res._fastColor.blue(), 255); + res._fastColor = QColor::fromRgb(red, green, blue); + + if (res._targetSlowCounter-- <= 0) + { + distance = Limit((int32_t)std::sqrt(sv_r * sv_r + sv_g * sv_g + sv_b * sv_b), 255); + distance = std::max(distance, 16) / 8; + + res._targetSlowR = Drive(sv_r, distance); + res._targetSlowG = Drive(sv_g, distance); + res._targetSlowB = Drive(sv_b, distance); + + if (std::abs(res._targetSlowR) >= 3 || std::abs(res._targetSlowG) >= 3 || std::abs(res._targetSlowB) >= 3) + res._targetSlowCounter = 3; + else if (std::abs(res._targetSlowR) >= 2 || std::abs(res._targetSlowG) >= 2 || std::abs(res._targetSlowB) >= 2) + res._targetSlowCounter = 1; + else + res._targetSlowCounter = 0; + } + + + red = Limit(res._targetSlowR + res._slowColor.red(), 255); + green = Limit(res._targetSlowG + res._slowColor.green(), 255); + blue = Limit(res._targetSlowB + res._slowColor.blue(), 255); + res._slowColor = QColor::fromRgb(red, green, blue); + + + if (res._targetAverageCounter-- <= 0) + { + distance = Limit((int32_t)std::sqrt(av_r * av_r + av_g * av_g + av_b * av_b), 255); + distance = std::max(distance, 22) / 11; + + res._targetAverageR = Drive(av_r, distance); + res._targetAverageG = Drive(av_g, distance); + res._targetAverageB = Drive(av_b, distance); + + if (std::abs(res._targetAverageR) >= 3 || std::abs(res._targetAverageG) >= 3 || std::abs(res._targetAverageB) >= 3) + res._targetAverageCounter = 3; + else if (std::abs(res._targetAverageR) >= 2 || std::abs(res._targetAverageG) >= 2 || std::abs(res._targetAverageB) >= 2) + res._targetAverageCounter = 1; + else + res._targetAverageCounter = 0; + } + + + red = Limit(res._targetAverageR + res._averageColor.red(), 255); + green = Limit(res._targetAverageG + res._averageColor.green(), 255); + blue = Limit(res._targetAverageB + res._averageColor.blue(), 255); + res._averageColor = QColor::fromRgb(red, green, blue); + + } + + if (_maxAverage > 1024) + _maxAverage--; + } + + mtInternal = res; + mtWorking = res; + + if (_validData) + _resultIndex++; +} + +void SoundCaptureResult::CalculateRgbDelta(QColor currentColor, QColor prevColor, QColor selcolor, int& ab_r, int& ab_g, int& ab_b) +{ + ab_r = ((currentColor.red() * 2 + prevColor.red()) / 3) - selcolor.red(); + ab_g = ((currentColor.green() * 2 + prevColor.green()) / 3) - selcolor.green(); + ab_b = ((currentColor.blue() * 2 + prevColor.blue()) / 3) - selcolor.blue(); +} + +bool SoundCaptureResult::hasMiddleAverage(int middle) +{ + if (mtWorking._targetAverageCounter <= 0) + { + return false; + } + + int red = Limit(mtWorking._targetAverageR / 2 + mtWorking._averageColor.red(), 255); + int green = Limit(mtWorking._targetAverageG / 2 + mtWorking._averageColor.green(), 255); + int blue = Limit(mtWorking._targetAverageB / 2 + mtWorking._averageColor.blue(), 255); + + mtWorking._averageColor = QColor::fromRgb(red, green, blue); + + if (middle == 0) + mtWorking._targetAverageCounter--; + + return true; +} + +bool SoundCaptureResult::hasMiddleSlow(int middle) +{ + if (mtWorking._targetSlowCounter <= 0) + { + return false; + } + + int red = Limit(mtWorking._targetSlowR / 2 + mtWorking._slowColor.red(), 255); + int green = Limit(mtWorking._targetSlowG / 2 + mtWorking._slowColor.green(), 255); + int blue = Limit(mtWorking._targetSlowB / 2 + mtWorking._slowColor.blue(), 255); + + mtWorking._slowColor = QColor::fromRgb(red, green, blue); + + if (middle == 0) + mtWorking._targetSlowCounter--; + + return true; +} + +bool SoundCaptureResult::hasMiddleFast(int middle) +{ + if (mtWorking._targetFastCounter <= 0) + { + return false; + } + + int red = Limit(mtWorking._targetFastR / 2 + mtWorking._fastColor.red(), 255); + int green = Limit(mtWorking._targetFastG / 2 + mtWorking._fastColor.green(), 255); + int blue = Limit(mtWorking._targetFastB / 2 + mtWorking._fastColor.blue(), 255); + + mtWorking._fastColor = QColor::fromRgb(red, green, blue); + + if (middle == 0) + mtWorking._targetFastCounter--; + + return true; +} + + +void SoundCaptureResult::RestoreFullLum(QColor& color, int scale) +{ + int a, b, v; + color.getHsv(&a, &b, &v); + + if (v < scale) + color = QColor::fromHsv(a, b, scale); +} + +bool SoundCaptureResult::GetStats(uint32_t& scaledAverage, uint32_t& currentMax, QColor& averageColor, QColor* fastColor, QColor* slowColor) +{ + scaledAverage = _scaledAverage; + averageColor = mtWorking._averageColor; + currentMax = _currentMax; + if (fastColor != NULL) + *fastColor = mtWorking._fastColor; + if (slowColor != NULL) + *slowColor = mtWorking._slowColor; + return _validData; +} + +uint32_t SoundCaptureResult::getResultIndex() +{ + return _resultIndex; +} + +void SoundCaptureResult::ResetData() +{ + _validData = false; + _maxAverage = 0; + _scaledAverage = 0; + _oldScaledAverage = 0; + _resultIndex = 0; + + memset(pureResult, 0, sizeof(pureResult)); + memset(lastResult, 0, sizeof(lastResult)); + + memset(pureScaledResult, 0, sizeof(pureScaledResult)); + memset(buffScaledResult, 0, sizeof(buffScaledResult)); + memset(maxResult, 0, sizeof(maxResult)); + + for (int i = 0; i < SOUNDCAP_RESULT_RES; i++) + deltas[i] = 10; + + averageDelta = 10; + + + _lastPrevColor = QColor(0, 0, 0); + _currentMax = 0; + + + mtWorking.Clear(); + mtInternal.Clear(); +} + +void SoundCaptureResult::GetBufResult(uint8_t* dest, size_t size) { + size_t c = std::min(size, sizeof(buffScaledResult)); + memcpy(dest, buffScaledResult, c); +} diff --git a/sources/base/SystemControl.cpp b/sources/base/SystemControl.cpp index 1c47bf70e..8bc934c19 100644 --- a/sources/base/SystemControl.cpp +++ b/sources/base/SystemControl.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,17 +25,17 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include +#endif + #include #include #include - #include -// qt includes -#include - SystemControl::SystemControl(HyperHdrInstance* hyperhdr) - : QObject() + : QObject(hyperhdr) , _hyperhdr(hyperhdr) , _sysCaptEnabled(false) , _alive(false) @@ -45,19 +45,30 @@ SystemControl::SystemControl(HyperHdrInstance* hyperhdr) , _isCEC(false) { // settings changes - connect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &SystemControl::handleSettingsUpdate); + connect(_hyperhdr, &HyperHdrInstance::SignalInstanceSettingsChanged, this, &SystemControl::handleSettingsUpdate); // comp changes - connect(_hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &SystemControl::handleCompStateChangeRequest); + connect(_hyperhdr, &HyperHdrInstance::SignalRequestComponent, this, &SystemControl::handleCompStateChangeRequest); // inactive timer system grabber - connect(&_sysInactiveTimer, &QTimer::timeout, this, &SystemControl::setSysInactive); - _sysInactiveTimer.setInterval(800); + connect(_sysInactiveTimer, &QTimer::timeout, this, &SystemControl::setSysInactive); + _sysInactiveTimer->setInterval(800); // init - handleSettingsUpdate(settings::type::SYSTEMCONTROL, _hyperhdr->getSetting(settings::type::SYSTEMCONTROL)); + QJsonDocument settings = _hyperhdr->getSetting(settings::type::SYSTEMCONTROL); + QUEUE_CALL_2(this, handleSettingsUpdate, settings::type, settings::type::SYSTEMCONTROL, QJsonDocument, settings); +} + +SystemControl::~SystemControl() +{ + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_SYSTEMGRABBER, int(_hyperhdr->getInstanceIndex()), false); - connect(this, &SystemControl::setSysCaptureEnableSignal, this, &SystemControl::setSysCaptureEnable); + std::cout << "SystemControl exists now" << std::endl; +} + +quint8 SystemControl::getCapturePriority() +{ + return _sysCaptPrio; } bool SystemControl::isCEC() @@ -75,8 +86,8 @@ void SystemControl::handleSysImage(const QString& name, const Image& i _alive = true; - if (!_sysInactiveTimer.isActive() && _sysInactiveTimer.remainingTime() < 0) - _sysInactiveTimer.start(); + if (!_sysInactiveTimer->isActive() && _sysInactiveTimer->remainingTime() < 0) + _sysInactiveTimer->start(); _hyperhdr->setInputImage(_sysCaptPrio, image); } @@ -88,21 +99,19 @@ void SystemControl::setSysCaptureEnable(bool enable) if (enable) { _hyperhdr->registerInput(_sysCaptPrio, hyperhdr::COMP_SYSTEMGRABBER); - connect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, &SystemControl::handleSysImage); - connect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, _hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewSystemImage, this, &SystemControl::handleSysImage, Qt::UniqueConnection); } else { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, &SystemControl::handleSysImage); - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, _hyperhdr, &HyperHdrInstance::forwardSystemProtoMessage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewSystemImage, this, &SystemControl::handleSysImage); _hyperhdr->clear(_sysCaptPrio); - _sysInactiveTimer.stop(); + _sysInactiveTimer->stop(); _sysCaptName = ""; } _sysCaptEnabled = enable; _hyperhdr->setNewComponentState(hyperhdr::COMP_SYSTEMGRABBER, enable); - emit GlobalSignals::getInstance()->requestSource(hyperhdr::COMP_SYSTEMGRABBER, int(_hyperhdr->getInstanceIndex()), enable); + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_SYSTEMGRABBER, int(_hyperhdr->getInstanceIndex()), enable); } } @@ -117,12 +126,9 @@ void SystemControl::handleSettingsUpdate(settings::type type, const QJsonDocumen _sysCaptPrio = obj["systemInstancePriority"].toInt(245); } - bool enabledCurrent = obj["systemInstanceEnable"].toBool(false); - setSysCaptureEnable(enabledCurrent); + setSysCaptureEnable(obj["systemInstanceEnable"].toBool(false)); _isCEC = obj["cecControl"].toBool(false); - - if (!enabledCurrent && SystemWrapper::getInstance() != nullptr) - SystemWrapper::getInstance()->stateChanged(false); + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_CEC, -int(_hyperhdr->getInstanceIndex()) - 2, _isCEC); } } diff --git a/sources/base/SystemWrapper.cpp b/sources/base/SystemWrapper.cpp index 910ce7d40..39aa7236e 100644 --- a/sources/base/SystemWrapper.cpp +++ b/sources/base/SystemWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,18 +25,17 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include + #include +#endif + +#include #include #include -#include - #include #include -#include - -#include -#include - -SystemWrapper* SystemWrapper::instance = nullptr; +#include SystemWrapper::SystemWrapper(const QString& grabberName, Grabber* ggrabber) : _grabberName(grabberName) @@ -44,19 +43,17 @@ SystemWrapper::SystemWrapper(const QString& grabberName, Grabber* ggrabber) , _configLoaded(false) , _grabber(ggrabber) { - SystemWrapper::instance = this; - // connect the image forwarding - connect(this, &SystemWrapper::systemImage, GlobalSignals::getInstance(), &GlobalSignals::setSystemImage); + connect(this, &SystemWrapper::SignalSystemImage, GlobalSignals::getInstance(), &GlobalSignals::SignalNewSystemImage); // listen for source requests - connect(GlobalSignals::getInstance(), &GlobalSignals::requestSource, this, &SystemWrapper::handleSourceRequest); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalRequestComponent, this, &SystemWrapper::signalRequestSourceHandler); } -void SystemWrapper::newFrame(const Image& image) +void SystemWrapper::newCapturedFrameHandler(const Image& image) { - emit systemImage(_grabberName, image); + emit SignalSystemImage(_grabberName, image); } void SystemWrapper::stateChanged(bool state) @@ -64,7 +61,7 @@ void SystemWrapper::stateChanged(bool state) } -void SystemWrapper::readError(const char* err) +void SystemWrapper::capturingExceptionHandler(const char* err) { Error(_log, "Grabber signals error (%s)", err); } @@ -74,7 +71,7 @@ SystemWrapper::~SystemWrapper() Debug(_log, "Closing grabber: %s", QSTRING_CSTR(_grabberName)); } -void SystemWrapper::handleSourceRequest(hyperhdr::Components component, int hyperhdrInd, bool listen) +void SystemWrapper::signalRequestSourceHandler(hyperhdr::Components component, int hyperhdrInd, bool listen) { if (component == hyperhdr::Components::COMP_SYSTEMGRABBER) { @@ -164,12 +161,15 @@ void SystemWrapper::handleSettingsUpdate(settings::type type, const QJsonDocumen #ifdef ENABLE_DX // HDR tone mapping setHdrToneMappingEnabled(obj["hdrToneMapping"].toBool(false) ? 1 : 0); - _grabber->alternativeCaching(obj["alternativeCaching"].toBool(false)); #endif // signal _grabber->setSignalDetectionEnable(obj["signalDetection"].toBool(false)); + _grabber->enableHardwareAcceleration(obj["hardware"].toBool(false)); + + _grabber->setMonitorNits(obj["monitor_nits"].toInt(200)); + _grabber->setSignalDetectionOffset( obj["sDHOffsetMin"].toDouble(0.25), obj["sDVOffsetMin"].toDouble(0.25), diff --git a/sources/base/VideoControl.cpp b/sources/base/VideoControl.cpp index 6292f7c46..dd3a31853 100644 --- a/sources/base/VideoControl.cpp +++ b/sources/base/VideoControl.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,13 +25,16 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include +#endif + #include #include #include +#include #include -#include - bool VideoControl::_stream = false; VideoControl::VideoControl(HyperHdrInstance* hyperhdr) @@ -45,20 +48,27 @@ VideoControl::VideoControl(HyperHdrInstance* hyperhdr) , _isCEC(false) { // settings changes - connect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &VideoControl::handleSettingsUpdate); + connect(_hyperhdr, &HyperHdrInstance::SignalInstanceSettingsChanged, this, &VideoControl::handleSettingsUpdate); // comp changes - connect(_hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &VideoControl::handleCompStateChangeRequest); + connect(_hyperhdr, &HyperHdrInstance::SignalRequestComponent, this, &VideoControl::handleCompStateChangeRequest); // inactive timer usb grabber - connect(&_usbInactiveTimer, &QTimer::timeout, this, &VideoControl::setUsbInactive); + connect(_usbInactiveTimer, &QTimer::timeout, this, &VideoControl::setUsbInactive); - _usbInactiveTimer.setInterval(800); + _usbInactiveTimer->setInterval(800); // init - handleSettingsUpdate(settings::type::VIDEOCONTROL, _hyperhdr->getSetting(settings::type::VIDEOCONTROL)); + QJsonDocument settings = _hyperhdr->getSetting(settings::type::VIDEOCONTROL); + QUEUE_CALL_2(this, handleSettingsUpdate, settings::type, settings::type::VIDEOCONTROL, QJsonDocument, settings); +} + +VideoControl::~VideoControl() +{ + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_VIDEOGRABBER, int(_hyperhdr->getInstanceIndex()), false); + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_CEC, int(_hyperhdr->getInstanceIndex()), false); - connect(this, &VideoControl::setUsbCaptureEnableSignal, this, &VideoControl::setUsbCaptureEnable); + std::cout << "VideoControl exists now" << std::endl; } bool VideoControl::isCEC() @@ -66,8 +76,35 @@ bool VideoControl::isCEC() return _isCEC; } -void VideoControl::handleUsbImage(const QString& name, const Image& image) +quint8 VideoControl::getCapturePriority() +{ + return _usbCaptPrio; +} + +void VideoControl::handleIncomingUsbImage(const QString& name, const Image& image) { + QMutexLocker locker(&incoming.mutex); + incoming.frame = image; + incoming.name = name; + QUEUE_CALL_0(this, handleUsbImage); +} + +void VideoControl::handleUsbImage() +{ + Image image; + QString name; + + incoming.mutex.lock(); + { + name = incoming.name; + image = incoming.frame; + incoming.frame = Image(); + } + incoming.mutex.unlock(); + + if (image.width() <= 1 || image.height() <=1) + return; + _stream = true; if (_usbCaptName != name) @@ -78,8 +115,8 @@ void VideoControl::handleUsbImage(const QString& name, const Image& im _alive = true; - if (!_usbInactiveTimer.isActive() && _usbInactiveTimer.remainingTime() < 0) - _usbInactiveTimer.start(); + if (!_usbInactiveTimer->isActive() && _usbInactiveTimer->remainingTime() < 0) + _usbInactiveTimer->start(); _hyperhdr->setInputImage(_usbCaptPrio, image); } @@ -91,21 +128,19 @@ void VideoControl::setUsbCaptureEnable(bool enable) if (enable) { _hyperhdr->registerInput(_usbCaptPrio, hyperhdr::COMP_VIDEOGRABBER); - connect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, this, &VideoControl::handleUsbImage); - connect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, _hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewVideoImage, this, &VideoControl::handleIncomingUsbImage, static_cast(Qt::DirectConnection | Qt::UniqueConnection)); } else { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, this, &VideoControl::handleUsbImage); - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, _hyperhdr, &HyperHdrInstance::forwardV4lProtoMessage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewVideoImage, this, &VideoControl::handleIncomingUsbImage); _hyperhdr->clear(_usbCaptPrio); - _usbInactiveTimer.stop(); + _usbInactiveTimer->stop(); _usbCaptName = ""; } _usbCaptEnabled = enable; _hyperhdr->setNewComponentState(hyperhdr::COMP_VIDEOGRABBER, enable); - emit GlobalSignals::getInstance()->requestSource(hyperhdr::COMP_VIDEOGRABBER, int(_hyperhdr->getInstanceIndex()), enable); + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_VIDEOGRABBER, int(_hyperhdr->getInstanceIndex()), enable); } } @@ -122,6 +157,7 @@ void VideoControl::handleSettingsUpdate(settings::type type, const QJsonDocument setUsbCaptureEnable(obj["videoInstanceEnable"].toBool(true)); _isCEC = obj["cecControl"].toBool(false); + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::COMP_CEC, int(_hyperhdr->getInstanceIndex()), _isCEC); } } @@ -142,8 +178,10 @@ void VideoControl::setUsbInactive() if (_stream) { _stream = false; - if (GrabberWrapper::getInstance() != nullptr) - GrabberWrapper::getInstance()->revive(); + std::shared_ptr grabberHelper; + emit GlobalSignals::getInstance()->SignalGetVideoGrabber(grabberHelper); + if (grabberHelper != nullptr && grabberHelper->grabberWrapper() != nullptr) + QUEUE_CALL_0(grabberHelper->grabberWrapper(), revive); } } diff --git a/sources/base/schema/schema-color.json b/sources/base/schema/schema-color.json index 902c1a65a..68d14c2de 100644 --- a/sources/base/schema/schema-color.json +++ b/sources/base/schema/schema-color.json @@ -205,7 +205,7 @@ "required" : true, "minimum" : 0, "maximum": 100, - "default" : 2, + "default" : 1, "append" : "edt_append_percent", "propertyOrder" : 11 }, diff --git a/sources/base/schema/schema-general.json b/sources/base/schema/schema-general.json index 6b51c751a..c13a9125c 100644 --- a/sources/base/schema/schema-general.json +++ b/sources/base/schema/schema-general.json @@ -26,7 +26,7 @@ "version" : { "type" : "integer", - "default" : 1, + "default" : 2, "required" : true, "options" : { "hidden":true diff --git a/sources/base/schema/schema-mqtt.json b/sources/base/schema/schema-mqtt.json index ed90ad8a5..908ee8610 100644 --- a/sources/base/schema/schema-mqtt.json +++ b/sources/base/schema/schema-mqtt.json @@ -65,6 +65,26 @@ } }, "propertyOrder" : 7 + }, + "custom_topic" : + { + "type": "string", + "title":"edt_conf_mqtt_custom_topic_title", + "required" : true, + "default" : "", + "propertyOrder" : 8 + }, + "maxRetry": + { + "type" : "integer", + "format" : "stepper", + "step" : 1, + "title" : "edt_dev_max_retry", + "minimum" : 0, + "maximum" : 120, + "default" : 0, + "required" : true, + "propertyOrder" : 9 } }, "additionalProperties" : false diff --git a/sources/base/schema/schema-systemGrabber.json b/sources/base/schema/schema-systemGrabber.json index 366c4e23d..8fcbe7ed6 100644 --- a/sources/base/schema/schema-systemGrabber.json +++ b/sources/base/schema/schema-systemGrabber.json @@ -19,7 +19,7 @@ "title" : "edt_conf_stream_max_resolution_title", "default" : 512, "minimum" : 196, - "maximum" : 960, + "maximum" : 1280, "append" : "edt_append_pixel", "required" : true, "propertyOrder" : 7 @@ -31,7 +31,7 @@ "title" : "edt_conf_stream_framerate_title", "default" : 10, "minimum" : 5, - "maximum" : 60, + "maximum" : 120, "append" : "fps", "required" : true, "propertyOrder" : 10 @@ -45,15 +45,32 @@ "required" : true, "propertyOrder" : 23 }, - "alternativeCaching" : + "hardware" : { "type" : "boolean", "format": "checkbox", - "title" : "edt_conf_stream_dx11_flickering_title", - "default" : false, + "title" : "edt_conf_hardware_title", + "default" : true, "required" : true, "propertyOrder" : 24 - }, + }, + "monitor_nits" : + { + "type" : "integer", + "format": "stepper", + "title" : "edt_conf_monitor_nits_title", + "default" : 200, + "minimum" : 0, + "maximum" : 10000, + "append" : "edt_append_nits", + "required" : true, + "options": { + "dependencies": { + "hardware": true + } + }, + "propertyOrder" : 25 + }, "cropLeft" : { "type" : "integer", diff --git a/sources/base/schema/schema-videoDetection.json b/sources/base/schema/schema-videoDetection.json index 104a245c0..0ac60646f 100644 --- a/sources/base/schema/schema-videoDetection.json +++ b/sources/base/schema/schema-videoDetection.json @@ -32,12 +32,9 @@ "calibration_sdr" : { "type" : "array", - "required" : true, - "default" : [], "items" : { "type" : "object", - "required" : true, "properties": { "x" : @@ -71,12 +68,9 @@ "calibration_hdr" : { "type" : "array", - "required" : true, - "default" : [], "items" : { "type" : "object", - "required" : true, "properties": { "x" : diff --git a/sources/base/schema/schema-videoGrabber.json b/sources/base/schema/schema-videoGrabber.json index 856f4ef3b..308c2a1ef 100644 --- a/sources/base/schema/schema-videoGrabber.json +++ b/sources/base/schema/schema-videoGrabber.json @@ -465,18 +465,6 @@ "required" : true, "propertyOrder" : 69 }, - "videoCache" : - { - "type" : "boolean", - "format": "checkbox", - "title" : "edt_conf_video_cache_title", - "default" : true, - "required" : true, - "options": { - "hidden":true - }, - "propertyOrder" : 70 - }, "led_off_pause" : { "type" : "boolean", diff --git a/sources/blackborder/BlackBorderProcessor.cpp b/sources/blackborder/BlackBorderProcessor.cpp index 6a40cdeeb..99ba91990 100644 --- a/sources/blackborder/BlackBorderProcessor.cpp +++ b/sources/blackborder/BlackBorderProcessor.cpp @@ -9,14 +9,13 @@ using namespace hyperhdr; BlackBorderProcessor::BlackBorderProcessor(HyperHdrInstance* hyperhdr, QObject* parent) : QObject(parent) - , _hyperhdr(hyperhdr) , _enabled(false) , _unknownSwitchCnt(600) , _borderSwitchCnt(50) , _maxInconsistentCnt(10) , _blurRemoveCnt(1) , _detectionMode("default") - , _detector(nullptr) + , _borderDetector(nullptr) , _currentBorder({ true, -1, -1 }) , _previousDetectedBorder({ true, -1, -1 }) , _consistentCnt(0) @@ -25,19 +24,17 @@ BlackBorderProcessor::BlackBorderProcessor(HyperHdrInstance* hyperhdr, QObject* , _hardDisabled(false) , _userEnabled(false) { - // init - handleSettingsUpdate(settings::type::BLACKBORDER, _hyperhdr->getSetting(settings::type::BLACKBORDER)); + connect(this, &BlackBorderProcessor::setNewComponentState, hyperhdr, &HyperHdrInstance::setNewComponentState); - // listen for settings updates - connect(_hyperhdr, &HyperHdrInstance::settingsChanged, this, &BlackBorderProcessor::handleSettingsUpdate); + handleSettingsUpdate(settings::type::BLACKBORDER, hyperhdr->getSetting(settings::type::BLACKBORDER)); - // listen for component state changes - connect(_hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest); + connect(hyperhdr, &HyperHdrInstance::SignalInstanceSettingsChanged, this, &BlackBorderProcessor::handleSettingsUpdate); + connect(hyperhdr, &HyperHdrInstance::SignalRequestComponent, this, &BlackBorderProcessor::handleCompStateChangeRequest); } BlackBorderProcessor::~BlackBorderProcessor() { - delete _detector; + _borderDetector = nullptr; } void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJsonDocument& config) @@ -56,9 +53,7 @@ void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJson { _oldThreshold = newThreshold; - delete _detector; - - _detector = new BlackBorderDetector(newThreshold); + _borderDetector = std::unique_ptr(new BlackBorderDetector(newThreshold)); } Info(Logger::getInstance("BLACKBORDER"), "Set mode to: %s", QSTRING_CSTR(_detectionMode)); @@ -84,7 +79,7 @@ void BlackBorderProcessor::handleCompStateChangeRequest(hyperhdr::Components com _enabled = enable; } - _hyperhdr->setNewComponentState(hyperhdr::COMP_BLACKBORDER, enable); + emit setNewComponentState(hyperhdr::COMP_BLACKBORDER, enable); } } @@ -198,17 +193,20 @@ bool BlackBorderProcessor::process(const Image& image) return true; } + if (_borderDetector == nullptr) + return false; + if (_detectionMode == "default") { - imageBorder = _detector->process(image); + imageBorder = _borderDetector->process(image); } else if (_detectionMode == "classic") { - imageBorder = _detector->process_classic(image); + imageBorder = _borderDetector->process_classic(image); } else if (_detectionMode == "osd") { - imageBorder = _detector->process_osd(image); + imageBorder = _borderDetector->process_osd(image); } else if (_detectionMode == "letterbox") { - imageBorder = _detector->process_letterbox(image); + imageBorder = _borderDetector->process_letterbox(image); } // add blur to the border if (imageBorder.horizontalSize > 0) diff --git a/sources/boblightserver/BoblightClientConnection.cpp b/sources/boblightserver/BoblightClientConnection.cpp index 28562d308..7c08cdffb 100644 --- a/sources/boblightserver/BoblightClientConnection.cpp +++ b/sources/boblightserver/BoblightClientConnection.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include @@ -24,11 +24,36 @@ // project includes #include "BoblightClientConnection.h" +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + #include +#else + #include +#endif + + +namespace QStringUtils { + #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + inline QList REFSPLITTER(const QString& string, QChar sep) + { + return QStringView{ string }.split(sep, Qt::SplitBehaviorFlags::SkipEmptyParts); + } + #elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + inline QVector REFSPLITTER(const QString& string, QChar sep) + { + return string.splitRef(sep, Qt::SkipEmptyParts); + } + #else + inline QVector REFSPLITTER(const QString& string, QChar sep) + { + return string.splitRef(sep, QString::SkipEmptyParts); + } +#endif +} + BoblightClientConnection::BoblightClientConnection(HyperHdrInstance* hyperhdr, QTcpSocket* socket, int priority) : QObject() , _locale(QLocale::C) , _socket(socket) - , _imageProcessor(hyperhdr->getImageProcessor()) , _hyperhdr(hyperhdr) , _receiveBuffer() , _priority(priority) @@ -47,7 +72,7 @@ BoblightClientConnection::BoblightClientConnection(HyperHdrInstance* hyperhdr, Q BoblightClientConnection::~BoblightClientConnection() { // clear the current channel - if (_priority != 0 && _priority >= 128 && _priority < PriorityMuxer::LOWEST_EFFECT_PRIORITY) + if (_priority != 0 && _priority >= 128 && _priority < Muxer::LOWEST_EFFECT_PRIORITY) _hyperhdr->clear(_priority); delete _socket; @@ -176,7 +201,7 @@ void BoblightClientConnection::handleMessage(const QString& message) // send current color values to HyperHDR if this is the last led assuming leds values are send in order of id if (ledIndex == _ledColors.size() - 1) { - _hyperhdr->setInput(_priority, _ledColors); + _hyperhdr->setColor(_priority, _ledColors); } return; @@ -198,13 +223,13 @@ void BoblightClientConnection::handleMessage(const QString& message) const int prio = static_cast(parseUInt(messageParts[2], &rc)); if (rc && prio != _priority) { - if (_priority != 0 && _hyperhdr->getPriorityInfo(_priority).componentId == hyperhdr::COMP_BOBLIGHTSERVER) + if (_priority != 0 && _hyperhdr->getComponentForPriority(_priority) == hyperhdr::COMP_BOBLIGHTSERVER) _hyperhdr->clear(_priority); - if (prio < 128 || prio >= 254) + if (prio < 128 || prio >= Muxer::LOWEST_PRIORITY) { _priority = 128; - while (_hyperhdr->getActivePriorities().contains(_priority)) + while (_hyperhdr->getComponentForPriority(_priority) != hyperhdr::COMP_COLOR && _priority < Muxer::LOWEST_PRIORITY - 1) { _priority += 1; } @@ -228,7 +253,7 @@ void BoblightClientConnection::handleMessage(const QString& message) else if (messageParts[0] == QStringLiteral("sync")) { if (_priority >= 128 && _priority < 254) - _hyperhdr->setInput(_priority, _ledColors); // send current color values to HyperHDR + _hyperhdr->setColor(_priority, _ledColors); // send current color values to HyperHDR return; } @@ -387,7 +412,7 @@ void BoblightClientConnection::sendLightMessage() double h0, h1, v0, v1; for (int i = 0; i < _hyperhdr->getLedCount(); ++i) { - _imageProcessor->getScanParameters(i, h0, h1, v0, v1); + _hyperhdr->getScanParameters(i, h0, h1, v0, v1); n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100 * v0, 100 * v1, 100 * h0, 100 * h1); sendMessage(QByteArray(buffer, n)); } diff --git a/sources/boblightserver/BoblightClientConnection.h b/sources/boblightserver/BoblightClientConnection.h index f49b9e4b5..33060b7b5 100644 --- a/sources/boblightserver/BoblightClientConnection.h +++ b/sources/boblightserver/BoblightClientConnection.h @@ -119,9 +119,6 @@ private slots: /// The TCP-Socket that is connected tot the boblight-client QTcpSocket* _socket; - /// The processor for translating images to led-values - ImageProcessor* _imageProcessor; - /// Link to HyperHDR for writing led-values to a priority channel HyperHdrInstance* _hyperhdr; diff --git a/sources/boblightserver/BoblightServer.cpp b/sources/boblightserver/BoblightServer.cpp index c316c5313..12ed3c1cc 100644 --- a/sources/boblightserver/BoblightServer.cpp +++ b/sources/boblightserver/BoblightServer.cpp @@ -27,7 +27,7 @@ BoblightServer::BoblightServer(HyperHdrInstance* hyperhdr, const QJsonDocument& Info(_log, "Instance created"); // listen for component change - connect(_hyperhdr, &HyperHdrInstance::compStateChangeRequest, this, &BoblightServer::compStateChangeRequest); + connect(_hyperhdr, &HyperHdrInstance::SignalRequestComponent, this, &BoblightServer::compStateChangeRequest); // listen new connection signal from server connect(_server, &QTcpServer::newConnection, this, &BoblightServer::newConnection); diff --git a/sources/bonjour/bonjourservicehelper.cpp b/sources/bonjour/BonjourServiceHelper.cpp similarity index 96% rename from sources/bonjour/bonjourservicehelper.cpp rename to sources/bonjour/BonjourServiceHelper.cpp index d1d4f5cab..485f67c94 100644 --- a/sources/bonjour/bonjourservicehelper.cpp +++ b/sources/bonjour/BonjourServiceHelper.cpp @@ -3,8 +3,8 @@ #endif #include -#include -#include +#include +#include #include #include @@ -379,18 +379,18 @@ int BonjourServiceHelper::service_mdns(QString hostname, QString serviceName, in Info(_log, "Starting the network discovery thread"); QByteArray mainBuffer(2048, 0); - QByteArray serviceNameBuf = serviceName.toLocal8Bit(); - QByteArray hostnameBuf = hostname.toLocal8Bit(); + const QByteArray& serviceNameBuf = serviceName.toLocal8Bit(); + const QByteArray& hostnameBuf = hostname.toLocal8Bit(); mdns_string_t service_string = { serviceNameBuf.data(), strlen(serviceNameBuf.data()) }; mdns_string_t hostname_string = { hostnameBuf.data(), strlen(hostnameBuf.data()) }; // Build the service instance ".<_service-name>._tcp.local." string - QByteArray serviceInstanceBuf = QString::asprintf("%.*s:%i.%.*s", MDNS_STRING_FORMAT(hostname_string), service_port, MDNS_STRING_FORMAT(service_string)).toLocal8Bit(); + const QByteArray& serviceInstanceBuf = QString::asprintf("%.*s:%i.%.*s", MDNS_STRING_FORMAT(hostname_string), service_port, MDNS_STRING_FORMAT(service_string)).toLocal8Bit(); mdns_string_t service_instance_string = { serviceInstanceBuf.data(), strlen(serviceInstanceBuf.data()) }; // Build the ".local." string - QByteArray qualifiedHostnameBuf = QString::asprintf("%.*s.local.", MDNS_STRING_FORMAT(hostname_string)).toLocal8Bit(); + const QByteArray& qualifiedHostnameBuf = QString::asprintf("%.*s.local.", MDNS_STRING_FORMAT(hostname_string)).toLocal8Bit(); mdns_string_t hostname_qualified_string = { qualifiedHostnameBuf.data(), strlen(qualifiedHostnameBuf.data()) }; service_t service = {}; @@ -512,7 +512,7 @@ int BonjourServiceHelper::service_mdns(QString hostname, QString serviceName, in { _scanService &= ~(1 << scanner); - QByteArray name = (DiscoveryRecord::getmDnsHeader(scanner) + ".local").toLocal8Bit(); + const QByteArray& name = (DiscoveryRecord::getmDnsHeader(scanner) + ".local").toLocal8Bit(); mdns_query_t serviceQuery{ MDNS_RECORDTYPE_PTR, name.data(), strlen(name.data()) }; for (int i = 0; i < num_sockets; i++) @@ -565,7 +565,7 @@ int BonjourServiceHelper::serviceCallback(int sock, const struct sockaddr* from, if (user_data != nullptr) { BonjourServiceRegister* receive = service->parent->_register; - emit receive->messageFromFriend(ttl > 0, QString::fromLocal8Bit(name.str, name.length), QString::fromLocal8Bit(srv.name.str, srv.name.length), srv.port); + emit receive->SignalMessageFromFriend(ttl > 0, QString::fromLocal8Bit(name.str, (int) name.length), QString::fromLocal8Bit(srv.name.str, (int) srv.name.length), srv.port); } } else if (rtype == MDNS_RECORDTYPE_A) { @@ -576,7 +576,7 @@ int BonjourServiceHelper::serviceCallback(int sock, const struct sockaddr* from, if (user_data != nullptr) { BonjourServiceRegister* receive = service->parent->_register; - emit receive->resolveIp(QString::fromLocal8Bit(name.str, name.length), addrstr); + emit receive->SignalIpResolved(QString::fromLocal8Bit(name.str, (int) name.length), addrstr); } } return 0; diff --git a/sources/bonjour/bonjourserviceregister.cpp b/sources/bonjour/BonjourServiceRegister.cpp similarity index 79% rename from sources/bonjour/bonjourserviceregister.cpp rename to sources/bonjour/BonjourServiceRegister.cpp index 34b1b80e9..45819aab7 100644 --- a/sources/bonjour/bonjourserviceregister.cpp +++ b/sources/bonjour/BonjourServiceRegister.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,22 +26,27 @@ */ #include -#include +#include #include +#include #include +#include #include +#define DEFAULT_RETRY 20 + BonjourServiceRegister::BonjourServiceRegister(QObject* parent, DiscoveryRecord::Service type, int port) : QObject(parent), - _helper(new BonjourServiceHelper(this, DiscoveryRecord::getmDnsHeader(type), port)) + _helper(new BonjourServiceHelper(this, DiscoveryRecord::getmDnsHeader(type), port)), + _retry(DEFAULT_RETRY) { _serviceRecord.port = port; _serviceRecord.type = type; connect(_helper, &QThread::finished, this, &BonjourServiceRegister::onThreadExits); - connect(this, &BonjourServiceRegister::messageFromFriend, this, &BonjourServiceRegister::messageFromFriendHandler); - connect(this, &BonjourServiceRegister::resolveIp, this, &BonjourServiceRegister::resolveIpHandler); - connect(DiscoveryWrapper::getInstance(), &DiscoveryWrapper::requestToScan, this, &BonjourServiceRegister::requestToScanHandler); + connect(this, &BonjourServiceRegister::SignalMessageFromFriend, this, &BonjourServiceRegister::messageFromFriendHandler); + connect(this, &BonjourServiceRegister::SignalIpResolved, this, &BonjourServiceRegister::signalIpResolvedHandler); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalDiscoveryRequestToScan, this, &BonjourServiceRegister::requestToScanHandler); } BonjourServiceRegister::~BonjourServiceRegister() @@ -59,14 +64,20 @@ BonjourServiceRegister::~BonjourServiceRegister() void BonjourServiceRegister::onThreadExits() { - if (_helper != nullptr) - QTimer::singleShot(10000, this, [=]() { if (_helper != nullptr) _helper->start(); }); + if (_helper != nullptr && _retry > 0) + { + _retry--; + QTimer::singleShot(15000, this, [this]() { + if (_helper != nullptr && _retry > 0) + _helper->start(); + }); + } } void BonjourServiceRegister::registerService() { QTimer::singleShot(1500, [this]() { - emit DiscoveryWrapper::getInstance()->requestToScan(DiscoveryRecord::Service::SerialPort); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::SerialPort); if (_helper != nullptr) _helper->start(); }); } @@ -109,11 +120,12 @@ void BonjourServiceRegister::messageFromFriendHandler(bool isExists, QString mdn } _result = newRecord; + _retry = DEFAULT_RETRY; resolveIps(); } -void BonjourServiceRegister::resolveIpHandler(QString serverName, QString ip) +void BonjourServiceRegister::signalIpResolvedHandler(QString serverName, QString ip) { if (serverName.length() > 0 && serverName[serverName.length() - 1] == '.') { @@ -138,7 +150,7 @@ void BonjourServiceRegister::resolveIps() if (!_result.address.isEmpty()) { - emit DiscoveryWrapper::getInstance()->discoveryEvent(_result); + emit GlobalSignals::getInstance()->SignalDiscoveryEvent(_result); } } } diff --git a/sources/bonjour/CMakeLists.txt b/sources/bonjour/CMakeLists.txt index 836574dc8..d916dfe5e 100644 --- a/sources/bonjour/CMakeLists.txt +++ b/sources/bonjour/CMakeLists.txt @@ -7,7 +7,7 @@ FILE ( GLOB Bonjour_SOURCES "${CURRENT_HEADER_DIR}/*.h" add_library(bonjour ${Bonjour_SOURCES} ) -target_include_directories(bonjour PUBLIC ${CMAKE_SOURCE_DIR}/dependencies/external/mdns) +target_include_directories(bonjour PUBLIC ${CMAKE_SOURCE_DIR}/external/mdns) target_link_libraries(bonjour hyperhdr-base diff --git a/sources/bonjour/DiscoveryRecord.cpp b/sources/bonjour/DiscoveryRecord.cpp new file mode 100644 index 000000000..f32f9e181 --- /dev/null +++ b/sources/bonjour/DiscoveryRecord.cpp @@ -0,0 +1,98 @@ +/* DiscoveryRecord.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +#include + + +const QString DiscoveryRecord::getmDnsHeader(Service service) +{ + switch (service) + { + case(Service::PhilipsHue): return QLatin1String("_hue._tcp"); break; + case(Service::WLED): return QLatin1String("_wled._tcp"); break; + case(Service::HyperHDR): return QLatin1String("_hyperhdr-http._tcp"); break; + default: return "SERVICE_UNKNOWN"; + } +} + +const QString DiscoveryRecord::getName() const +{ + return getName(type); +} + +const QString DiscoveryRecord::getName(Service _type) +{ + switch (_type) + { + case(Service::PhilipsHue): return "Hue bridge"; break; + case(Service::WLED): return "WLED"; break; + case(Service::HyperHDR): return "HyperHDR"; break; + case(Service::Pico): return "Pico/RP2040"; break; + case(Service::ESP32_S2): return "ESP32-S2"; break; + case(Service::ESP): return "ESP board"; break; + default: return "SERVICE_UNKNOWN"; + } +} + +void DiscoveryRecord::resetTTL() +{ + ttl = 7; +} + +bool DiscoveryRecord::expired() +{ + ttl >>= 1; + if (type == Service::WLED) + return ttl == 0; + else if (type == Service::Pico || type == Service::ESP32_S2 || type == Service::ESP) + return ttl <= 2; + else + return ttl <= 1; +} + +DiscoveryRecord::DiscoveryRecord() : + type(Service::Unknown), + port(-1), + isExists(false) +{ + resetTTL(); +} + +bool DiscoveryRecord::operator==(const DiscoveryRecord& other) const +{ + return type == other.type + && address == other.address + && port == other.port; +} + +bool DiscoveryRecord::operator!=(const DiscoveryRecord& other) const +{ + return type != other.type + || address != other.address + || port != other.port; +} + diff --git a/sources/bonjour/DiscoveryWrapper.cpp b/sources/bonjour/DiscoveryWrapper.cpp index c6945b931..fbcf7aabc 100644 --- a/sources/bonjour/DiscoveryWrapper.cpp +++ b/sources/bonjour/DiscoveryWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -37,8 +37,7 @@ #include #include #include - -DiscoveryWrapper* DiscoveryWrapper::instance = nullptr; +#include DiscoveryWrapper::DiscoveryWrapper(QObject* parent) : QObject(parent) @@ -50,16 +49,12 @@ DiscoveryWrapper::DiscoveryWrapper(QObject* parent) qRegisterMetaType>("QList"); qRegisterMetaType("DiscoveryRecord::Service"); - DiscoveryWrapper::instance = this; - - connect(this, &DiscoveryWrapper::discoveryEvent, this, &DiscoveryWrapper::discoveryEventHandler); - connect(this, &DiscoveryWrapper::requestToScan, this, &DiscoveryWrapper::requestToScanHandler); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalDiscoveryEvent, this, &DiscoveryWrapper::signalDiscoveryEventHandler); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalDiscoveryRequestToScan, this, &DiscoveryWrapper::signalDiscoveryRequestToScanHandler); } DiscoveryWrapper::~DiscoveryWrapper() { - if (_serialDevice != nullptr) - delete _serialDevice; _serialDevice = nullptr; } @@ -82,14 +77,14 @@ void DiscoveryWrapper::cleanUp(QList& target) } if (action != DiscoveryRecord::Service::Unknown) - emit foundService(action, target); + emit SignalDiscoveryFoundService(action, target); } QList DiscoveryWrapper::getPhilipsHUE() { cleanUp(_hueDevices); - emit requestToScan(DiscoveryRecord::Service::PhilipsHue); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::PhilipsHue); return _hueDevices; } @@ -98,7 +93,7 @@ QList DiscoveryWrapper::getWLED() { cleanUp(_wledDevices); - emit requestToScan(DiscoveryRecord::Service::WLED); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::WLED); return _wledDevices; } @@ -116,16 +111,16 @@ QList DiscoveryWrapper::getAllServices() void DiscoveryWrapper::requestServicesScan() { cleanUp(_wledDevices); - emit requestToScan(DiscoveryRecord::Service::WLED); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::WLED); cleanUp(_hueDevices); - emit requestToScan(DiscoveryRecord::Service::PhilipsHue); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::PhilipsHue); cleanUp(_hyperhdrSessions); - emit requestToScan(DiscoveryRecord::Service::HyperHDR); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::HyperHDR); cleanUp(_esp32s2Devices); cleanUp(_espDevices); cleanUp(_picoDevices); - emit requestToScan(DiscoveryRecord::Service::SerialPort); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::SerialPort); } void DiscoveryWrapper::gotMessage(QList& target, DiscoveryRecord message) @@ -162,15 +157,15 @@ void DiscoveryWrapper::gotMessage(QList& target, DiscoveryRecor if (target.length() != newSessions.length()) { - QString log = QString("%1 %2 at %3:%4 (%5)").arg((message.isExists) ? "Found" : "Deregistered").arg(message.getName()).arg(message.address).arg(message.port).arg(message.hostName); + QString log = QString("%1 %2 at %3:%4 (%5)").arg((message.isExists) ? "Found" : "Deregistering").arg(message.getName()).arg(message.address).arg(message.port).arg(message.hostName); Info(_log, "%s", QSTRING_CSTR(log)); target = newSessions; - emit foundService(message.type, target); + emit SignalDiscoveryFoundService(message.type, target); } } -void DiscoveryWrapper::discoveryEventHandler(DiscoveryRecord message) +void DiscoveryWrapper::signalDiscoveryEventHandler(DiscoveryRecord message) { if (message.type == DiscoveryRecord::Service::HyperHDR) gotMessage(_hyperhdrSessions, message); @@ -186,7 +181,7 @@ void DiscoveryWrapper::discoveryEventHandler(DiscoveryRecord message) gotMessage(_espDevices, message); } -void DiscoveryWrapper::requestToScanHandler(DiscoveryRecord::Service type) +void DiscoveryWrapper::signalDiscoveryRequestToScanHandler(DiscoveryRecord::Service type) { if (type == DiscoveryRecord::Service::SerialPort) { @@ -195,9 +190,13 @@ void DiscoveryWrapper::requestToScanHandler(DiscoveryRecord::Service type) QJsonObject deviceConfig; deviceConfig["type"] = "adalight"; - _serialDevice = LedDeviceFactory::construct(deviceConfig); + _serialDevice = std::unique_ptr(LedDeviceFactory::construct(deviceConfig)); } QJsonObject params; QJsonObject devicesDiscovered = _serialDevice->discover(params); } + else if (type == DiscoveryRecord::Service::REFRESH_ALL) + { + requestServicesScan(); + } } diff --git a/sources/cec/CMakeLists.txt b/sources/cec/CMakeLists.txt index 0662ee1a8..4f526132e 100644 --- a/sources/cec/CMakeLists.txt +++ b/sources/cec/CMakeLists.txt @@ -6,7 +6,7 @@ FILE ( GLOB CECHANDLER_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR add_library(cechandler ${CECHANDLER_SOURCES}) -target_include_directories(cechandler PUBLIC "${CMAKE_SOURCE_DIR}/dependencies/external/libcec/include") +target_include_directories(cechandler PUBLIC ${CEC_INCLUDE_DIRS}) target_link_libraries(cechandler ${CEC_LIBRARIES} Qt${Qt_VERSION}::Network diff --git a/sources/cec/WrapperCEC.cpp b/sources/cec/WrapperCEC.cpp new file mode 100644 index 000000000..524a4e699 --- /dev/null +++ b/sources/cec/WrapperCEC.cpp @@ -0,0 +1,74 @@ +#ifndef PCH_ENABLED + #include +#endif + +#include + + +WrapperCEC::WrapperCEC(): + _cecHandler(nullptr), + _log(Logger::getInstance("CEC")) +{ +} + +WrapperCEC::~WrapperCEC() +{ + enable(false); +} + +void WrapperCEC::sourceRequestHandler(hyperhdr::Components component, int hyperhdrInd, bool listen) +{ + if (component == hyperhdr::Components::COMP_CEC) + { + if (listen && !CEC_CLIENTS.contains(hyperhdrInd)) + CEC_CLIENTS.append(hyperhdrInd); + else if (!listen) + CEC_CLIENTS.removeOne(hyperhdrInd); + + if (CEC_CLIENTS.empty()) + enable(false); + else + enable(true); + } +} + +void WrapperCEC::enable(bool enabled) +{ + if (enabled) + { + if (_cecHandler == nullptr) + { + Info(_log, "Opening libCEC library."); + +#if defined(ENABLE_CEC) + _cecHandler = new cecHandler(); + connect(_cecHandler, &cecHandler::stateChange, this, &WrapperCEC::SignalStateChange); + connect(_cecHandler, &cecHandler::keyPressed, this, &WrapperCEC::SignalKeyPressed); + if (_cecHandler->start()) + Info(_log, "Success: libCEC library loaded."); + else + { + Error(_log, "Could not open libCEC library"); + enable(false); + } +#endif + } + } + else + { + if (_cecHandler != nullptr) + { + Info(_log, "Disconnecting from libCEC library"); + +#if defined(ENABLE_CEC) + disconnect(_cecHandler, &cecHandler::stateChange, this, &WrapperCEC::SignalStateChange); + disconnect(_cecHandler, &cecHandler::keyPressed, this, &WrapperCEC::SignalKeyPressed); + + _cecHandler->stop(); + Info(_log, "Cleaning up libCEC"); + delete _cecHandler; + _cecHandler = nullptr; +#endif + } + } +} diff --git a/sources/cec/cecHandler.cpp b/sources/cec/cecHandler.cpp index c7a8ab442..fc6411751 100644 --- a/sources/cec/cecHandler.cpp +++ b/sources/cec/cecHandler.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,11 +8,21 @@ #include #ifndef LIBCEC_OSD_NAME_SIZE -#define LIBCEC_OSD_NAME_SIZE sizeof(CEC::libcec_configuration::strDeviceName) + #define LIBCEC_OSD_NAME_SIZE sizeof(CEC::libcec_configuration::strDeviceName) #endif +void handleCecLogMessage(void* context, const CEC::cec_log_message* message); +void handleCecCommandMessage(void* context, const CEC::cec_command* command); +void handleCecKeyPress(void* context, const CEC::cec_keypress* key); + +namespace +{ + CEC::ICECCallbacks _cecCallbacks; + CEC::libcec_configuration _cecConfig; + CEC::ICECAdapter* _cecAdapter = nullptr; +} + cecHandler::cecHandler() : - _cecAdapter(nullptr), _log(Logger::getInstance("CEC")) { Info(_log, "CEC object created"); @@ -117,7 +128,7 @@ void cecHandler::stop() } } -void cecHandler::handleCecLogMessage(void * context, const CEC::cec_log_message* message) +void handleCecLogMessage(void * context, const CEC::cec_log_message* message) { cecHandler* handler = static_cast(context); @@ -137,35 +148,35 @@ void cecHandler::handleCecLogMessage(void * context, const CEC::cec_log_message* } } -void cecHandler::handleCecKeyPress(void* context, const CEC::cec_keypress* key) +void handleCecKeyPress(void* context, const CEC::cec_keypress* key) { cecHandler* handler = static_cast(context); if (handler == nullptr) return; - Debug(handler->_log, "Key pressed: %s, (key code = %i)", handler->_cecAdapter->ToString(key->keycode), (int)key->keycode); + Debug(handler->_log, "Key pressed: %s, (key code = %i)", _cecAdapter->ToString(key->keycode), (int)key->keycode); emit handler->keyPressed((int)key->keycode); } -void cecHandler::handleCecCommandMessage(void * context, const CEC::cec_command* command) +void handleCecCommandMessage(void * context, const CEC::cec_command* command) { cecHandler* handler = static_cast(context); - if (handler == nullptr || handler->_cecAdapter == nullptr || + if (handler == nullptr || _cecAdapter == nullptr || (command->opcode != CEC::CEC_OPCODE_SET_STREAM_PATH && command->opcode != CEC::CEC_OPCODE_STANDBY)) return; if (command->opcode == CEC::CEC_OPCODE_SET_STREAM_PATH) { - emit handler->stateChange(true, QString(handler->_cecAdapter->ToString(command->initiator))); + emit handler->stateChange(true, QString(_cecAdapter->ToString(command->initiator))); } else { - emit handler->stateChange(false, QString(handler->_cecAdapter->ToString(command->initiator))); + emit handler->stateChange(false, QString(_cecAdapter->ToString(command->initiator))); } } diff --git a/sources/commandline/Option.cpp b/sources/commandline/Option.cpp index 1f8b6bded..44e9ca40e 100644 --- a/sources/commandline/Option.cpp +++ b/sources/commandline/Option.cpp @@ -40,7 +40,7 @@ QString Option::getError() const return this->_error; } -const char* Option::getCString(Parser& parser) const +const char* Option::getCString(Parser& parser) const & { return value(parser).toLocal8Bit().constData(); } diff --git a/sources/db/AuthTable.cpp b/sources/db/AuthTable.cpp index 684d13ac1..fc56dc191 100644 --- a/sources/db/AuthTable.cpp +++ b/sources/db/AuthTable.cpp @@ -1,14 +1,12 @@ +#include + #include -#include +using namespace hyperhdr; -AuthTable::AuthTable(const QString& rootPath, QObject* parent, bool readonlyMode) - : DBManager(parent) +AuthTable::AuthTable(bool readonlyMode) + : DBManager() { setReadonlyMode(readonlyMode); - if (!rootPath.isEmpty()) { - setRootPath(rootPath); - setDatabaseName("hyperhdr"); - } // init Auth table setTable("auth"); // create table columns diff --git a/sources/db/DBManager.cpp b/sources/db/DBManager.cpp index d42a026e9..2f801f3ac 100644 --- a/sources/db/DBManager.cpp +++ b/sources/db/DBManager.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -33,6 +33,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include + #include // Required to determine the cmake options #ifdef USE_STATIC_QT_PLUGINS @@ -40,14 +47,11 @@ Q_IMPORT_PLUGIN(QSQLiteDriverPlugin) #endif -#define EXPORT_FILE_FORMAT_VERSION "HyperHDR_export_format_v17" - -QString DBManager::_rootPath; +QFileInfo DBManager::_databaseName; QThreadStorage DBManager::_databasePool; -DBManager::DBManager(QObject* parent) - : QObject(parent) - , _log(Logger::getInstance("DB")) +DBManager::DBManager() + : _log(Logger::getInstance("DB")) , _readonlyMode(false) { } @@ -61,18 +65,11 @@ void DBManager::setReadonlyMode(bool readOnly) _readonlyMode = readOnly; }; -void DBManager::setRootPath(const QString& rootPath) +void DBManager::initializeDatabaseFilename(QFileInfo databaseName) { - _rootPath = rootPath; - // create directory - QDir().mkpath(_rootPath + "/db"); + _databaseName = databaseName; } -void DBManager::setDatabaseName(const QString& dbn) -{ - _dbn = dbn; -}; - void DBManager::setTable(const QString& table) { _table = table; @@ -86,17 +83,15 @@ QSqlDatabase DBManager::getDB() const { auto db = QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString()); _databasePool.setLocalData(db); - - QFileInfo dbFile(_rootPath + "/db/" + _dbn + ".db"); - - db.setDatabaseName(dbFile.absoluteFilePath()); + + db.setDatabaseName(_databaseName.absoluteFilePath()); if (!db.open()) { Error(_log, QSTRING_CSTR(db.lastError().text())); throw std::runtime_error("Failed to open database connection!"); } else - Info(_log, "Database opened: %s", QSTRING_CSTR(dbFile.absoluteFilePath())); + Info(_log, "Database opened: %s", QSTRING_CSTR(_databaseName.absoluteFilePath())); return db; } @@ -580,7 +575,7 @@ const QJsonObject DBManager::getBackup() allSettings.append(entry); } - backup["version"] = EXPORT_FILE_FORMAT_VERSION; + backup["version"] = CURRENT_HYPERHDR_DB_EXPORT_VERSION; backup["instances"] = allInstances; backup["settings"] = allSettings; @@ -592,7 +587,14 @@ QString DBManager::restoreBackup(const QJsonObject& backupData) { QSqlDatabase idb = getDB(); const QJsonObject& message = backupData.value("config").toObject(); - bool rm = _readonlyMode; + bool rm = _readonlyMode; + + Info(_log, "Creating DB backup first."); + QString resultFile = createLocalBackup(); + if (!resultFile.isEmpty()) + Info(_log, "The backup is saved as: %s", QSTRING_CSTR(resultFile)); + else + Warning(_log, "Could not create a backup"); _readonlyMode = true; @@ -694,3 +696,25 @@ QString DBManager::restoreBackup(const QJsonObject& backupData) return ""; } + + +QString DBManager::createLocalBackup() +{ + QJsonObject backupFirst = getBackup(); + QString backupName = getDB().databaseName(); + if (!backupName.isEmpty() && QFile::exists(backupName)) + { + backupName = QDir(QFileInfo(backupName).absoluteDir()).filePath(QString("backup_%1.json").arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz"))); + QFile backFile(backupName); + if (backFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + { + QTextStream out(&backFile); + out.setGenerateByteOrderMark(true); + out << QJsonDocument(backupFirst).toJson(QJsonDocument::Compact); + out.flush(); + backFile.close(); + return backupName; + } + } + return QString(); +} diff --git a/sources/db/InstanceTable.cpp b/sources/db/InstanceTable.cpp index 3257bc93d..c925e5309 100644 --- a/sources/db/InstanceTable.cpp +++ b/sources/db/InstanceTable.cpp @@ -1,14 +1,11 @@ #include -InstanceTable::InstanceTable(const QString& rootPath, QObject* parent, bool readonlyMode) - : DBManager(parent) +InstanceTable::InstanceTable(bool readonlyMode) + : DBManager() { setReadonlyMode(readonlyMode); - setRootPath(rootPath); - setDatabaseName("hyperhdr"); - // Init instance table setTable("instances"); createTable(QStringList() << "instance INTEGER" << "friendly_name TEXT" << "enabled INTEGER DEFAULT 0" << "last_use TEXT"); diff --git a/sources/db/MetaTable.cpp b/sources/db/MetaTable.cpp index c1bbd8823..ff17a0d64 100644 --- a/sources/db/MetaTable.cpp +++ b/sources/db/MetaTable.cpp @@ -1,9 +1,10 @@ -#include +#include #include +#include -MetaTable::MetaTable(QObject* parent, bool readonlyMode) - : DBManager(parent) +MetaTable::MetaTable(bool readonlyMode) + : DBManager() { setReadonlyMode(readonlyMode); diff --git a/sources/db/SettingsTable.cpp b/sources/db/SettingsTable.cpp index 4e762e851..f54d2254e 100644 --- a/sources/db/SettingsTable.cpp +++ b/sources/db/SettingsTable.cpp @@ -3,14 +3,13 @@ #define INSTANCE_COLUMN QString("hyperhdr_instance") -SettingsTable::SettingsTable(quint8 instance, QObject* parent) - : DBManager(parent) - , _hyperhdr_inst(instance) +SettingsTable::SettingsTable(quint8 instance) + : DBManager() + , _instance(instance) { setTable("settings"); // create table columns createTable(QStringList() << "type TEXT" << "config TEXT" << (INSTANCE_COLUMN + " INTEGER") << "updated_at TEXT"); - }; /// @@ -29,7 +28,7 @@ bool SettingsTable::createSettingsRecord(const QString& type, const QString& con cond.append(CPair("type", type)); // when a setting is not global we are searching also for the instance if (!isSettingGlobal(type)) - cond.append(CPair("AND " + INSTANCE_COLUMN, _hyperhdr_inst)); + cond.append(CPair("AND " + INSTANCE_COLUMN, _instance)); return createRecord(cond, map); } @@ -45,7 +44,7 @@ bool SettingsTable::recordExist(const QString& type) const cond.append(CPair("type", type)); // when a setting is not global we are searching also for the instance if (!isSettingGlobal(type)) - cond.append(CPair("AND " + INSTANCE_COLUMN, _hyperhdr_inst)); + cond.append(CPair("AND " + INSTANCE_COLUMN, _instance)); return recordExists(cond); } @@ -61,7 +60,7 @@ QJsonDocument SettingsTable::getSettingsRecord(const QString& type) const cond.append(CPair("type", type)); // when a setting is not global we are searching also for the instance if (!isSettingGlobal(type)) - cond.append(CPair("AND " + INSTANCE_COLUMN, _hyperhdr_inst)); + cond.append(CPair("AND " + INSTANCE_COLUMN, _instance)); getRecord(cond, results, QStringList("config")); return QJsonDocument::fromJson(results["config"].toByteArray()); } @@ -78,7 +77,7 @@ QString SettingsTable::getSettingsRecordString(const QString& type) const cond.append(CPair("type", type)); // when a setting is not global we are searching also for the instance if (!isSettingGlobal(type)) - cond.append(CPair("AND " + INSTANCE_COLUMN, _hyperhdr_inst)); + cond.append(CPair("AND " + INSTANCE_COLUMN, _instance)); getRecord(cond, results, QStringList("config")); return results["config"].toString(); } @@ -90,7 +89,7 @@ bool SettingsTable::deleteSettingsRecordString(const QString& type) const cond.append(CPair("type", type)); // when a setting is not global we are searching also for the instance if (!isSettingGlobal(type)) - cond.append(CPair("AND " + INSTANCE_COLUMN, _hyperhdr_inst)); + cond.append(CPair("AND " + INSTANCE_COLUMN, _instance)); return deleteRecord(cond); } @@ -108,7 +107,7 @@ bool SettingsTable::purge(const QString& type) const void SettingsTable::deleteInstance() const { VectorPair cond; - cond.append(CPair(INSTANCE_COLUMN, _hyperhdr_inst)); + cond.append(CPair(INSTANCE_COLUMN, _instance)); deleteRecord(cond); } diff --git a/sources/effectengine/Animation4Music_PulseBlue.cpp b/sources/effectengine/Animation4Music_PulseBlue.cpp index 7166bff2d..e1ccfb3ff 100644 --- a/sources/effectengine/Animation4Music_PulseBlue.cpp +++ b/sources/effectengine/Animation4Music_PulseBlue.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseBlue::Animation4Music_PulseBlue() : EffectDefinition Animation4Music_PulseBlue::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSEBLUE; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseBlue::hasOwnImage() bool Animation4Music_PulseBlue::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_PulseGreen.cpp b/sources/effectengine/Animation4Music_PulseGreen.cpp index 3ccac0f70..cb5c1e58a 100644 --- a/sources/effectengine/Animation4Music_PulseGreen.cpp +++ b/sources/effectengine/Animation4Music_PulseGreen.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseGreen::Animation4Music_PulseGreen() : EffectDefinition Animation4Music_PulseGreen::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSEGREEN; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseGreen::hasOwnImage() bool Animation4Music_PulseGreen::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_PulseMulti.cpp b/sources/effectengine/Animation4Music_PulseMulti.cpp index 9efb37956..14cda4e44 100644 --- a/sources/effectengine/Animation4Music_PulseMulti.cpp +++ b/sources/effectengine/Animation4Music_PulseMulti.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseMulti::Animation4Music_PulseMulti() : EffectDefinition Animation4Music_PulseMulti::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSEMULTI; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseMulti::hasOwnImage() bool Animation4Music_PulseMulti::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor color; diff --git a/sources/effectengine/Animation4Music_PulseMultiFast.cpp b/sources/effectengine/Animation4Music_PulseMultiFast.cpp index 92f35628c..6758390a7 100644 --- a/sources/effectengine/Animation4Music_PulseMultiFast.cpp +++ b/sources/effectengine/Animation4Music_PulseMultiFast.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseMultiFast::Animation4Music_PulseMultiFast() : EffectDefinition Animation4Music_PulseMultiFast::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSEMULTIFAST; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseMultiFast::hasOwnImage() bool Animation4Music_PulseMultiFast::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor color, empty; diff --git a/sources/effectengine/Animation4Music_PulseMultiSlow.cpp b/sources/effectengine/Animation4Music_PulseMultiSlow.cpp index 70d852285..761172285 100644 --- a/sources/effectengine/Animation4Music_PulseMultiSlow.cpp +++ b/sources/effectengine/Animation4Music_PulseMultiSlow.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseMultiSlow::Animation4Music_PulseMultiSlow() : EffectDefinition Animation4Music_PulseMultiSlow::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSEMULTISLOW; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseMultiSlow::hasOwnImage() bool Animation4Music_PulseMultiSlow::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor color, empty; diff --git a/sources/effectengine/Animation4Music_PulseRed.cpp b/sources/effectengine/Animation4Music_PulseRed.cpp index 0075f2d78..474cc5bf1 100644 --- a/sources/effectengine/Animation4Music_PulseRed.cpp +++ b/sources/effectengine/Animation4Music_PulseRed.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseRed::Animation4Music_PulseRed() : EffectDefinition Animation4Music_PulseRed::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSERED; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseRed::hasOwnImage() bool Animation4Music_PulseRed::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_PulseWhite.cpp b/sources/effectengine/Animation4Music_PulseWhite.cpp index 4110cf3d9..d2d46b6ce 100644 --- a/sources/effectengine/Animation4Music_PulseWhite.cpp +++ b/sources/effectengine/Animation4Music_PulseWhite.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseWhite::Animation4Music_PulseWhite() : EffectDefinition Animation4Music_PulseWhite::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSEWHITE; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseWhite::hasOwnImage() bool Animation4Music_PulseWhite::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_PulseYellow.cpp b/sources/effectengine/Animation4Music_PulseYellow.cpp index 5ec035453..bda64c77c 100644 --- a/sources/effectengine/Animation4Music_PulseYellow.cpp +++ b/sources/effectengine/Animation4Music_PulseYellow.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_PulseYellow::Animation4Music_PulseYellow() : EffectDefinition Animation4Music_PulseYellow::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_PULSEYELLOW; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_PulseYellow::hasOwnImage() bool Animation4Music_PulseYellow::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_QuatroBlue.cpp b/sources/effectengine/Animation4Music_QuatroBlue.cpp index 8a7dfd264..b4c0691bb 100644 --- a/sources/effectengine/Animation4Music_QuatroBlue.cpp +++ b/sources/effectengine/Animation4Music_QuatroBlue.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroBlue::Animation4Music_QuatroBlue() : EffectDefinition Animation4Music_QuatroBlue::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATROBLUE; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroBlue::hasOwnImage() bool Animation4Music_QuatroBlue::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_QuatroGreen.cpp b/sources/effectengine/Animation4Music_QuatroGreen.cpp index 33d43b7d9..2ee63d1e4 100644 --- a/sources/effectengine/Animation4Music_QuatroGreen.cpp +++ b/sources/effectengine/Animation4Music_QuatroGreen.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroGreen::Animation4Music_QuatroGreen() : EffectDefinition Animation4Music_QuatroGreen::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATROGREEN; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroGreen::hasOwnImage() bool Animation4Music_QuatroGreen::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_QuatroMulti.cpp b/sources/effectengine/Animation4Music_QuatroMulti.cpp index d29b838d1..04cf95fa5 100644 --- a/sources/effectengine/Animation4Music_QuatroMulti.cpp +++ b/sources/effectengine/Animation4Music_QuatroMulti.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroMulti::Animation4Music_QuatroMulti() : EffectDefinition Animation4Music_QuatroMulti::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATROMULTI; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroMulti::hasOwnImage() bool Animation4Music_QuatroMulti::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor selected; diff --git a/sources/effectengine/Animation4Music_QuatroMultiFast.cpp b/sources/effectengine/Animation4Music_QuatroMultiFast.cpp index 227105453..2563b87ef 100644 --- a/sources/effectengine/Animation4Music_QuatroMultiFast.cpp +++ b/sources/effectengine/Animation4Music_QuatroMultiFast.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroMultiFast::Animation4Music_QuatroMultiFast() : EffectDefinition Animation4Music_QuatroMultiFast::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATROMULTIFAST; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroMultiFast::hasOwnImage() bool Animation4Music_QuatroMultiFast::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor empty, selected; diff --git a/sources/effectengine/Animation4Music_QuatroMultiSlow.cpp b/sources/effectengine/Animation4Music_QuatroMultiSlow.cpp index 37bc7d88d..5199c4377 100644 --- a/sources/effectengine/Animation4Music_QuatroMultiSlow.cpp +++ b/sources/effectengine/Animation4Music_QuatroMultiSlow.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroMultiSlow::Animation4Music_QuatroMultiSlow() : EffectDefinition Animation4Music_QuatroMultiSlow::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATROMULTISLOW; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroMultiSlow::hasOwnImage() bool Animation4Music_QuatroMultiSlow::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor empty, selected; diff --git a/sources/effectengine/Animation4Music_QuatroRed.cpp b/sources/effectengine/Animation4Music_QuatroRed.cpp index 579bd788c..8fa0c7fc0 100644 --- a/sources/effectengine/Animation4Music_QuatroRed.cpp +++ b/sources/effectengine/Animation4Music_QuatroRed.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroRed::Animation4Music_QuatroRed() : EffectDefinition Animation4Music_QuatroRed::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATRORED; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroRed::hasOwnImage() bool Animation4Music_QuatroRed::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_QuatroWhite.cpp b/sources/effectengine/Animation4Music_QuatroWhite.cpp index bc759395c..ad413c373 100644 --- a/sources/effectengine/Animation4Music_QuatroWhite.cpp +++ b/sources/effectengine/Animation4Music_QuatroWhite.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroWhite::Animation4Music_QuatroWhite() : EffectDefinition Animation4Music_QuatroWhite::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATROWHITE; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroWhite::hasOwnImage() bool Animation4Music_QuatroWhite::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_QuatroYellow.cpp b/sources/effectengine/Animation4Music_QuatroYellow.cpp index 1bc83a446..03c2b447b 100644 --- a/sources/effectengine/Animation4Music_QuatroYellow.cpp +++ b/sources/effectengine/Animation4Music_QuatroYellow.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_QuatroYellow::Animation4Music_QuatroYellow() : EffectDefinition Animation4Music_QuatroYellow::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_QUATROYELLOW; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_QuatroYellow::hasOwnImage() bool Animation4Music_QuatroYellow::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_StereoBlue.cpp b/sources/effectengine/Animation4Music_StereoBlue.cpp index 9504b857f..1782ed16a 100644 --- a/sources/effectengine/Animation4Music_StereoBlue.cpp +++ b/sources/effectengine/Animation4Music_StereoBlue.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_StereoBlue::Animation4Music_StereoBlue() : EffectDefinition Animation4Music_StereoBlue::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREOBLUE; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_StereoBlue::hasOwnImage() bool Animation4Music_StereoBlue::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_StereoGreen.cpp b/sources/effectengine/Animation4Music_StereoGreen.cpp index a7bb2270d..e2e0b44cc 100644 --- a/sources/effectengine/Animation4Music_StereoGreen.cpp +++ b/sources/effectengine/Animation4Music_StereoGreen.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_StereoGreen::Animation4Music_StereoGreen() : EffectDefinition Animation4Music_StereoGreen::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREOGREEN; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_StereoGreen::hasOwnImage() bool Animation4Music_StereoGreen::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_StereoMulti.cpp b/sources/effectengine/Animation4Music_StereoMulti.cpp index 95428149a..6aad3bacd 100644 --- a/sources/effectengine/Animation4Music_StereoMulti.cpp +++ b/sources/effectengine/Animation4Music_StereoMulti.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_StereoMulti::Animation4Music_StereoMulti() : EffectDefinition Animation4Music_StereoMulti::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREOMULTI; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_StereoMulti::hasOwnImage() bool Animation4Music_StereoMulti::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor selected; diff --git a/sources/effectengine/Animation4Music_StereoMultiFast.cpp b/sources/effectengine/Animation4Music_StereoMultiFast.cpp index 8121579b0..a60aa370c 100644 --- a/sources/effectengine/Animation4Music_StereoMultiFast.cpp +++ b/sources/effectengine/Animation4Music_StereoMultiFast.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_StereoMultiFast::Animation4Music_StereoMultiFast() : EffectDefinition Animation4Music_StereoMultiFast::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREOMULTIFAST; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_StereoMultiFast::hasOwnImage() bool Animation4Music_StereoMultiFast::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor selected, empty; diff --git a/sources/effectengine/Animation4Music_StereoMultiSlow.cpp b/sources/effectengine/Animation4Music_StereoMultiSlow.cpp index 288ed9c47..9504ea852 100644 --- a/sources/effectengine/Animation4Music_StereoMultiSlow.cpp +++ b/sources/effectengine/Animation4Music_StereoMultiSlow.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_StereoMultiSlow::Animation4Music_StereoMultiSlow() : EffectDefinition Animation4Music_StereoMultiSlow::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREOMULTISLOW; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_StereoMultiSlow::hasOwnImage() bool Animation4Music_StereoMultiSlow::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor selected, empty; diff --git a/sources/effectengine/Animation4Music_StereoRed.cpp b/sources/effectengine/Animation4Music_StereoRed.cpp index bc2babc75..f9671b1ba 100644 --- a/sources/effectengine/Animation4Music_StereoRed.cpp +++ b/sources/effectengine/Animation4Music_StereoRed.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,9 +39,8 @@ Animation4Music_StereoRed::Animation4Music_StereoRed() : EffectDefinition Animation4Music_StereoRed::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREORED; - ed.args = GetArgs(); return ed; } @@ -66,9 +65,9 @@ bool Animation4Music_StereoRed::hasOwnImage() bool Animation4Music_StereoRed::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_StereoWhite.cpp b/sources/effectengine/Animation4Music_StereoWhite.cpp index ec4ff4786..5998460a0 100644 --- a/sources/effectengine/Animation4Music_StereoWhite.cpp +++ b/sources/effectengine/Animation4Music_StereoWhite.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_StereoWhite::Animation4Music_StereoWhite() : EffectDefinition Animation4Music_StereoWhite::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREOWHITE; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_StereoWhite::hasOwnImage() bool Animation4Music_StereoWhite::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_StereoYellow.cpp b/sources/effectengine/Animation4Music_StereoYellow.cpp index ed55008f5..f9777122e 100644 --- a/sources/effectengine/Animation4Music_StereoYellow.cpp +++ b/sources/effectengine/Animation4Music_StereoYellow.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,9 +38,8 @@ Animation4Music_StereoYellow::Animation4Music_StereoYellow() : EffectDefinition Animation4Music_StereoYellow::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_STEREOYELLOW; - ed.args = GetArgs(); return ed; } @@ -65,9 +64,9 @@ bool Animation4Music_StereoYellow::hasOwnImage() bool Animation4Music_StereoYellow::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; int value = r->getValue(_oldMulti); diff --git a/sources/effectengine/Animation4Music_TestEq.cpp b/sources/effectengine/Animation4Music_TestEq.cpp index afafc8235..e67fc1252 100644 --- a/sources/effectengine/Animation4Music_TestEq.cpp +++ b/sources/effectengine/Animation4Music_TestEq.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -30,16 +30,16 @@ Animation4Music_TestEq::Animation4Music_TestEq() : AnimationBaseMusic(AMUSIC_TESTEQ), - _internalIndex(0) + _internalIndex(0), + _oldMulti(0) { }; EffectDefinition Animation4Music_TestEq::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_TESTEQ; - ed.args = GetArgs(); return ed; } @@ -64,9 +64,9 @@ bool Animation4Music_TestEq::hasOwnImage() bool Animation4Music_TestEq::getImage(Image& newImage) { uint8_t buffScaledResult[SOUNDCAP_RESULT_RES]; - auto r = SoundCapture::getInstance()->hasResult(_internalIndex); + auto r = _soundCapture->hasResult(this, _internalIndex, nullptr, nullptr, nullptr, &_oldMulti); - if (r == NULL) + if (r == nullptr) return false; r->GetBufResult(buffScaledResult, sizeof(buffScaledResult)); diff --git a/sources/effectengine/Animation4Music_WavesPulse.cpp b/sources/effectengine/Animation4Music_WavesPulse.cpp index b7f70b464..b5b32a5e1 100644 --- a/sources/effectengine/Animation4Music_WavesPulse.cpp +++ b/sources/effectengine/Animation4Music_WavesPulse.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -41,9 +41,8 @@ Animation4Music_WavesPulse::Animation4Music_WavesPulse() : EffectDefinition Animation4Music_WavesPulse::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_WAVESPULSE; - ed.args = GetArgs(); return ed; } @@ -68,9 +67,9 @@ bool Animation4Music_WavesPulse::hasOwnImage() bool Animation4Music_WavesPulse::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, &newData, NULL, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor selected; diff --git a/sources/effectengine/Animation4Music_WavesPulseFast.cpp b/sources/effectengine/Animation4Music_WavesPulseFast.cpp index 7664c5110..c94e381bf 100644 --- a/sources/effectengine/Animation4Music_WavesPulseFast.cpp +++ b/sources/effectengine/Animation4Music_WavesPulseFast.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -41,9 +41,8 @@ Animation4Music_WavesPulseFast::Animation4Music_WavesPulseFast() : EffectDefinition Animation4Music_WavesPulseFast::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_WAVESPULSEFAST; - ed.args = GetArgs(); return ed; } @@ -68,9 +67,9 @@ bool Animation4Music_WavesPulseFast::hasOwnImage() bool Animation4Music_WavesPulseFast::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, NULL, &newData, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor empty, selected; diff --git a/sources/effectengine/Animation4Music_WavesPulseSlow.cpp b/sources/effectengine/Animation4Music_WavesPulseSlow.cpp index ab4542103..4cdd72130 100644 --- a/sources/effectengine/Animation4Music_WavesPulseSlow.cpp +++ b/sources/effectengine/Animation4Music_WavesPulseSlow.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -41,9 +41,8 @@ Animation4Music_WavesPulseSlow::Animation4Music_WavesPulseSlow() : EffectDefinition Animation4Music_WavesPulseSlow::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(true, EffectFactory); ed.name = AMUSIC_WAVESPULSESLOW; - ed.args = GetArgs(); return ed; } @@ -69,9 +68,9 @@ bool Animation4Music_WavesPulseSlow::hasOwnImage() bool Animation4Music_WavesPulseSlow::getImage(Image& newImage) { bool newData = false; - auto r = SoundCapture::getInstance()->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); + auto r = _soundCapture->hasResult(this, _internalIndex, NULL, &newData, NULL, &_oldMulti); - if (r == NULL || !newData) + if (r == nullptr || !newData) return false; QColor empty, selected; diff --git a/sources/effectengine/AnimationBase.cpp b/sources/effectengine/AnimationBase.cpp index 5bef3a77b..f4fe190ea 100644 --- a/sources/effectengine/AnimationBase.cpp +++ b/sources/effectengine/AnimationBase.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -30,7 +30,6 @@ AnimationBase::AnimationBase(QString name) : _name(name), _sleepTime(100), - _isDevice(false), _stopMe(false) { }; diff --git a/sources/effectengine/AnimationBaseMusic.cpp b/sources/effectengine/AnimationBaseMusic.cpp index 830cd9def..0844ba089 100644 --- a/sources/effectengine/AnimationBaseMusic.cpp +++ b/sources/effectengine/AnimationBaseMusic.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,18 +26,25 @@ */ #include +#include +#include AnimationBaseMusic::AnimationBaseMusic(QString name) : AnimationBase(name) { _myTarget.Clear(); + + emit GlobalSignals::getInstance()->SignalGetSoundCapture(_soundCapture); + if (_soundCapture != nullptr) + SAFE_CALL_0_RET(_soundCapture.get(), open, uint32_t, _soundHandle) + else + setStopMe(true); }; -QJsonObject AnimationBaseMusic::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = true; - doc["smoothing-direct-mode"] = true; - return doc; +AnimationBaseMusic::~AnimationBaseMusic() +{ + if (_soundHandle != 0 && _soundCapture != nullptr) + QUEUE_CALL_1(_soundCapture.get(), close, uint32_t, _soundHandle); } bool AnimationBaseMusic::isSoundEffect() diff --git a/sources/effectengine/Animation_AtomicSwirl.cpp b/sources/effectengine/Animation_AtomicSwirl.cpp index 4b36db852..3f72c9cf0 100644 --- a/sources/effectengine/Animation_AtomicSwirl.cpp +++ b/sources/effectengine/Animation_AtomicSwirl.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -55,14 +55,7 @@ Animation_AtomicSwirl::Animation_AtomicSwirl(QString name) : EffectDefinition Animation_AtomicSwirl::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_ATOMIC_SWIRL; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_AtomicSwirl::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_BlueMoodBlobs.cpp b/sources/effectengine/Animation_BlueMoodBlobs.cpp index b2a2806e9..9ef30eded 100644 --- a/sources/effectengine/Animation_BlueMoodBlobs.cpp +++ b/sources/effectengine/Animation_BlueMoodBlobs.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,14 +39,7 @@ Animation_BlueMoodBlobs::Animation_BlueMoodBlobs(QString name) : EffectDefinition Animation_BlueMoodBlobs::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_BLUE_MOOD_BLOBS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_BlueMoodBlobs::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_Breath.cpp b/sources/effectengine/Animation_Breath.cpp index aee0a31bd..a35a3b2a5 100644 --- a/sources/effectengine/Animation_Breath.cpp +++ b/sources/effectengine/Animation_Breath.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,14 +42,7 @@ Animation_Breath::Animation_Breath(QString name) : EffectDefinition Animation_Breath::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_BREATH; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_Breath::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_Candle.cpp b/sources/effectengine/Animation_Candle.cpp index 556d8d0f7..0982cd5fd 100644 --- a/sources/effectengine/Animation_Candle.cpp +++ b/sources/effectengine/Animation_Candle.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -36,16 +36,7 @@ Animation_Candle::Animation_Candle(QString name) : EffectDefinition Animation_Candle::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory, 500, 20); ed.name = ANIM_CANDLE; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_Candle::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = true; - doc["smoothing-time_ms"] = 500; - doc["smoothing-updateFrequency"] = 20.0; - return doc; -} diff --git a/sources/effectengine/Animation_CandleLight.cpp b/sources/effectengine/Animation_CandleLight.cpp index f087877d9..77e9a561c 100644 --- a/sources/effectengine/Animation_CandleLight.cpp +++ b/sources/effectengine/Animation_CandleLight.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * diff --git a/sources/effectengine/Animation_CinemaBrightenLights.cpp b/sources/effectengine/Animation_CinemaBrightenLights.cpp index 6357e962d..f5a64c76e 100644 --- a/sources/effectengine/Animation_CinemaBrightenLights.cpp +++ b/sources/effectengine/Animation_CinemaBrightenLights.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,14 +42,7 @@ Animation_CinemaBrightenLights::Animation_CinemaBrightenLights(QString name) : EffectDefinition Animation_CinemaBrightenLights::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_CINEMA_BRIGHTEN_LIGHTS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_CinemaBrightenLights::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_CinemaDimLights.cpp b/sources/effectengine/Animation_CinemaDimLights.cpp index 7e1334c30..e32a95b20 100644 --- a/sources/effectengine/Animation_CinemaDimLights.cpp +++ b/sources/effectengine/Animation_CinemaDimLights.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,14 +42,7 @@ Animation_CinemaDimLights::Animation_CinemaDimLights(QString name) : EffectDefinition Animation_CinemaDimLights::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_CINEMA_DIM_LIGHTS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_CinemaDimLights::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_ColdMoodBlobs.cpp b/sources/effectengine/Animation_ColdMoodBlobs.cpp index 94b953855..20defbff2 100644 --- a/sources/effectengine/Animation_ColdMoodBlobs.cpp +++ b/sources/effectengine/Animation_ColdMoodBlobs.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -43,14 +43,7 @@ Animation_ColdMoodBlobs::Animation_ColdMoodBlobs(QString name) : EffectDefinition Animation_ColdMoodBlobs::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_COLD_MOOD_BLOBS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_ColdMoodBlobs::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_DoubleSwirl.cpp b/sources/effectengine/Animation_DoubleSwirl.cpp index 8dc42b5c3..38313fba1 100644 --- a/sources/effectengine/Animation_DoubleSwirl.cpp +++ b/sources/effectengine/Animation_DoubleSwirl.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -61,14 +61,8 @@ Animation_DoubleSwirl::Animation_DoubleSwirl(QString name) : EffectDefinition Animation_DoubleSwirl::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_DOUBLE_SWIRL; - ed.args = GetArgs(); return ed; } -QJsonObject Animation_DoubleSwirl::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_Fade.cpp b/sources/effectengine/Animation_Fade.cpp index 2a6f03147..9646c7a77 100644 --- a/sources/effectengine/Animation_Fade.cpp +++ b/sources/effectengine/Animation_Fade.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * diff --git a/sources/effectengine/Animation_FullColorMoodBlobs.cpp b/sources/effectengine/Animation_FullColorMoodBlobs.cpp index 10c67c2be..8dcfedaac 100644 --- a/sources/effectengine/Animation_FullColorMoodBlobs.cpp +++ b/sources/effectengine/Animation_FullColorMoodBlobs.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -43,14 +43,7 @@ Animation_FullColorMoodBlobs::Animation_FullColorMoodBlobs(QString name) : EffectDefinition Animation_FullColorMoodBlobs::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_FULLCOLOR_MOOD_BLOBS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_FullColorMoodBlobs::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_GreenMoodBlobs.cpp b/sources/effectengine/Animation_GreenMoodBlobs.cpp index 41b5fec97..1f8b1e071 100644 --- a/sources/effectengine/Animation_GreenMoodBlobs.cpp +++ b/sources/effectengine/Animation_GreenMoodBlobs.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,14 +39,7 @@ Animation_GreenMoodBlobs::Animation_GreenMoodBlobs(QString name) : EffectDefinition Animation_GreenMoodBlobs::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_GREEN_MOOD_BLOBS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_GreenMoodBlobs::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_KnightRider.cpp b/sources/effectengine/Animation_KnightRider.cpp index 7938e9a45..7bdbf9ef2 100644 --- a/sources/effectengine/Animation_KnightRider.cpp +++ b/sources/effectengine/Animation_KnightRider.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -46,9 +46,8 @@ Animation_KnightRider::Animation_KnightRider() : EffectDefinition Animation_KnightRider::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_KNIGHT_RIDER; - ed.args = GetArgs(); return ed; } @@ -119,11 +118,6 @@ bool Animation_KnightRider::Play(QPainter* painter) return ret; } -QJsonObject Animation_KnightRider::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_MoodBlobs.cpp b/sources/effectengine/Animation_MoodBlobs.cpp index 5c11bc28b..d500dbeee 100644 --- a/sources/effectengine/Animation_MoodBlobs.cpp +++ b/sources/effectengine/Animation_MoodBlobs.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * diff --git a/sources/effectengine/Animation_NotifyBlue.cpp b/sources/effectengine/Animation_NotifyBlue.cpp index 411d551be..af53b1b67 100644 --- a/sources/effectengine/Animation_NotifyBlue.cpp +++ b/sources/effectengine/Animation_NotifyBlue.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,14 +42,8 @@ Animation_NotifyBlue::Animation_NotifyBlue(QString name) : EffectDefinition Animation_NotifyBlue::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_NOTIFY_BLUE; - ed.args = GetArgs(); return ed; } -QJsonObject Animation_NotifyBlue::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_Plasma.cpp b/sources/effectengine/Animation_Plasma.cpp index ef80d2d2b..9a1db6dcb 100644 --- a/sources/effectengine/Animation_Plasma.cpp +++ b/sources/effectengine/Animation_Plasma.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -49,9 +49,8 @@ Animation_Plasma::Animation_Plasma() : EffectDefinition Animation_Plasma::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_PLASMA; - ed.args = GetArgs(); return ed; } @@ -112,12 +111,6 @@ bool Animation_Plasma::Play(QPainter* painter) return ret; } -QJsonObject Animation_Plasma::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} - diff --git a/sources/effectengine/Animation_Police.cpp b/sources/effectengine/Animation_Police.cpp index cf9834259..c6f93c042 100644 --- a/sources/effectengine/Animation_Police.cpp +++ b/sources/effectengine/Animation_Police.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * diff --git a/sources/effectengine/Animation_PoliceLightsSingle.cpp b/sources/effectengine/Animation_PoliceLightsSingle.cpp index f114d13ed..f2a6aa4af 100644 --- a/sources/effectengine/Animation_PoliceLightsSingle.cpp +++ b/sources/effectengine/Animation_PoliceLightsSingle.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,14 +39,8 @@ Animation_PoliceLightsSingle::Animation_PoliceLightsSingle(QString name) : EffectDefinition Animation_PoliceLightsSingle::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_POLICELIGHTSSINGLE; - ed.args = GetArgs(); return ed; } -QJsonObject Animation_PoliceLightsSingle::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_PoliceLightsSolid.cpp b/sources/effectengine/Animation_PoliceLightsSolid.cpp index ad673fbf4..aa820dc63 100644 --- a/sources/effectengine/Animation_PoliceLightsSolid.cpp +++ b/sources/effectengine/Animation_PoliceLightsSolid.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -38,14 +38,8 @@ Animation_PoliceLightsSolid::Animation_PoliceLightsSolid(QString name) : EffectDefinition Animation_PoliceLightsSolid::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_POLICELIGHTSSOLID; - ed.args = GetArgs(); return ed; } -QJsonObject Animation_PoliceLightsSolid::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_RainbowSwirl.cpp b/sources/effectengine/Animation_RainbowSwirl.cpp index d09173eff..701272461 100644 --- a/sources/effectengine/Animation_RainbowSwirl.cpp +++ b/sources/effectengine/Animation_RainbowSwirl.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -43,16 +43,8 @@ Animation_RainbowSwirl::Animation_RainbowSwirl(QString name) : EffectDefinition Animation_RainbowSwirl::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory, 200, 25); ed.name = ANIM_RAINBOW_SWIRL; - ed.args = GetArgs(); return ed; } -QJsonObject Animation_RainbowSwirl::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = true; - doc["smoothing-time_ms"] = 200; - doc["smoothing-updateFrequency"] = 25.0; - return doc; -} diff --git a/sources/effectengine/Animation_RainbowWaves.cpp b/sources/effectengine/Animation_RainbowWaves.cpp index d5d2899d4..2b2a12af7 100644 --- a/sources/effectengine/Animation_RainbowWaves.cpp +++ b/sources/effectengine/Animation_RainbowWaves.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -70,16 +70,9 @@ bool Animation_RainbowWaves::hasLedData(QVector& buffer) EffectDefinition Animation_RainbowWaves::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_RAINBOW_WAVES; - ed.args = GetArgs(); return ed; } -QJsonObject Animation_RainbowWaves::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} - diff --git a/sources/effectengine/Animation_RedMoodBlobs.cpp b/sources/effectengine/Animation_RedMoodBlobs.cpp index d2e03aac2..32943874b 100644 --- a/sources/effectengine/Animation_RedMoodBlobs.cpp +++ b/sources/effectengine/Animation_RedMoodBlobs.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,14 +39,7 @@ Animation_RedMoodBlobs::Animation_RedMoodBlobs(QString name) : EffectDefinition Animation_RedMoodBlobs::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_RED_MOOD_BLOBS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_RedMoodBlobs::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_SeaWaves.cpp b/sources/effectengine/Animation_SeaWaves.cpp index bceefc0fe..c45bdf6e5 100644 --- a/sources/effectengine/Animation_SeaWaves.cpp +++ b/sources/effectengine/Animation_SeaWaves.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -55,16 +55,7 @@ Animation_SeaWaves::Animation_SeaWaves(QString name) : EffectDefinition Animation_SeaWaves::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory, 200, 25); ed.name = ANIM_SEAWAVES; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_SeaWaves::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = true; - doc["smoothing-time_ms"] = 200; - doc["smoothing-updateFrequency"] = 25.0; - return doc; -} diff --git a/sources/effectengine/Animation_Sparks.cpp b/sources/effectengine/Animation_Sparks.cpp index b21d694c0..326994d99 100644 --- a/sources/effectengine/Animation_Sparks.cpp +++ b/sources/effectengine/Animation_Sparks.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -72,16 +72,7 @@ bool Animation_Sparks::hasLedData(QVector& buffer) EffectDefinition Animation_Sparks::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_SPARKS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_Sparks::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} - - diff --git a/sources/effectengine/Animation_StrobeRed.cpp b/sources/effectengine/Animation_StrobeRed.cpp index 6579dde10..0fa9aa267 100644 --- a/sources/effectengine/Animation_StrobeRed.cpp +++ b/sources/effectengine/Animation_StrobeRed.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,14 +42,7 @@ Animation_StrobeRed::Animation_StrobeRed(QString name) : EffectDefinition Animation_StrobeRed::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_STROBE_RED; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_StrobeRed::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_StrobeWhite.cpp b/sources/effectengine/Animation_StrobeWhite.cpp index fb52ac2bc..d5fa6ddd0 100644 --- a/sources/effectengine/Animation_StrobeWhite.cpp +++ b/sources/effectengine/Animation_StrobeWhite.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,14 +42,7 @@ Animation_StrobeWhite::Animation_StrobeWhite(QString name) : EffectDefinition Animation_StrobeWhite::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_STROBE_WHITE; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_StrobeWhite::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_Swirl.cpp b/sources/effectengine/Animation_Swirl.cpp index 47c71ce7f..9fd2a1ff7 100644 --- a/sources/effectengine/Animation_Swirl.cpp +++ b/sources/effectengine/Animation_Swirl.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * diff --git a/sources/effectengine/Animation_SwirlFast.cpp b/sources/effectengine/Animation_SwirlFast.cpp index 25042be0d..6979fad27 100644 --- a/sources/effectengine/Animation_SwirlFast.cpp +++ b/sources/effectengine/Animation_SwirlFast.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -43,14 +43,7 @@ Animation_SwirlFast::Animation_SwirlFast(QString name) : EffectDefinition Animation_SwirlFast::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_SWIRL_FAST; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_SwirlFast::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Animation_SystemShutdown.cpp b/sources/effectengine/Animation_SystemShutdown.cpp index 019976ac5..0a590f321 100644 --- a/sources/effectengine/Animation_SystemShutdown.cpp +++ b/sources/effectengine/Animation_SystemShutdown.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,9 +42,8 @@ Animation_SystemShutdown::Animation_SystemShutdown() : EffectDefinition Animation_SystemShutdown::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_SYSTEM_SHUTDOWN; - ed.args = GetArgs(); return ed; } @@ -116,12 +115,6 @@ bool Animation_SystemShutdown::Play(QPainter* painter) return ret; } -QJsonObject Animation_SystemShutdown::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} - diff --git a/sources/effectengine/Animation_WarmMoodBlobs.cpp b/sources/effectengine/Animation_WarmMoodBlobs.cpp index 9df8d2da3..0d1ced1dc 100644 --- a/sources/effectengine/Animation_WarmMoodBlobs.cpp +++ b/sources/effectengine/Animation_WarmMoodBlobs.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -43,16 +43,7 @@ Animation_WarmMoodBlobs::Animation_WarmMoodBlobs(QString name) : EffectDefinition Animation_WarmMoodBlobs::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory, 200, 25); ed.name = ANIM_WARM_MOOD_BLOBS; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_WarmMoodBlobs::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = true; - doc["smoothing-time_ms"] = 200; - doc["smoothing-updateFrequency"] = 25.0; - return doc; -} diff --git a/sources/effectengine/Animation_Waves.cpp b/sources/effectengine/Animation_Waves.cpp index df3f554fa..a9618fd94 100644 --- a/sources/effectengine/Animation_Waves.cpp +++ b/sources/effectengine/Animation_Waves.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * diff --git a/sources/effectengine/Animation_WavesWithColor.cpp b/sources/effectengine/Animation_WavesWithColor.cpp index fcca79f98..40ccb268c 100644 --- a/sources/effectengine/Animation_WavesWithColor.cpp +++ b/sources/effectengine/Animation_WavesWithColor.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -35,14 +35,7 @@ Animation_WavesWithColor::Animation_WavesWithColor(QString name) : EffectDefinition Animation_WavesWithColor::getDefinition() { - EffectDefinition ed; + EffectDefinition ed(EffectFactory); ed.name = ANIM_WAVESWITHCOLOR; - ed.args = GetArgs(); return ed; } - -QJsonObject Animation_WavesWithColor::GetArgs() { - QJsonObject doc; - doc["smoothing-custom-settings"] = false; - return doc; -} diff --git a/sources/effectengine/Effect.cpp b/sources/effectengine/Effect.cpp index 515cd2ef5..3ca8e6b96 100644 --- a/sources/effectengine/Effect.cpp +++ b/sources/effectengine/Effect.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,10 +25,10 @@ * SOFTWARE. */ -// Qt includes -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif // effect engin eincludes #include @@ -92,446 +92,161 @@ #include #include -#include +#include -Effect::Effect(HyperHdrInstance* hyperhdr, int visiblePriority, int priority, int timeout, const QString& name, const QJsonObject& args, const QString& imageData) - : QThread() - , _hyperhdr(hyperhdr) +Effect::Effect(HyperHdrInstance* hyperhdr, int visiblePriority, int priority, int timeout, const EffectDefinition& effect) + : QObject() , _visiblePriority(visiblePriority) , _priority(priority) , _timeout(timeout) - , _name(name) - , _args(args) - , _imageData(imageData) + , _instanceIndex(hyperhdr->getInstanceIndex()) + , _name(effect.name) + , _effect(effect.factory()) , _endTime(-1) - , _colors() - , _imageSize(hyperhdr->getLedGridSize()) - , _image(_imageSize, QImage::Format_ARGB32_Premultiplied) - , _painter(NULL) - , _effect(NULL) - , _soundHandle(0) + , _interrupt(false) + , _image(hyperhdr->getLedGridSize(), QImage::Format_ARGB32_Premultiplied) + , _timer(this) + , _ledCount(hyperhdr->getLedCount()) { - _ledCount = _hyperhdr->getLedCount(); + _log = Logger::getInstance(QString("EFFECT%1(%2)").arg(_instanceIndex).arg((_name.length() > 9) ? _name.left(6) + "..." : _name)); + _colors.resize(_ledCount); _colors.fill(ColorRgb::BLACK); - - _log = Logger::getInstance(QString("EFFECT%1(%2)").arg(hyperhdr->getInstanceIndex()).arg((name.length() > 9) ? name.left(6) + "..." : name)); - - // init effect image for image based effects, size is based on led layout _image.fill(Qt::black); + _timer.setTimerType(Qt::PreciseTimer); + connect(&_timer, &QTimer::timeout, this, &Effect::run); +} - if (name == ANIM_RAINBOW_SWIRL) - { - _effect = new Animation_RainbowSwirl(); - } - else if (name == ANIM_SWIRL_FAST) - { - _effect = new Animation_SwirlFast(); - } - else if (name == ANIM_RAINBOW_WAVES) - { - _effect = new Animation_RainbowWaves(); - } - else if (name == ANIM_ATOMIC_SWIRL) - { - _effect = new Animation_AtomicSwirl(); - } - else if (name == ANIM_DOUBLE_SWIRL) - { - _effect = new Animation_DoubleSwirl(); - } - else if (name == ANIM_KNIGHT_RIDER) - { - _effect = new Animation_KnightRider(); - } - else if (name == ANIM_PLASMA) - { - _effect = new Animation_Plasma(); - } - else if (name == ANIM_POLICELIGHTSSINGLE) - { - _effect = new Animation_PoliceLightsSingle(); - } - else if (name == ANIM_POLICELIGHTSSOLID) - { - _effect = new Animation_PoliceLightsSolid(); - } - else if (name == ANIM_WAVESWITHCOLOR) - { - _effect = new Animation_WavesWithColor(); - } - else if (name == ANIM_SEAWAVES) - { - _effect = new Animation_SeaWaves(); - } - else if (name == ANIM_RED_MOOD_BLOBS) - { - _effect = new Animation_RedMoodBlobs(); - } - else if (name == ANIM_COLD_MOOD_BLOBS) - { - _effect = new Animation_ColdMoodBlobs(); - } - else if (name == ANIM_BLUE_MOOD_BLOBS) - { - _effect = new Animation_BlueMoodBlobs(); - } - else if (name == ANIM_FULLCOLOR_MOOD_BLOBS) - { - _effect = new Animation_FullColorMoodBlobs(); - } - else if (name == ANIM_GREEN_MOOD_BLOBS) - { - _effect = new Animation_GreenMoodBlobs(); - } - else if (name == ANIM_WARM_MOOD_BLOBS) - { - _effect = new Animation_WarmMoodBlobs(); - } - else if (name == ANIM_BREATH) - { - _effect = new Animation_Breath(); - } - else if (name == ANIM_CINEMA_BRIGHTEN_LIGHTS) - { - _effect = new Animation_CinemaBrightenLights(); - } - else if (name == ANIM_CINEMA_DIM_LIGHTS) - { - _effect = new Animation_CinemaDimLights(); - } - else if (name == ANIM_NOTIFY_BLUE) - { - _effect = new Animation_NotifyBlue(); - } - else if (name == ANIM_STROBE_RED) - { - _effect = new Animation_StrobeRed(); - } - else if (name == ANIM_SPARKS) - { - _effect = new Animation_Sparks(); - } - else if (name == ANIM_STROBE_WHITE) - { - _effect = new Animation_StrobeWhite(); - } - else if (name == ANIM_SYSTEM_SHUTDOWN) - { - _effect = new Animation_SystemShutdown(); - } - else if (name == ANIM_CANDLE) - { - _effect = new Animation_Candle(); - } - else if (name == AMUSIC_TESTEQ) - { - _effect = new Animation4Music_TestEq(); - } - else if (name == AMUSIC_PULSEWHITE) - { - _effect = new Animation4Music_PulseWhite(); - } - else if (name == AMUSIC_PULSEYELLOW) - { - _effect = new Animation4Music_PulseYellow(); - } - else if (name == AMUSIC_PULSERED) - { - _effect = new Animation4Music_PulseRed(); - } - else if (name == AMUSIC_PULSEGREEN) - { - _effect = new Animation4Music_PulseGreen(); - } - else if (name == AMUSIC_PULSEBLUE) - { - _effect = new Animation4Music_PulseBlue(); - } - else if (name == AMUSIC_PULSEMULTI) - { - _effect = new Animation4Music_PulseMulti(); - } - else if (name == AMUSIC_PULSEMULTIFAST) - { - _effect = new Animation4Music_PulseMultiFast(); - } - else if (name == AMUSIC_PULSEMULTISLOW) - { - _effect = new Animation4Music_PulseMultiSlow(); - } - - - else if (name == AMUSIC_STEREOWHITE) - { - _effect = new Animation4Music_StereoWhite(); - } - else if (name == AMUSIC_STEREOYELLOW) - { - _effect = new Animation4Music_StereoYellow(); - } - else if (name == AMUSIC_STEREORED) - { - _effect = new Animation4Music_StereoRed(); - } - else if (name == AMUSIC_STEREOGREEN) - { - _effect = new Animation4Music_StereoGreen(); - } - else if (name == AMUSIC_STEREOBLUE) - { - _effect = new Animation4Music_StereoBlue(); - } - else if (name == AMUSIC_STEREOMULTI) - { - _effect = new Animation4Music_StereoMulti(); - } - else if (name == AMUSIC_STEREOMULTIFAST) - { - _effect = new Animation4Music_StereoMultiFast(); - } - else if (name == AMUSIC_STEREOMULTISLOW) - { - _effect = new Animation4Music_StereoMultiSlow(); - } - - - else if (name == AMUSIC_QUATROWHITE) - { - _effect = new Animation4Music_QuatroWhite(); - } - else if (name == AMUSIC_QUATROYELLOW) - { - _effect = new Animation4Music_QuatroYellow(); - } - else if (name == AMUSIC_QUATRORED) - { - _effect = new Animation4Music_QuatroRed(); - } - else if (name == AMUSIC_QUATROGREEN) - { - _effect = new Animation4Music_QuatroGreen(); - } - else if (name == AMUSIC_QUATROBLUE) - { - _effect = new Animation4Music_QuatroBlue(); - } - else if (name == AMUSIC_QUATROMULTI) - { - _effect = new Animation4Music_QuatroMulti(); - } - else if (name == AMUSIC_QUATROMULTIFAST) - { - _effect = new Animation4Music_QuatroMultiFast(); - } - else if (name == AMUSIC_QUATROMULTISLOW) - { - _effect = new Animation4Music_QuatroMultiSlow(); - } +Effect::~Effect() +{ + delete _effect; - else if (name == AMUSIC_WAVESPULSE) - { - _effect = new Animation4Music_WavesPulse(); - } - else if (name == AMUSIC_WAVESPULSEFAST) - { - _effect = new Animation4Music_WavesPulseFast(); - } - else if (name == AMUSIC_WAVESPULSESLOW) - { - _effect = new Animation4Music_WavesPulseSlow(); - } + Info(_log, "Effect named: '%s' is deleted", QSTRING_CSTR(_name)); } -Effect::~Effect() +void Effect::start() { - Info(_log, "Deleting effect named: '%s'", QSTRING_CSTR(_name)); + _ledBuffer.resize(_ledCount); - if (_effect != nullptr) - { - delete _effect; - _effect = nullptr; - } + _effect->Init(_image, 10); - if (_painter != nullptr) - { - delete _painter; - _painter = nullptr; - } + if (_timeout > 0) + _endTime = InternalClock::now() + _timeout; + else + _endTime = -1; - Info(_log, "Effect named: '%s' is deleted", QSTRING_CSTR(_name)); + _timer.setInterval(_effect->GetSleepTime()); + + Info(_log, "Begin playing the effect with priority: %i", _priority); + + run(); + _timer.start(); } void Effect::run() { - if (_effect == NULL) + int left = (_timeout >= 0) ? std::max(static_cast(_endTime - InternalClock::now()), 0) : -1; + + if (_interrupt || (left == 0 && _timeout > 0) || _effect->isStop()) { - Error(_log, "Unable to find effect by this name. Please review configuration. Effect name: '%s'", QSTRING_CSTR(_name)); + stop(); return; } - int latchTime = 10; + if (_visiblePriority < _priority) + return; - _ledBuffer.resize(_ledCount); + bool hasLedData = false; - _effect->Init(_image, latchTime); + if (!_effect->hasOwnImage()) + { + _painter.begin(&_image); + _effect->Play(&_painter); + _painter.end(); - _painter = new QPainter(&_image); + hasLedData = _effect->hasLedData(_ledBuffer); + } - if (_timeout > 0) + if (_effect->hasOwnImage()) { - _endTime = InternalClock::now() + _timeout; + Image image(80, 45); + + if (_effect->getImage(image)) + emit SignalSetImage(_priority, image, left, false); } - - if (_effect->isSoundEffect()) + else if (hasLedData) { - if (!_interupt) - SAFE_CALL_0_RET(SoundCapture::getInstance(), getCaptureInstance, uint32_t, _soundHandle) - else - return; + ledShow(left); } - - Info(_log, "Begin playing the effect with priority: %i", _priority); - while (!_interupt && (_timeout <= 0 || InternalClock::now() < _endTime)) + else { - if (_priority > 0) - { - if (_visiblePriority < _priority) - { - if (!_interupt && (_timeout <= 0 || InternalClock::now() < _endTime)) - QThread::msleep(500); - continue; - } - } - - bool hasLedData = false; - - if (!_effect->hasOwnImage()) - { - _effect->Play(_painter); - - hasLedData = _effect->hasLedData(_ledBuffer); - } - - int micro = _effect->GetSleepTime(); - qint64 dieTime = InternalClock::now() + micro; - - if (_effect->hasOwnImage()) - { - Image image(80, 45); - int timeout = _timeout; - if (timeout > 0) - { - timeout = _endTime - InternalClock::now(); - if (timeout <= 0) - break; - } - - if (_effect->getImage(image)) - emit setInputImage(_priority, image, timeout, false); - } - else if (hasLedData) - { - if (!LedShow()) - break; - } - else - { - ImageShow(); - } - - if (_effect->isStop()) - break; - - while (!_interupt && InternalClock::now() < dieTime && - (_timeout <= 0 || InternalClock::now() < _endTime)) - { - micro = dieTime - InternalClock::now(); - while (micro > 200) - micro /= 2; - - if (micro > 0) - QThread::msleep(micro); - } + imageShow(left); } - Info(_log, "The effect quits with priority: %i", _priority); - - if (_soundHandle != 0) + int sleepTime = std::min(_effect->GetSleepTime(), 100); + if (sleepTime != _timer.interval()) { - Info(_log, "Releasing sound handle %i for effect named: '%s'", _soundHandle, QSTRING_CSTR(_name)); - QUEUE_CALL_1(SoundCapture::getInstance(), releaseCaptureInstance, uint32_t, _soundHandle); - _soundHandle = 0; + _timer.setInterval(sleepTime); } } -bool Effect::LedShow() +void Effect::stop() { - if (_interupt) - return false; + Info(_log, "The effect quits with priority: %i", _priority); - int timeout = _timeout; - if (timeout > 0) - { - timeout = _endTime - InternalClock::now(); - if (timeout <= 0) - return false; - } + _timer.stop(); + + emit SignalEffectFinished(_priority, _name, _interrupt); +} +void Effect::ledShow(int left) +{ if (_ledCount == _ledBuffer.length()) { QVector _cQV = _ledBuffer; - emit setInput(_priority, std::vector(_cQV.begin(), _cQV.end()), timeout, false); + emit SignalSetLeds(_priority, std::vector(_cQV.begin(), _cQV.end()), left, false); } else { Warning(_log, "Mismatch led number detected for the effect"); _ledBuffer.resize(_ledCount); } - - return true; } -bool Effect::ImageShow() +void Effect::imageShow(int left) { - if (_interupt) - return false; - - int timeout = _timeout; - if (timeout > 0) - { - timeout = _endTime - InternalClock::now(); - if (timeout <= 0) - return false; - } - int width = _image.width(); int height = _image.height(); Image image(width, height); - QByteArray binaryImage; + uint8_t* rawColors = image.rawMem(); - for (int i = 0; i < height; ++i) + for (int i = 0; i < height; i++) { const QRgb* scanline = reinterpret_cast(_image.scanLine(i)); - for (int j = 0; j < width; ++j) + for (int j = 0; j < width; j++) { - binaryImage.append((char)qRed(scanline[j])); - binaryImage.append((char)qGreen(scanline[j])); - binaryImage.append((char)qBlue(scanline[j])); + *(rawColors++) = qRed(scanline[j]); + *(rawColors++) = qGreen(scanline[j]); + *(rawColors++) = qBlue(scanline[j]); } } - memcpy(image.rawMem(), binaryImage.data(), binaryImage.size()); - emit setInputImage(_priority, image, timeout, false); - - return true; + emit SignalSetImage(_priority, image, left, false); } void Effect::visiblePriorityChanged(quint8 priority) { _visiblePriority = priority; + + if (_timeout <= 0) + { + if (_visiblePriority < _priority) + _timer.stop(); + else if (!_timer.isActive()) + _timer.start(); + } } void Effect::setLedCount(int newCount) @@ -539,16 +254,12 @@ void Effect::setLedCount(int newCount) _ledCount = newCount; } -int Effect::getPriority() const { +int Effect::getPriority() const { return _priority; } void Effect::requestInterruption() { - _interupt = true; -} - -bool Effect::isInterruptionRequested() { - return _interupt; + _interrupt = true; } QString Effect::getName() const { @@ -559,6 +270,131 @@ int Effect::getTimeout() const { return _timeout; } -QJsonObject Effect::getArgs() const { - return _args; +QString Effect::getDescription() const +{ + return QString("effect%1/%2 => \"%3\""). + arg(_instanceIndex). + arg(_priority). + arg(_name); +} + + +std::list Effect::getAvailableEffects() +{ + std::list _availableEffects; + + _availableEffects.push_back(Animation4Music_WavesPulse::getDefinition()); + + _availableEffects.push_back(Animation4Music_WavesPulseFast::getDefinition()); + + _availableEffects.push_back(Animation4Music_WavesPulseSlow::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseMulti::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseMultiFast::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseMultiSlow::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseYellow::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseWhite::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseRed::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseGreen::getDefinition()); + + _availableEffects.push_back(Animation4Music_PulseBlue::getDefinition()); + + + _availableEffects.push_back(Animation4Music_StereoMulti::getDefinition()); + + _availableEffects.push_back(Animation4Music_StereoMultiFast::getDefinition()); + + _availableEffects.push_back(Animation4Music_StereoMultiSlow::getDefinition()); + + _availableEffects.push_back(Animation4Music_StereoYellow::getDefinition()); + + _availableEffects.push_back(Animation4Music_StereoWhite::getDefinition()); + + _availableEffects.push_back(Animation4Music_StereoRed::getDefinition()); + + _availableEffects.push_back(Animation4Music_StereoGreen::getDefinition()); + + _availableEffects.push_back(Animation4Music_StereoBlue::getDefinition()); + + + _availableEffects.push_back(Animation4Music_QuatroMulti::getDefinition()); + + _availableEffects.push_back(Animation4Music_QuatroMultiFast::getDefinition()); + + _availableEffects.push_back(Animation4Music_QuatroMultiSlow::getDefinition()); + + _availableEffects.push_back(Animation4Music_QuatroYellow::getDefinition()); + + _availableEffects.push_back(Animation4Music_QuatroWhite::getDefinition()); + + _availableEffects.push_back(Animation4Music_QuatroRed::getDefinition()); + + _availableEffects.push_back(Animation4Music_QuatroGreen::getDefinition()); + + _availableEffects.push_back(Animation4Music_QuatroBlue::getDefinition()); + + + + + _availableEffects.push_back(Animation4Music_TestEq::getDefinition()); + + _availableEffects.push_back(Animation_AtomicSwirl::getDefinition()); + + _availableEffects.push_back(Animation_BlueMoodBlobs::getDefinition()); + + _availableEffects.push_back(Animation_Breath::getDefinition()); + + _availableEffects.push_back(Animation_Candle::getDefinition()); + + _availableEffects.push_back(Animation_CinemaBrightenLights::getDefinition()); + + _availableEffects.push_back(Animation_CinemaDimLights::getDefinition()); + + _availableEffects.push_back(Animation_ColdMoodBlobs::getDefinition()); + + _availableEffects.push_back(Animation_DoubleSwirl::getDefinition()); + + _availableEffects.push_back(Animation_FullColorMoodBlobs::getDefinition()); + + _availableEffects.push_back(Animation_GreenMoodBlobs::getDefinition()); + + _availableEffects.push_back(Animation_KnightRider::getDefinition()); + + _availableEffects.push_back(Animation_NotifyBlue::getDefinition()); + + _availableEffects.push_back(Animation_Plasma::getDefinition()); + + _availableEffects.push_back(Animation_PoliceLightsSingle::getDefinition()); + + _availableEffects.push_back(Animation_PoliceLightsSolid::getDefinition()); + + _availableEffects.push_back(Animation_RainbowSwirl::getDefinition()); + + _availableEffects.push_back(Animation_SwirlFast::getDefinition()); + + _availableEffects.push_back(Animation_RainbowWaves::getDefinition()); + + _availableEffects.push_back(Animation_RedMoodBlobs::getDefinition()); + + _availableEffects.push_back(Animation_SeaWaves::getDefinition()); + + _availableEffects.push_back(Animation_Sparks::getDefinition()); + + _availableEffects.push_back(Animation_StrobeRed::getDefinition()); + + _availableEffects.push_back(Animation_StrobeWhite::getDefinition()); + + _availableEffects.push_back(Animation_SystemShutdown::getDefinition()); + + _availableEffects.push_back(Animation_WarmMoodBlobs::getDefinition()); + + _availableEffects.push_back(Animation_WavesWithColor::getDefinition()); + + return _availableEffects; } diff --git a/sources/effectengine/EffectDBHandler.cpp b/sources/effectengine/EffectDBHandler.cpp index 8155315f2..0c0abac93 100644 --- a/sources/effectengine/EffectDBHandler.cpp +++ b/sources/effectengine/EffectDBHandler.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,8 +25,15 @@ * SOFTWARE. */ -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include +#endif +#include #include #include @@ -84,43 +91,11 @@ #include #include -// util #include -// qt -#include -#include -#include -#include -#include - -EffectDBHandler* EffectDBHandler::efhInstance = NULL; - -EffectDBHandler::EffectDBHandler(const QString& rootPath, const QJsonDocument& effectConfig, QObject* parent) - : QObject(parent) - , _effectConfig() - , _log(Logger::getInstance("EFFECTDB")) - , _rootPath(rootPath) -{ - EffectDBHandler::efhInstance = this; - - // init - handleSettingsUpdate(settings::type::EFFECTS, effectConfig); -} - -void EffectDBHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +std::list EffectDBHandler::getEffects() { - if (type == settings::type::EFFECTS) - { - _effectConfig = config.object(); - updateEffects(); - } -} - - -void EffectDBHandler::updateEffects() -{ - _availableEffects.clear(); + std::list _availableEffects; _availableEffects.push_back(Animation4Music_WavesPulse::getDefinition()); @@ -235,17 +210,5 @@ void EffectDBHandler::updateEffects() _availableEffects.push_back(Animation_WavesWithColor::getDefinition()); - ErrorIf(_availableEffects.size() == 0, _log, "No effects found... something gone wrong"); - - emit effectListChanged(); -} - -EffectDBHandler* EffectDBHandler::getInstance() -{ - return efhInstance; -} - -std::list EffectDBHandler::getEffects() const -{ return _availableEffects; } diff --git a/sources/effectengine/EffectEngine.cpp b/sources/effectengine/EffectEngine.cpp index 2d56699f7..16caf48b4 100644 --- a/sources/effectengine/EffectEngine.cpp +++ b/sources/effectengine/EffectEngine.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,110 +25,108 @@ * SOFTWARE. */ -// Qt includes -#include +#ifndef PCH_ENABLED + #include +#endif +#include + +#include #include #include #include -// effect engine includes #include #include -#include -#include "HyperhdrConfig.h" EffectEngine::EffectEngine(HyperHdrInstance* hyperhdr) : _hyperInstance(hyperhdr) - , _availableEffects() - , _activeEffects() + , _availableEffects(Effect::getAvailableEffects()) , _log(Logger::getInstance(QString("EFFECTENGINE%1").arg(hyperhdr->getInstanceIndex()))) - , _effectDBHandler(EffectDBHandler::getInstance()) { qRegisterMetaType("hyperhdr::Components"); - // connect the HyperHDR channel clear feedback - connect(_hyperInstance, &HyperHdrInstance::channelCleared, this, &EffectEngine::channelCleared); - connect(_hyperInstance, &HyperHdrInstance::allChannelsCleared, this, &EffectEngine::allChannelsCleared); - - // get notifications about refreshed effect list - connect(_effectDBHandler, &EffectDBHandler::effectListChanged, this, &EffectEngine::handleUpdatedEffectList); - // register smooth cfgs and fill available effects - handleUpdatedEffectList(); + createSmoothingConfigs(); } EffectEngine::~EffectEngine() { - auto copy = _activeEffects; + _activeEffects.clear(); - _activeEffects.clear(); + Debug(_log, "EffectEngine is released"); +} - for (Effect* effect : copy) - { - effect->requestInterruption(); - effect->quit(); - } +int EffectEngine::runEffectScript(const QString& name, int priority, int timeout, const QString& origin) +{ + // find the effect's definition + std::list::iterator it = std::find_if( + _availableEffects.begin(), + _availableEffects.end(), + [&name](const EffectDefinition& s) { + return s.name == name; + }); - for (Effect* effect : copy) + if (it == _availableEffects.end()) { - effect->wait(); - delete effect; + Debug(_log, "Could not find the effect named: %s", QSTRING_CSTR(name)); + return 0; } - Debug(_log, "EffectEngine is released"); -} - -int EffectEngine::runEffectScript(const QString& name, const QJsonObject& args, int priority, int timeout, const QString& origin, unsigned smoothCfg, const QString& imageData) -{ // clear current effect on the channel channelCleared(priority); // create the effect - Effect* effect = new Effect(_hyperInstance, _hyperInstance->getCurrentPriority(), priority, timeout, name, args, imageData); - connect(effect, &Effect::setInput, this, &EffectEngine::gotLedsHandler, Qt::QueuedConnection); - connect(effect, &Effect::setInputImage, _hyperInstance, &HyperHdrInstance::setInputImage, Qt::QueuedConnection); - connect(effect, &QThread::finished, this, &EffectEngine::effectFinished); - connect(_hyperInstance, &HyperHdrInstance::finished, effect, &Effect::requestInterruption, Qt::DirectConnection); - _activeEffects.push_back(effect); + auto effect = std::unique_ptr( + new Effect(_hyperInstance, _hyperInstance->getCurrentPriority(), priority, timeout, *it), + [](Effect* oldEffect) { + oldEffect->requestInterruption(); + hyperhdr::THREAD_REMOVER(oldEffect->getDescription(), oldEffect->thread(), oldEffect); + } + ); + connect(effect.get(), &Effect::SignalSetLeds, this, &EffectEngine::handlerSetLeds); + connect(effect.get(), &Effect::SignalSetImage, _hyperInstance, &HyperHdrInstance::setInputImage); + connect(effect.get(), &Effect::SignalEffectFinished, this, &EffectEngine::handlerEffectFinished); // start the effect - Debug(_log, "Start the effect: name [%s], smoothCfg [%u]", QSTRING_CSTR(name), smoothCfg); - _hyperInstance->registerInput(priority, hyperhdr::COMP_EFFECT, origin, name, smoothCfg); + Debug(_log, "Start the effect: name [%s]", QSTRING_CSTR(name)); + _hyperInstance->registerInput(priority, hyperhdr::COMP_EFFECT, origin, name, (*it).smoothingConfig); - effect->start(); + // start the effect + QThread* newThread = new QThread(); + effect->moveToThread(newThread); + newThread->start(); + QUEUE_CALL_0(effect.get(), start); + + // enlist & move control + _activeEffects.push_back(std::move(effect)); return 0; } -void EffectEngine::effectFinished() +void EffectEngine::handlerEffectFinished(int priority, QString name, bool forced) { - Effect* effect = qobject_cast(sender()); - if (!effect->isInterruptionRequested()) + if (!forced) { - // effect stopped by itself. Clear the channel - _hyperInstance->clear(effect->getPriority()); + _hyperInstance->clear(priority); } - Info(_log, "Effect '%s' has finished.", QSTRING_CSTR(effect->getName())); + Info(_log, "Effect '%s' has finished.", QSTRING_CSTR(name)); for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt) { - if (*effectIt == effect) + if ((*effectIt).get() == sender()) { _activeEffects.erase(effectIt); break; } } - - // cleanup the effect - effect->deleteLater(); } void EffectEngine::visiblePriorityChanged(quint8 priority) { - for (Effect* effect : _activeEffects) + for (auto&& effect : _activeEffects) { - effect->visiblePriorityChanged(priority); + QUEUE_CALL_1(effect.get(), visiblePriorityChanged, quint8, priority); } } @@ -141,148 +139,65 @@ std::list EffectEngine::getActiveEffects() const { std::list availableActiveEffects; - for (Effect* effect : _activeEffects) + for (const auto& effect : _activeEffects) { ActiveEffectDefinition activeEffectDefinition; activeEffectDefinition.name = effect->getName(); activeEffectDefinition.priority = effect->getPriority(); activeEffectDefinition.timeout = effect->getTimeout(); - activeEffectDefinition.args = effect->getArgs(); availableActiveEffects.push_back(activeEffectDefinition); } return availableActiveEffects; } -void EffectEngine::gotLedsHandler(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect) +void EffectEngine::handlerSetLeds(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect) { int ledNum = _hyperInstance->getLedCount(); if (ledNum == static_cast(ledColors.size())) - emit _hyperInstance->setInput(priority, ledColors, timeout_ms, false); - else for (Effect* effect : _activeEffects) + _hyperInstance->setInputLeds(priority, ledColors, timeout_ms, false); + else for (auto&& effect : _activeEffects) { effect->setLedCount(ledNum); } } -void EffectEngine::cacheRunningEffects() +void EffectEngine::createSmoothingConfigs() { - _cachedActiveEffects.clear(); - - for (Effect* effect : _activeEffects) - { - ActiveEffectDefinition activeEffectDefinition; - activeEffectDefinition.name = effect->getName(); - activeEffectDefinition.priority = effect->getPriority(); - activeEffectDefinition.timeout = effect->getTimeout(); - activeEffectDefinition.args = effect->getArgs(); - _cachedActiveEffects.push_back(activeEffectDefinition); - channelCleared(effect->getPriority()); - } -} - -void EffectEngine::startCachedEffects() -{ - for (const auto& def : _cachedActiveEffects) - { - // the smooth cfg AND origin are ignored for this start! - runEffect(def.name, def.args, def.priority, def.timeout); - } - _cachedActiveEffects.clear(); -} - -void EffectEngine::handleUpdatedEffectList() -{ - _availableEffects.clear(); - unsigned id = 2; unsigned dynamicId = 3; _hyperInstance->updateSmoothingConfig(id); - for (auto def : _effectDBHandler->getEffects()) + for (EffectDefinition& def : _availableEffects) { // add smoothing configs to HyperHdr - if (def.args["smoothing-custom-settings"].toBool()) + if (def.smoothingCustomSettings) { - def.smoothCfg = _hyperInstance->updateSmoothingConfig( + def.smoothingConfig = _hyperInstance->updateSmoothingConfig( dynamicId++, - def.args["smoothing-time_ms"].toInt(200), - def.args["smoothing-updateFrequency"].toDouble(25), - def.args["smoothing-direct-mode"].toBool(false)); + def.smoothingTime, + def.smoothingFrequency, + def.smoothingDirectMode); } else { - def.smoothCfg = _hyperInstance->updateSmoothingConfig(id); + def.smoothingConfig = _hyperInstance->updateSmoothingConfig(id); } - _availableEffects.push_back(def); } } int EffectEngine::runEffect(const QString& effectName, int priority, int timeout, const QString& origin) -{ - unsigned smoothCfg = 0; - for (auto def : _availableEffects) - { - if (def.name == effectName) - { - smoothCfg = def.smoothCfg; - break; - } - } - return runEffect(effectName, QJsonObject(), priority, timeout, origin, smoothCfg); -} - -int EffectEngine::runEffect(const QString& effectName, const QJsonObject& args, int priority, int timeout, const QString& origin, unsigned smoothCfg, const QString& imageData) { Info(_log, "Run effect \"%s\" on channel %d", QSTRING_CSTR(effectName), priority); - return runEffectScript(effectName, args, priority, timeout, origin, smoothCfg, imageData); -} - -void EffectEngine::handleInitialEffect(HyperHdrInstance* hyperhdr, const QJsonObject& FGEffectConfig) -{ - const int FG_PRIORITY = 0; - const int DURATION_INFINITY = 0; - - // initial foreground effect/color - if (FGEffectConfig["enable"].toBool(true)) - { - const QString fgTypeConfig = FGEffectConfig["type"].toString("effect"); - const QString fgEffectConfig = FGEffectConfig["effect"].toString("Rainbow swirl fast"); - const QJsonValue fgColorConfig = FGEffectConfig["color"]; - int default_fg_duration_ms = 3000; - int fg_duration_ms = FGEffectConfig["duration_ms"].toInt(default_fg_duration_ms); - if (fg_duration_ms == DURATION_INFINITY) - { - fg_duration_ms = default_fg_duration_ms; - Warning(Logger::getInstance("HYPERHDR"), "foreground effect duration 'infinity' is forbidden, set to default value %d ms", default_fg_duration_ms); - } - if (fgTypeConfig.contains("color")) - { - auto FGCONFIG_ARRAY = fgColorConfig.toArray(); - std::vector fg_color = { - ColorRgb { - static_cast(FGCONFIG_ARRAY.at(0).toInt(0)), - static_cast(FGCONFIG_ARRAY.at(1).toInt(0)), - static_cast(FGCONFIG_ARRAY.at(2).toInt(0)) - } - }; - hyperhdr->setColor(FG_PRIORITY, fg_color, fg_duration_ms); - Info(Logger::getInstance("HYPERHDR"), "Initial foreground color set (%d %d %d)", fg_color.at(0).red, fg_color.at(0).green, fg_color.at(0).blue); - } - else - { - int result = hyperhdr->setEffect(fgEffectConfig, FG_PRIORITY, fg_duration_ms); - Info(Logger::getInstance("HYPERHDR"), "Initial foreground effect '%s' %s", QSTRING_CSTR(fgEffectConfig), ((result == 0) ? "started" : "failed")); - } - } + return runEffectScript(effectName, priority, timeout, origin); } void EffectEngine::channelCleared(int priority) { - for (Effect* effect : _activeEffects) + for (auto&& effect : _activeEffects) { - if (effect->getPriority() == priority && !effect->isInterruptionRequested()) + if (effect->getPriority() == priority) { effect->requestInterruption(); } @@ -291,9 +206,9 @@ void EffectEngine::channelCleared(int priority) void EffectEngine::allChannelsCleared() { - for (Effect* effect : _activeEffects) + for (auto&& effect : _activeEffects) { - if (effect->getPriority() != PriorityMuxer::LOWEST_EFFECT_PRIORITY && !effect->isInterruptionRequested()) + if (effect->getPriority() != Muxer::LOWEST_EFFECT_PRIORITY) { effect->requestInterruption(); } diff --git a/sources/flatbufserver/FlatBufferClient.cpp b/sources/flatbufserver/FlatBufferClient.cpp index 9daa1ae50..9cf01b14c 100644 --- a/sources/flatbufserver/FlatBufferClient.cpp +++ b/sources/flatbufserver/FlatBufferClient.cpp @@ -10,7 +10,7 @@ // util includes #include -FlatBufferClient::FlatBufferClient(QTcpSocket* socket, QLocalSocket* domain, int timeout, int hdrToneMappingEnabled, uint8_t* lutBuffer, QObject* parent) +FlatBufferClient::FlatBufferClient(QTcpSocket* socket, QLocalSocket* domain, int timeout, QObject* parent) : QObject(parent) , _log(Logger::getInstance("FLATBUFSERVER")) , _socket(socket) @@ -18,9 +18,7 @@ FlatBufferClient::FlatBufferClient(QTcpSocket* socket, QLocalSocket* domain, int , _clientAddress("@LocalSocket") , _timeoutTimer(new QTimer(this)) , _timeout(timeout * 1000) - , _priority() - , _hdrToneMappingMode(hdrToneMappingEnabled) - , _lutBuffer(lutBuffer) + , _priority(140) { if (_socket != nullptr) _clientAddress = "@" + _socket->peerAddress().toString(); @@ -89,12 +87,6 @@ void FlatBufferClient::forceClose() _domain->close(); } -void FlatBufferClient::setHdrToneMappingEnabled(int mode, uint8_t* lutBuffer) -{ - _hdrToneMappingMode = mode; - _lutBuffer = lutBuffer; -} - void FlatBufferClient::disconnected() { Debug(_log, "Socket Closed"); @@ -104,10 +96,10 @@ void FlatBufferClient::disconnected() if (_domain != nullptr) _domain->deleteLater(); - if (_priority != 0 && _priority >= 100 && _priority < 200) - emit clearGlobalInput(_priority); + if (_priority >= 50 && _priority <= 250) + emit SignalClearGlobalInput(_priority, false); - emit clientDisconnected(); + emit SignalClientDisconnected(this); } void FlatBufferClient::handleMessage(const hyperhdrnet::Request* req) @@ -137,39 +129,25 @@ void FlatBufferClient::handleColorCommand(const hyperhdrnet::Color* colorReq) std::vector color{ ColorRgb{ uint8_t(qRed(rgbData)), uint8_t(qGreen(rgbData)), uint8_t(qBlue(rgbData)) } }; // set output - emit setGlobalInputColor(_priority, color, colorReq->duration()); + emit SignalSetGlobalColor(_priority, color, colorReq->duration(), hyperhdr::Components::COMP_FLATBUFSERVER, _clientDescription); // send reply sendSuccessReply(); } -void FlatBufferClient::registationRequired(int priority) -{ - if (_priority == priority) - { - auto reply = hyperhdrnet::CreateReplyDirect(_builder, nullptr, -1, -1); - _builder.Finish(reply); - - // send reply - sendMessage(); - - _builder.Clear(); - } -} - void FlatBufferClient::handleRegisterCommand(const hyperhdrnet::Register* regReq) { - if (regReq->priority() < 100 || regReq->priority() >= 200) + if (regReq->priority() < 50 || regReq->priority() > 250) { - Error(_log, "Register request from client %s contains invalid priority %d. Valid priority for Flatbuffer connections is between 100 and 199.", QSTRING_CSTR(_clientAddress), regReq->priority()); - sendErrorReply("The priority " + std::to_string(regReq->priority()) + " is not in the priority range between 100 and 199."); + Error(_log, "Register request from client %s contains invalid priority %d. Valid priority for Flatbuffer connections is between 50 and 250.", QSTRING_CSTR(_clientAddress), regReq->priority()); + sendErrorReply("The priority " + std::to_string(regReq->priority()) + " is not in the priority range between 50 and 250."); return; } _priority = regReq->priority(); - emit registerGlobalInput(_priority, hyperhdr::COMP_FLATBUFSERVER, regReq->origin()->c_str() + _clientAddress); + _clientDescription = regReq->origin()->c_str() + _clientAddress; - auto reply = hyperhdrnet::CreateReplyDirect(_builder, nullptr, -1, (_priority ? _priority : -1)); + auto reply = hyperhdrnet::CreateReplyDirect(_builder, nullptr, -1, _priority); _builder.Finish(reply); // send reply @@ -200,10 +178,7 @@ void FlatBufferClient::handleImageCommand(const hyperhdrnet::Image* image) Image imageDest(width, height); memmove(imageDest.rawMem(), imageData->data(), imageData->size()); - // tone mapping - FrameDecoder::applyLUT(imageDest.rawMem(), imageDest.width(), imageDest.height(), _lutBuffer, _hdrToneMappingMode); - - emit setGlobalInputImage(_priority, imageDest, duration); + emit SignalImageReceived(_priority, imageDest, duration, hyperhdr::Components::COMP_FLATBUFSERVER, _clientDescription); } // send reply @@ -214,14 +189,9 @@ void FlatBufferClient::handleImageCommand(const hyperhdrnet::Image* image) void FlatBufferClient::handleClearCommand(const hyperhdrnet::Clear* clear) { // extract parameters - const int priority = clear->priority(); - - // Check if we are clearing ourselves. - if (priority == _priority) { - _priority = -1; - } + const int priority = clear->priority(); - emit clearGlobalInput(priority); + emit SignalClearGlobalInput(priority, false); sendSuccessReply(); } diff --git a/sources/flatbufserver/FlatBufferClient.h b/sources/flatbufserver/FlatBufferClient.h index ddac6b195..47773359d 100644 --- a/sources/flatbufserver/FlatBufferClient.h +++ b/sources/flatbufserver/FlatBufferClient.h @@ -14,124 +14,35 @@ class QTcpSocket; class QLocalSocket; class QTimer; -/// -/// @brief Socket (client) of FlatBufferServer -/// class FlatBufferClient : 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 FlatBufferClient(QTcpSocket* socket, QLocalSocket* domain, int timeout, int hdrToneMappingEnabled, uint8_t* lutBuffer, QObject* parent = nullptr); + explicit FlatBufferClient(QTcpSocket* socket, QLocalSocket* domain, int timeout, QObject* parent = nullptr); signals: - /// - /// @brief forward register data to HyperHDRDaemon - /// - void registerGlobalInput(int priority, hyperhdr::Components component, const QString& origin = "FlatBuffer", const QString& owner = "", unsigned smooth_cfg = 0); - - /// - /// @brief Forward clear command to HyperHDRDaemon - /// - void clearGlobalInput(int priority, bool forceClearAll = false); - - /// - /// @brief forward prepared image to HyperHDRDaemon - /// - bool setGlobalInputImage(int priority, const Image& image, int timeout_ms, bool clearEffect = false); - - /// - /// @brief Forward requested color - /// - void setGlobalInputColor(int priority, const std::vector& ledColor, int timeout_ms, const QString& origin = "FlatBuffer", bool clearEffects = true); - - /// - /// @brief Emits whenever the client disconnected - /// - void clientDisconnected(); + void SignalClearGlobalInput(int priority, bool forceClearAll); + void SignalImageReceived(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin, QString clientDescription); + void SignalSetGlobalColor(int priority, const std::vector& ledColor, int timeout_ms, hyperhdr::Components origin, QString clientDescription); + void SignalClientDisconnected(FlatBufferClient* client); public slots: - /// - /// @brief Requests a registration from the client - /// - void registationRequired(int priority); - - /// - /// @brief close the socket and call disconnected() - /// void forceClose(); - /// - /// @brief Change HDR tone mapping - /// - void setHdrToneMappingEnabled(int mode, uint8_t* lutBuffer); - 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: - /// - /// @brief Handle the received message - /// void handleMessage(const hyperhdrnet::Request* req); - - /// - /// Register new priority - /// void handleRegisterCommand(const hyperhdrnet::Register* regReq); - - /// - /// @brief Hande Color message - /// void handleColorCommand(const hyperhdrnet::Color* colorReq); - - /// - /// Handle an incoming Image message - /// - /// @param image the incoming image - /// void handleImageCommand(const hyperhdrnet::Image* image); - - /// - /// @brief Handle clear command - /// - /// @param clear the incoming clear request - /// void handleClearCommand(const hyperhdrnet::Clear* clear); - - /// - /// Send handle not implemented - /// void handleNotImplemented(); - - /// - /// Send a message to the connected client - /// void sendMessage(); - - /// - /// 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: @@ -142,13 +53,9 @@ private slots: QTimer* _timeoutTimer; int _timeout; int _priority; + QString _clientDescription; - QByteArray _receiveBuffer; + QByteArray _receiveBuffer; - // Flatbuffers builder flatbuffers::FlatBufferBuilder _builder; - - // tone mapping - int _hdrToneMappingMode; - uint8_t* _lutBuffer; }; diff --git a/sources/flatbufserver/FlatBufferConnection.cpp b/sources/flatbufserver/FlatBufferConnection.cpp index bf3433361..83d0f7974 100644 --- a/sources/flatbufserver/FlatBufferConnection.cpp +++ b/sources/flatbufserver/FlatBufferConnection.cpp @@ -11,9 +11,10 @@ #include "hyperhdr_reply_generated.h" #include "hyperhdr_request_generated.h" -FlatBufferConnection::FlatBufferConnection(const QString& origin, const QString& address, int priority, bool skipReply) - : _socket((address == HYPERHDR_DOMAIN_SERVER) ? nullptr : new QTcpSocket()) - , _domain((address == HYPERHDR_DOMAIN_SERVER) ? new QLocalSocket() : nullptr) +FlatBufferConnection::FlatBufferConnection(QObject* parent, const QString& origin, const QString& address, int priority, bool skipReply) + : QObject(parent) + , _socket((address == HYPERHDR_DOMAIN_SERVER) ? nullptr : new QTcpSocket(this)) + , _domain((address == HYPERHDR_DOMAIN_SERVER) ? new QLocalSocket(this) : nullptr) , _origin(origin) , _priority(priority) , _prevSocketState(QAbstractSocket::UnconnectedState) @@ -72,7 +73,7 @@ FlatBufferConnection::FlatBufferConnection(const QString& origin, const QString& connect(&_timer, &QTimer::timeout, this, &FlatBufferConnection::connectToHost); _timer.start(); - connect(this, &FlatBufferConnection::onImage, this, &FlatBufferConnection::setImage); + connect(this, &FlatBufferConnection::SignalImageToSend, this, &FlatBufferConnection::sendImage); } FlatBufferConnection::~FlatBufferConnection() @@ -177,7 +178,7 @@ void FlatBufferConnection::setColor(const ColorRgb& color, int priority, int dur _builder.Clear(); } -void FlatBufferConnection::setImage(const Image& image) +void FlatBufferConnection::sendImage(const Image& image) { auto current = InternalClock::now(); auto outOfTime = (current - _lastSendImage); diff --git a/sources/flatbufserver/FlatBufferServer.cpp b/sources/flatbufserver/FlatBufferServer.cpp index c019e4af4..7bd1cf0e0 100644 --- a/sources/flatbufserver/FlatBufferServer.cpp +++ b/sources/flatbufserver/FlatBufferServer.cpp @@ -5,7 +5,7 @@ // util #include #include -#include +#include #include // qt @@ -19,49 +19,40 @@ #define LUT_FILE_SIZE 50331648 -FlatBufferServer* FlatBufferServer::instance = nullptr; - -FlatBufferServer::FlatBufferServer(const QJsonDocument& config, const QString& configurationPath, QObject* parent) +FlatBufferServer::FlatBufferServer(std::shared_ptr netOrigin, const QJsonDocument& config, const QString& configurationPath, QObject* parent) : QObject(parent) , _server(new QTcpServer(this)) , _domain(new QLocalServer(this)) - , _netOrigin(nullptr) + , _netOrigin(netOrigin) , _log(Logger::getInstance("FLATBUFSERVER")) , _timeout(5000) , _port(19400) , _config(config) , _hdrToneMappingMode(0) , _realHdrToneMappingMode(0) - , _lutBuffer(nullptr) , _lutBufferInit(false) , _configurationPath(configurationPath) , _userLutFile("") -{ - FlatBufferServer::instance = this; +{ } FlatBufferServer::~FlatBufferServer() { - stopServer(); + Debug(_log, "Prepare to shutdown"); - delete _server; - delete _domain; - - if (_lutBuffer != NULL) - free(_lutBuffer); - _lutBuffer = NULL; + stopServer(); - FlatBufferServer::instance = nullptr; + Debug(_log, "FlatBufferServer instance is closed"); } void FlatBufferServer::initServer() { - _netOrigin = NetOrigin::getInstance(); - if (_server != nullptr) - connect(_server, &QTcpServer::newConnection, this, &FlatBufferServer::newConnection); + connect(_server, &QTcpServer::newConnection, this, &FlatBufferServer::handlerNewConnection); if (_domain != nullptr) - connect(_domain, &QLocalServer::newConnection, this, &FlatBufferServer::newConnection); + connect(_domain, &QLocalServer::newConnection, this, &FlatBufferServer::handlerNewConnection); + + connect(this, &FlatBufferServer::SignalImportFromProto, this, &FlatBufferServer::handlerImportFromProto); // apply config handleSettingsUpdate(settings::type::FLATBUFSERVER, _config); @@ -69,24 +60,21 @@ void FlatBufferServer::initServer() loadLutFile(); } -void FlatBufferServer::setHdrToneMappingEnabled(int mode) +void FlatBufferServer::signalRequestSourceHandler(hyperhdr::Components component, int instanceIndex, bool listen) { - bool status = (_hdrToneMappingMode != 0) && (mode != 0); - - if (status) - loadLutFile(); - - _realHdrToneMappingMode = (_lutBufferInit && status) ? mode : 0; - - // inform clients - emit hdrToneMappingChanged(_realHdrToneMappingMode, _lutBuffer); - + if (component == hyperhdr::Components::COMP_HDR) + { + if (instanceIndex < 0) + { + bool status = (_hdrToneMappingMode != 0) && listen; -#if !defined(ENABLE_MF) && !defined(ENABLE_AVF) && !defined(ENABLE_V4L2) - emit HdrChanged(_realHdrToneMappingMode); - emit HyperHdrIManager::getInstance()->setNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, (_realHdrToneMappingMode != 0)); -#endif + if (status) + loadLutFile(); + _realHdrToneMappingMode = (_lutBufferInit && status) ? listen : 0; + } + emit SignalSetNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, (_realHdrToneMappingMode != 0)); + } } int FlatBufferServer::getHdrToneMappingEnabled() @@ -100,6 +88,15 @@ void FlatBufferServer::handleSettingsUpdate(settings::type type, const QJsonDocu { const QJsonObject& obj = config.object(); + if (obj.keys().contains(BASEAPI_FLATBUFFER_USER_LUT_FILE)) + { + QString filename = obj[BASEAPI_FLATBUFFER_USER_LUT_FILE].toString(); + _userLutFile = filename.replace("~", "").replace("/", "").replace("\\", "").replace("..", ""); + Info(_log, "Setting user LUT filename to: '%s'", QSTRING_CSTR(_userLutFile)); + return; + } + + quint16 port = obj["port"].toInt(19400); // port check @@ -112,7 +109,7 @@ void FlatBufferServer::handleSettingsUpdate(settings::type type, const QJsonDocu // HDR tone mapping _hdrToneMappingMode = obj["hdrToneMapping"].toBool(false) ? obj["hdrToneMappingMode"].toInt(1) : 0; - setHdrToneMappingEnabled(_hdrToneMappingMode); + signalRequestSourceHandler(hyperhdr::Components::COMP_HDR, -1, _hdrToneMappingMode); // new timeout just for new connections _timeout = obj["timeout"].toInt(5000); @@ -123,17 +120,14 @@ void FlatBufferServer::handleSettingsUpdate(settings::type type, const QJsonDocu void FlatBufferServer::setupClient(FlatBufferClient* client) { - connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected); - connect(client, &FlatBufferClient::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput); - connect(client, &FlatBufferClient::clearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput); - connect(client, &FlatBufferClient::setGlobalInputImage, GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage); - connect(client, &FlatBufferClient::setGlobalInputColor, GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor); - connect(GlobalSignals::getInstance(), &GlobalSignals::globalRegRequired, client, &FlatBufferClient::registationRequired); - connect(this, &FlatBufferServer::hdrToneMappingChanged, client, &FlatBufferClient::setHdrToneMappingEnabled); + connect(client, &FlatBufferClient::SignalClientDisconnected, this, &FlatBufferServer::handlerClientDisconnected); + connect(client, &FlatBufferClient::SignalClearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::SignalClearGlobalInput); + connect(client, &FlatBufferClient::SignalImageReceived, this, &FlatBufferServer::handlerImageReceived); + connect(client, &FlatBufferClient::SignalSetGlobalColor, GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalColor); _openConnections.append(client); } -void FlatBufferServer::newConnection() +void FlatBufferServer::handlerNewConnection() { while (_server != nullptr && _server->hasPendingConnections()) { @@ -142,7 +136,7 @@ void FlatBufferServer::newConnection() if (_netOrigin->accessAllowed(socket->peerAddress(), socket->localAddress())) { Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); - FlatBufferClient* client = new FlatBufferClient(socket, nullptr, _timeout, _hdrToneMappingMode, _lutBuffer, this); + FlatBufferClient* client = new FlatBufferClient(socket, nullptr, _timeout, this); // internal setupClient(client); } @@ -155,18 +149,20 @@ void FlatBufferServer::newConnection() if (QLocalSocket* socket = _domain->nextPendingConnection()) { Debug(_log, "New local domain connection"); - FlatBufferClient* client = new FlatBufferClient(nullptr, socket, _timeout, _hdrToneMappingMode, _lutBuffer, this); + FlatBufferClient* client = new FlatBufferClient(nullptr, socket, _timeout, this); // internal setupClient(client); } } } -void FlatBufferServer::clientDisconnected() +void FlatBufferServer::handlerClientDisconnected(FlatBufferClient* client) { - FlatBufferClient* client = qobject_cast(sender()); - client->deleteLater(); - _openConnections.removeAll(client); + if (client != nullptr) + { + client->deleteLater(); + _openConnections.removeAll(client); + } } void FlatBufferServer::startServer() @@ -195,9 +191,10 @@ void FlatBufferServer::stopServer() { if ((_server != nullptr &&_server->isListening()) || (_domain != nullptr && _domain->isListening())) { - // close client connections - for (const auto& client : _openConnections) + QVectorIterator i(_openConnections); + while (i.hasNext()) { + const auto& client = i.next(); client->forceClose(); } @@ -269,10 +266,9 @@ void FlatBufferServer::loadLutFile() file.seek(index); - if (_lutBuffer == NULL) - _lutBuffer = (unsigned char*)malloc(length + 4); + _lut.resize(length + 4); - if (file.read((char*)_lutBuffer, LUT_FILE_SIZE) != LUT_FILE_SIZE) + if (file.read((char*)_lut.data(), LUT_FILE_SIZE) != LUT_FILE_SIZE) { Error(_log, "Error reading LUT file %s", QSTRING_CSTR(fileName3d)); } @@ -297,16 +293,19 @@ void FlatBufferServer::loadLutFile() } } -void FlatBufferServer::importFromProtoHandler(int priority, int duration, const Image& image) +void FlatBufferServer::handlerImportFromProto(int priority, int duration, const Image& image, QString clientDescription) { - FrameDecoder::applyLUT((uint8_t*)image.rawMem(), image.width(), image.height(), _lutBuffer, _hdrToneMappingMode); + if (getHdrToneMappingEnabled()) + FrameDecoder::applyLUT((uint8_t*)image.rawMem(), image.width(), image.height(), _lut.data(), getHdrToneMappingEnabled()); - emit GlobalSignals::getInstance()->setGlobalImage(priority, image, duration); + emit GlobalSignals::getInstance()->SignalSetGlobalImage(priority, image, duration, hyperhdr::Components::COMP_PROTOSERVER, clientDescription); } -void FlatBufferServer::setUserLut(QString filename) +void FlatBufferServer::handlerImageReceived(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin, QString clientDescription) { - _userLutFile = filename.replace("~", "").replace("/", "").replace("..", ""); - - Info(_log, "Setting user LUT filename to: '%s'", QSTRING_CSTR(_userLutFile)); + if (getHdrToneMappingEnabled()) + FrameDecoder::applyLUT((uint8_t*)image.rawMem(), image.width(), image.height(), _lut.data(), getHdrToneMappingEnabled()); + + emit GlobalSignals::getInstance()->SignalSetGlobalImage(priority, image, timeout_ms, origin, clientDescription); } + diff --git a/sources/grabber/AVF/AVFGrabber.mm b/sources/grabber/AVF/AVFGrabber.mm index 6c4d06fe7..ab233f6e8 100644 --- a/sources/grabber/AVF/AVFGrabber.mm +++ b/sources/grabber/AVF/AVFGrabber.mm @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -40,13 +40,13 @@ #include #include -#include +#include #include #include #include -#include +#include #include // Apple frameworks @@ -118,7 +118,7 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB if (!getPermission()) { Warning(_log, "HyperHDR has NOT been granted the camera's permission. Will check it later again."); - QTimer::singleShot(3000, this, SLOT(getPermission())); + QTimer::singleShot(3000, this, &AVFGrabber::getPermission); } _isAVF = true; @@ -153,7 +153,7 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB if (_isAVF) { if (!_permission) - QTimer::singleShot(5000, this, SLOT(getPermission())); + QTimer::singleShot(5000, this, &AVFGrabber::getPermission); else { Info(_log, "Got the video permission. Now trying to start HyperHDR's video grabber."); @@ -188,10 +188,10 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB void AVFGrabber::setHdrToneMappingEnabled(int mode) { - if (_hdrToneMappingEnabled != mode || _lutBuffer == NULL) + if (_hdrToneMappingEnabled != mode || _lut.data() == nullptr) { _hdrToneMappingEnabled = mode; - if (_lutBuffer != NULL || !mode) + if (_lut.data() != nullptr || !mode) Debug(_log, "setHdrToneMappingMode to: %s", (mode == 0) ? "Disabled" : ((mode == 1) ? "Fullscreen" : "Border mode")); else Warning(_log, "setHdrToneMappingMode to: enable, but the LUT file is currently unloaded"); @@ -206,6 +206,7 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB loadLutFile(PixelFormat::RGB24); _AVFWorkerManager.Start(); } + emit SignalSetNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, (mode != 0)); } else Debug(_log, "setHdrToneMappingMode nothing changed: %s", (mode == 0) ? "Disabled" : ((mode == 1) ? "Fullscreen" : "Border mode")); @@ -770,10 +771,10 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB { int total = (frameStat.badFrame + frameStat.goodFrame); int av = (frameStat.goodFrame > 0) ? frameStat.averageFrame / frameStat.goodFrame : 0; - + QString access = (frameStat.directAccess) ? " (direct)" : ""; if (diff >= 59000 && diff <= 65000) - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::VIDEO_GRABBER), frameStat.token, this->_actualDeviceName, total / qMax(diff / 1000.0, 1.0), av, frameStat.goodFrame, frameStat.badFrame)); + emit GlobalSignals::getInstance()->SignalPerformanceNewReport( + PerformanceReport(hyperhdr::PerformanceReportType::VIDEO_GRABBER, frameStat.token, this->_actualDeviceName + access, total / qMax(diff / 1000.0, 1.0), av, frameStat.goodFrame, frameStat.badFrame)); resetCounter(now); @@ -791,8 +792,8 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB for (unsigned int i = 0; i < _AVFWorkerManager.workersCount && _AVFWorkerManager.workers != nullptr; i++) { AVFWorker* _workerThread = _AVFWorkerManager.workers[i]; - connect(_workerThread, SIGNAL(newFrameError(unsigned int, QString, quint64)), this, SLOT(newWorkerFrameError(unsigned int, QString, quint64))); - connect(_workerThread, SIGNAL(newFrame(unsigned int, Image, quint64, qint64)), this, SLOT(newWorkerFrame(unsigned int, Image, quint64, qint64))); + connect(_workerThread, &AVFWorker::SignalNewFrameError, this, &AVFGrabber::newWorkerFrameErrorHandler); + connect(_workerThread, &AVFWorker::SignalNewFrame, this, &AVFGrabber::newWorkerFrameHandler); } } @@ -810,13 +811,14 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB loadLutFile(); } + bool directAccess = !(_signalAutoDetectionEnabled || _signalDetectionEnabled || isCalibrating() || (_benchmarkStatus >= 0)); _workerThread->setup( i, _actualVideoFormat, (uint8_t*)frameImageBuffer, size, _actualWidth, _actualHeight, _lineLength, _cropLeft, _cropTop, _cropBottom, _cropRight, processFrameIndex, InternalClock::nowPrecise(), _hdrToneMappingEnabled, - (_lutBufferInit) ? _lutBuffer : NULL, _qframe); + (_lutBufferInit) ? _lut.data() : nullptr, _qframe, directAccess, _deviceName); if (_AVFWorkerManager.workersCount > 1) _AVFWorkerManager.workers[i]->start(); @@ -834,7 +836,7 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB return frameSend; } -void AVFGrabber::newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) +void AVFGrabber::newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) { frameStat.badFrame++; @@ -850,23 +852,9 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB } -void AVFGrabber::newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) +void AVFGrabber::newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) { - frameStat.goodFrame++; - frameStat.averageFrame += InternalClock::nowPrecise() - _frameBegin; - - if (_signalAutoDetectionEnabled || isCalibrating()) - { - if (checkSignalDetectionAutomatic(image)) - emit newFrame(image); - } - else if (_signalDetectionEnabled) - { - if (checkSignalDetectionManual(image)) - emit newFrame(image); - } - else - emit newFrame(image); + handleNewFrame(workerIndex, image, sourceCount, _frameBegin); // get next frame if (workerIndex > _AVFWorkerManager.workersCount) diff --git a/sources/grabber/AVF/AVFWorker.cpp b/sources/grabber/AVF/AVFWorker.cpp index bad1c6570..85c6ddb48 100644 --- a/sources/grabber/AVF/AVFWorker.cpp +++ b/sources/grabber/AVF/AVFWorker.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -43,13 +43,12 @@ #include #include -#include #include #include -#include "grabber/AVFWorker.h" - +#include +#include std::atomic AVFWorker::_isActive(false); @@ -129,8 +128,6 @@ AVFWorker::AVFWorker() : _semaphore(1), _workerIndex(0), _pixelFormat(PixelFormat::NO_CHANGE), - _localData(nullptr), - _localDataSize(0), _size(0), _width(0), _height(0), @@ -150,20 +147,14 @@ AVFWorker::AVFWorker() : } AVFWorker::~AVFWorker() -{ - if (_localData != NULL) - { - free(_localData); - _localData = NULL; - _localDataSize = 0; - } +{ } void AVFWorker::setup(unsigned int __workerIndex, PixelFormat __pixelFormat, uint8_t* __sharedData, int __size, int __width, int __height, int __lineLength, uint __cropLeft, uint __cropTop, uint __cropBottom, uint __cropRight, quint64 __currentFrame, qint64 __frameBegin, - int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe) + int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe, bool __directAccess, QString __deviceName) { _workerIndex = __workerIndex; _lineLength = __lineLength; @@ -180,21 +171,12 @@ void AVFWorker::setup(unsigned int __workerIndex, PixelFormat __pixelFormat, _hdrToneMappingEnabled = __hdrToneMappingEnabled; _lutBuffer = __lutBuffer; _qframe = __qframe; + _directAccess = __directAccess; + _deviceName = __deviceName; - if (__size > _localDataSize) - { - if (_localData != NULL) - { - free(_localData); - _localData = NULL; - _localDataSize = 0; - } - _localData = (uint8_t*)malloc((size_t)__size + 1); - _localDataSize = __size; - } + _localBuffer.resize((size_t)__size + 1); - if (_localData != NULL) - memcpy(_localData, __sharedData, __size); + memcpy(_localBuffer.data(), __sharedData, __size); } @@ -211,9 +193,16 @@ void AVFWorker::runMe() { Image image(_width >> 1, _height >> 1); FrameDecoder::processQImage( - _localData, _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); + _localBuffer.data(), _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } else @@ -224,9 +213,16 @@ void AVFWorker::runMe() FrameDecoder::processImage( _cropLeft, _cropRight, _cropTop, _cropBottom, - _localData, _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); + _localBuffer.data(), _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } } } diff --git a/sources/grabber/AVF/AVFWrapper.cpp b/sources/grabber/AVF/AVFWrapper.cpp index 27f567a6f..2e41b53eb 100644 --- a/sources/grabber/AVF/AVFWrapper.cpp +++ b/sources/grabber/AVF/AVFWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,16 +26,18 @@ */ #include -#include +#include +#include AVFWrapper::AVFWrapper(const QString& device, const QString& configurationPath) - : GrabberWrapper("macOS AVF:" + device.left(14), &_grabber) - , _grabber(device, configurationPath) + : GrabberWrapper("macOS AVF:" + device.left(14)) { - qRegisterMetaType>("Image"); - connect(&_grabber, &Grabber::newFrame, this, &GrabberWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &Grabber::readError, this, &GrabberWrapper::readError, Qt::DirectConnection); + _grabber = std::unique_ptr(new AVFGrabber(device, configurationPath)); + connect(_grabber.get(), &Grabber::SignalBenchmarkUpdate, this, &GrabberWrapper::SignalBenchmarkUpdate); + connect(_grabber.get(), &Grabber::SignalCapturingException, this, &GrabberWrapper::capturingExceptionHandler); + connect(_grabber.get(), &Grabber::SignalSetNewComponentStateToAllInstances, this, &GrabberWrapper::SignalSetNewComponentStateToAllInstances); + connect(_grabber.get(), &Grabber::SignalSaveCalibration, this, &GrabberWrapper::SignalSaveCalibration); } diff --git a/sources/grabber/AVF/CMakeLists.txt b/sources/grabber/AVF/CMakeLists.txt index e3935d116..dd1ad1b00 100644 --- a/sources/grabber/AVF/CMakeLists.txt +++ b/sources/grabber/AVF/CMakeLists.txt @@ -1,8 +1,8 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/AVF) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/AVF) -FILE ( GLOB AVF_SOURCES "${CURRENT_HEADER_DIR}/AVF*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" "${CURRENT_SOURCE_DIR}/*.mm" ) +FILE ( GLOB AVF_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" "${CURRENT_SOURCE_DIR}/*.mm" ) add_library(AVF-grabber ${AVF_SOURCES} ) @@ -10,3 +10,7 @@ target_link_libraries(AVF-grabber hyperhdr-base ${QT_LIBRARIES} ) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(AVF-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/CMakeLists.txt b/sources/grabber/CMakeLists.txt index 6f6cc1999..6f726c56e 100644 --- a/sources/grabber/CMakeLists.txt +++ b/sources/grabber/CMakeLists.txt @@ -29,15 +29,3 @@ endif (ENABLE_V4L2) if (ENABLE_MF) add_subdirectory(MF) endif (ENABLE_MF) - -if (ENABLE_SOUNDCAPLINUX) - add_subdirectory(SoundCapLinux) -endif (ENABLE_SOUNDCAPLINUX) - -if (ENABLE_SOUNDCAPWINDOWS) - add_subdirectory(SoundCapWindows) -endif (ENABLE_SOUNDCAPWINDOWS) - -if (ENABLE_SOUNDCAPMACOS) - add_subdirectory(SoundCapMacOS) -endif (ENABLE_SOUNDCAPMACOS) diff --git a/sources/grabber/DX/CMakeLists.txt b/sources/grabber/DX/CMakeLists.txt index 0ba25a2d1..c62dbd9e3 100644 --- a/sources/grabber/DX/CMakeLists.txt +++ b/sources/grabber/DX/CMakeLists.txt @@ -1,8 +1,8 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/DX) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/DX) -FILE ( GLOB DX_SOURCES "${CURRENT_HEADER_DIR}/Dx*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) +FILE ( GLOB DX_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) add_library(DX-grabber ${DX_SOURCES} ) @@ -10,3 +10,7 @@ target_link_libraries(DX-grabber hyperhdr-base ${QT_LIBRARIES} ) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(DX-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/DX/DxGrabber.cpp b/sources/grabber/DX/DxGrabber.cpp index 5af6d043c..c808b9110 100644 --- a/sources/grabber/DX/DxGrabber.cpp +++ b/sources/grabber/DX/DxGrabber.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -24,6 +24,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#define NOMINMAX #include #include @@ -37,17 +38,18 @@ #include #include #include +#include #include -#include #include #include #include -#include #include - +#include +#include +#include #pragma comment (lib, "ole32.lib") #pragma comment(lib, "dxgi.lib") @@ -55,6 +57,7 @@ #define CHECK(hr) SUCCEEDED(hr) #define CLEAR(x) memset(&(x), 0, sizeof(x)) +#define MAX_WARNINGS 6 template void SafeRelease(T** ppT) { @@ -66,21 +69,34 @@ template void SafeRelease(T** ppT) } DxGrabber::DxGrabber(const QString& device, const QString& configurationPath) - : Grabber("DX11_SYSTEM:" + device.left(14)) + : Grabber(configurationPath, "DX11_SYSTEM:" + device.left(14)) , _configurationPath(configurationPath) - , _semaphore(1) - , _warningCounter(5) + , _timer(new QTimer(this)) + , _retryTimer(new QTimer(this)) + , _warningCounter(MAX_WARNINGS) + , _actualDivide(-1) + , _wideGamut(false) + , _dxRestartNow(false) - , _d3dCache(false) - , _alternative(false) , _d3dDevice(nullptr) , _d3dContext(nullptr) - , _sourceTexture(nullptr) + , _d3dBuffer(nullptr) + , _d3dSampler(nullptr) + , _d3dVertexLayout(nullptr) + , _d3dVertexShader(nullptr) + , _d3dPixelShader(nullptr) + , _d3dSourceTexture(nullptr) + , _d3dConvertTexture(nullptr) + , _d3dConvertTextureView(nullptr) + , _d3dRenderTargetView(nullptr) , _d3dDuplicate(nullptr) { - _timer.setTimerType(Qt::PreciseTimer); - connect(&_timer, &QTimer::timeout, this, &DxGrabber::grabFrame); + _timer->setTimerType(Qt::PreciseTimer); + connect(_timer, &QTimer::timeout, this, &DxGrabber::grabFrame); + + _retryTimer->setSingleShot(true); + connect(_retryTimer, &QTimer::timeout, this, &DxGrabber::restart); // Refresh devices getDevices(); @@ -102,10 +118,10 @@ void DxGrabber::loadLutFile(PixelFormat color) void DxGrabber::setHdrToneMappingEnabled(int mode) { - if (_hdrToneMappingEnabled != mode || _lutBuffer == NULL) + if (_hdrToneMappingEnabled != mode || _lut.data() == nullptr) { _hdrToneMappingEnabled = mode; - if (_lutBuffer != NULL || !mode) + if (_lut.data() != nullptr || !mode) Debug(_log, "setHdrToneMappingMode to: %s", (mode == 0) ? "Disabled" : ((mode == 1) ? "Fullscreen" : "Border mode")); else Warning(_log, "setHdrToneMappingMode to: enable, but the LUT file is currently unloaded"); @@ -120,8 +136,6 @@ void DxGrabber::setHdrToneMappingEnabled(int mode) DxGrabber::~DxGrabber() { uninit(); - - // destroy core elements from contructor } void DxGrabber::uninit() @@ -129,31 +143,43 @@ void DxGrabber::uninit() // stop if the grabber was not stopped if (_initialized) { + disconnect(this, &Grabber::SignalNewCapturedFrame, this, &DxGrabber::cacheHandler); + _cacheImage = Image(); + stop(); Debug(_log, "Uninit grabber: %s", QSTRING_CSTR(_deviceName)); } - _d3dCache = false; - SafeRelease(&_sourceTexture); + SafeRelease(&_d3dRenderTargetView); + SafeRelease(&_d3dSourceTexture); + SafeRelease(&_d3dConvertTextureView); + SafeRelease(&_d3dConvertTexture); + SafeRelease(&_d3dVertexShader); + SafeRelease(&_d3dVertexLayout); + SafeRelease(&_d3dPixelShader); + SafeRelease(&_d3dSampler); + SafeRelease(&_d3dBuffer); SafeRelease(&_d3dDuplicate); SafeRelease(&_d3dContext); SafeRelease(&_d3dDevice); - _warningCounter = 5; + _warningCounter = MAX_WARNINGS; + _actualDivide = -1; _initialized = false; if (_dxRestartNow) { _dxRestartNow = false; - QTimer::singleShot(1000, this, &DxGrabber::restart); + _retryTimer->setInterval(1000); + _retryTimer->start(); } } -void DxGrabber::newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) +void DxGrabber::newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) { }; -void DxGrabber::newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) +void DxGrabber::newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) { }; @@ -208,8 +234,11 @@ bool DxGrabber::init() Info(_log, "Starting DX grabber. Selected: '%s' max width: %d (%d) @ %d fps", QSTRING_CSTR(foundDevice), _width, _height, _fps); Info(_log, "*************************************************************************************************"); - if (init_device(foundDevice)) + if (initDirectX(foundDevice)) + { + connect(this, &Grabber::SignalNewCapturedFrame, this, &DxGrabber::cacheHandler, Qt::UniqueConnection); _initialized = true; + } } return _initialized; @@ -263,8 +292,8 @@ bool DxGrabber::start() { if (init()) { - _timer.setInterval(1000 / _fps); - _timer.start(); + _timer->setInterval(1000 / _fps); + _timer->start(); Info(_log, "Started"); return true; } @@ -281,20 +310,19 @@ void DxGrabber::stop() { if (_initialized) { - _semaphore.acquire(); - _timer.stop(); - _semaphore.release(); + _timer->stop(); Info(_log, "Stopped"); } } -bool DxGrabber::init_device(QString selectedDeviceName) +bool DxGrabber::initDirectX(QString selectedDeviceName) { bool result = false, exitNow = false; - IDXGIFactory1* pFactory = NULL; + IDXGIFactory2* pFactory = NULL; - if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory), (void**)&pFactory))) + if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory2), (void**)&pFactory))) { + Error(_log, "Could not create IDXGIFactory2"); return result; } @@ -316,12 +344,11 @@ bool DxGrabber::init_device(QString selectedDeviceName) if (exitNow) { - IDXGIOutput1* pOutput1 = nullptr; + IDXGIOutput6* pOutput6 = nullptr; - if (CHECK(pOutput->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast(&pOutput1)))) + if (CHECK(pOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(&pOutput6)))) { HRESULT findDriver = E_FAIL; - DXGI_OUTDUPL_DESC duplicateDesc; D3D_FEATURE_LEVEL featureLevel; std::vector driverTypes{ D3D_DRIVER_TYPE_HARDWARE, @@ -329,8 +356,7 @@ bool DxGrabber::init_device(QString selectedDeviceName) D3D_DRIVER_TYPE_REFERENCE, D3D_DRIVER_TYPE_UNKNOWN }; - - CLEAR(duplicateDesc); + CLEAR(featureLevel); for (auto& driverType : driverTypes) @@ -338,6 +364,7 @@ bool DxGrabber::init_device(QString selectedDeviceName) findDriver = D3D11CreateDevice(pAdapter, driverType, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &_d3dDevice, &featureLevel, &_d3dContext); + if (SUCCEEDED(findDriver)) { switch (driverType) @@ -347,20 +374,91 @@ bool DxGrabber::init_device(QString selectedDeviceName) case D3D_DRIVER_TYPE_REFERENCE: Info(_log, "Selected D3D_DRIVER_TYPE_REFERENCE"); break; case D3D_DRIVER_TYPE_UNKNOWN: Info(_log, "Selected D3D_DRIVER_TYPE_UNKNOWN"); break; } + break; } } - + if (CHECK(findDriver) && _d3dDevice != nullptr) { - HRESULT status = pOutput1->DuplicateOutput(_d3dDevice, &_d3dDuplicate); + HRESULT status = E_FAIL; + DXGI_OUTPUT_DESC1 descGamut; + + pOutput6->GetDesc1(&descGamut); + + _wideGamut = descGamut.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + Info(_log, "Gamut: %s, min nits: %0.2f, max nits: %0.2f, max frame nits: %0.2f, white point: [%0.2f, %0.2f]", + (_wideGamut) ? "HDR" : "SDR", descGamut.MinLuminance, descGamut.MaxLuminance, descGamut.MaxFullFrameLuminance, + descGamut.WhitePoint[0], descGamut.WhitePoint[1]); + + if (_wideGamut && _targetMonitorNits == 0) + { + Warning(_log, "Target SDR brightness is set to %i nits. Disabling wide gamut.", _targetMonitorNits); + _wideGamut = false; + } + + + if (_hardware && _wideGamut) + { + Info(_log, "Using wide gamut for HDR. Target SDR brightness: %i nits", _targetMonitorNits); + + std::vector wideFormat({ DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R10G10B10A2_UNORM }); + status = pOutput6->DuplicateOutput1(_d3dDevice, 0, static_cast(wideFormat.size()), wideFormat.data(), &_d3dDuplicate); + + if (!CHECK(status)) + { + Warning(_log, "No support for DXGI_FORMAT_R16G16B16A16_FLOAT/DXGI_FORMAT_R10G10B10A2_UNORM. Fallback to BGRA"); + _wideGamut = false; + } + } + + if (!CHECK(status)) + { + Info(_log, "Using BGRA format"); + + DXGI_FORMAT rgbFormat = DXGI_FORMAT_B8G8R8A8_UNORM; + status = pOutput6->DuplicateOutput1(_d3dDevice, 0, 1, &rgbFormat, &_d3dDuplicate); + } + if (CHECK(status)) { - _d3dDuplicate->GetDesc(&duplicateDesc); + CLEAR(_surfaceProperties); + _d3dDuplicate->GetDesc(&_surfaceProperties); + + Info(_log, "Surface format: %i", _surfaceProperties.ModeDesc.Format); + + int targetSizeX = _surfaceProperties.ModeDesc.Width, targetSizeY = _surfaceProperties.ModeDesc.Height; + + if (_hardware) + { + _actualWidth = targetSizeX; + _actualHeight = targetSizeY; + + if (!_wideGamut) + { + int maxSize = std::max((_actualWidth - _cropLeft - _cropRight), (_actualHeight - _cropTop - _cropBottom)); + + _actualDivide = 0; + while (maxSize > _width) + { + _actualDivide++; + maxSize >>= 1; + } + + targetSizeX = _surfaceProperties.ModeDesc.Width >> _actualDivide; + targetSizeY = _surfaceProperties.ModeDesc.Height >> _actualDivide; + } + else + { + _actualDivide = -1; + getTargetSystemFrameDimension(targetSizeX, targetSizeY); + } + } - D3D11_TEXTURE2D_DESC sourceTextureDesc; - sourceTextureDesc.Width = duplicateDesc.ModeDesc.Width; - sourceTextureDesc.Height = duplicateDesc.ModeDesc.Height; + D3D11_TEXTURE2D_DESC sourceTextureDesc{}; + CLEAR(sourceTextureDesc); + sourceTextureDesc.Width = targetSizeX; + sourceTextureDesc.Height = targetSizeY; sourceTextureDesc.ArraySize = 1; sourceTextureDesc.MipLevels = 1; sourceTextureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; @@ -370,19 +468,34 @@ bool DxGrabber::init_device(QString selectedDeviceName) sourceTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; sourceTextureDesc.MiscFlags = 0; sourceTextureDesc.BindFlags = 0; - - if (CHECK(_d3dDevice->CreateTexture2D(&sourceTextureDesc, NULL, &_sourceTexture))) + if (CHECK(_d3dDevice->CreateTexture2D(&sourceTextureDesc, NULL, &_d3dSourceTexture))) { _actualVideoFormat = PixelFormat::XRGB; - _actualWidth = duplicateDesc.ModeDesc.Width; - _actualHeight = duplicateDesc.ModeDesc.Height; + _actualWidth = sourceTextureDesc.Width; + _actualHeight = sourceTextureDesc.Height; loadLutFile(PixelFormat::RGB24); _frameByteSize = _actualWidth * _actualHeight * 4; _lineLength = _actualWidth * 4; - Info(_log, "Device initialized"); - result = true; + if (_hardware) + { + result = initShaders(); + if (result) + { + Info(_log, "The DX11 device has been initialized. Hardware acceleration is enabled"); + } + else + { + Error(_log, "CreateShaders failed"); + uninit(); + } + } + else + { + result = true; + Info(_log, "The DX11 device has been initialized. Hardware acceleration is disabled"); + } } else { @@ -397,7 +510,8 @@ bool DxGrabber::init_device(QString selectedDeviceName) if (status == E_ACCESSDENIED) { Error(_log, "The device is temporarily unavailable. Will try in 5 seconds again."); - QTimer::singleShot(5000, this, &DxGrabber::restart); + _retryTimer->setInterval(5000); + _retryTimer->start(); } else Error(_log, "Could not create a mirror for d3d device (busy resources?). Code: %i", status); @@ -405,7 +519,8 @@ bool DxGrabber::init_device(QString selectedDeviceName) } else Error(_log, "Could not found any d3d device"); - SafeRelease(&pOutput1); + + SafeRelease(&pOutput6); } else Error(_log, "Cast to IDXGIOutput1 failed"); @@ -420,109 +535,289 @@ bool DxGrabber::init_device(QString selectedDeviceName) return result; } -void DxGrabber::grabFrame() +bool DxGrabber::initShaders() { - DXGI_OUTDUPL_FRAME_INFO infoFrame; - DXGI_MAPPED_RECT mappedRect; - IDXGIResource* resourceDesktop = nullptr; + HRESULT status; + + std::unique_ptr descConvert; + + if (_actualDivide < 0) + descConvert = std::make_unique( + DXGI_FORMAT_B8G8R8A8_UNORM, + _actualWidth, _actualHeight, + 1, + 1, + D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, + D3D11_USAGE_DEFAULT, + 0, + 1); + else + descConvert = std::make_unique( + DXGI_FORMAT_B8G8R8A8_UNORM, + _surfaceProperties.ModeDesc.Width, _surfaceProperties.ModeDesc.Height, + 1, + _actualDivide + 1, + D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, + D3D11_USAGE_DEFAULT, + 0, + 1, + 0, + D3D11_RESOURCE_MISC_GENERATE_MIPS); + + status = _d3dDevice->CreateTexture2D(descConvert.get(), nullptr, &_d3dConvertTexture); + if (CHECK(status)) + { + if (_actualDivide < 0) + { + CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc( + D3D11_RTV_DIMENSION_TEXTURE2D, + DXGI_FORMAT_B8G8R8A8_UNORM); - CLEAR(mappedRect); + status = _d3dDevice->CreateRenderTargetView(_d3dConvertTexture, &rtvDesc, &_d3dRenderTargetView); + if (CHECK(status)) + { + _d3dContext->OMSetRenderTargets(1, &_d3dRenderTargetView, NULL); + } + else + Error(_log, "CreateRenderTargetView failed. Reason: %x", status); + } + else + { + D3D11_SHADER_RESOURCE_VIEW_DESC shaderDesc = {}; + + CLEAR(shaderDesc); + shaderDesc.Format = descConvert->Format; + shaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + shaderDesc.Texture2D.MipLevels = descConvert->MipLevels;; + + status = _d3dDevice->CreateShaderResourceView(_d3dConvertTexture, &shaderDesc, &_d3dConvertTextureView); + return CHECK(status); + } + } + else + Error(_log, "CreateConvertTexture2D failed. Reason: %x", status); - if (_semaphore.tryAcquire()) + if (CHECK(status)) { - auto status = _d3dDuplicate->AcquireNextFrame(0, &infoFrame, &resourceDesktop); + status = _d3dDevice->CreateVertexShader(g_VertexShaderHyperHDR, sizeof(g_VertexShaderHyperHDR), nullptr, &_d3dVertexShader); + if (CHECK(status)) + { + _d3dContext->VSSetShader(_d3dVertexShader, NULL, 0); + _d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + } + else + Error(_log, "Could not create vertex shaders. Reason: %x", status); + } + if (CHECK(status)) + { + status = _d3dDevice->CreatePixelShader(g_PixelShaderHyperHDR, sizeof(g_PixelShaderHyperHDR), nullptr, &_d3dPixelShader); if (CHECK(status)) { - status = _d3dDuplicate->MapDesktopSurface(&mappedRect); - if (CHECK(status)) { - processSystemFrameBGRA((uint8_t*)mappedRect.pBits); - _d3dDuplicate->UnMapDesktopSurface(); - } - else if (status == DXGI_ERROR_UNSUPPORTED) - { - D3D11_MAPPED_SUBRESOURCE internalMap; - ID3D11Texture2D* texDesktop = nullptr; + _d3dContext->PSSetShader(_d3dPixelShader, NULL, 0); + } + else + Error(_log, "Could not create pixel shaders. Reason: %x", status); + } - CLEAR(internalMap); + if (CHECK(status)) + { + D3D11_INPUT_ELEMENT_DESC layout[] = { {"SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, }; + UINT numElements = ARRAYSIZE(layout); - status = resourceDesktop->QueryInterface(__uuidof(ID3D11Texture2D), (void**)&texDesktop); - if (CHECK(status) && texDesktop != nullptr) - { - _d3dContext->CopyResource(_sourceTexture, texDesktop); + status = _d3dDevice->CreateInputLayout(layout, numElements, g_VertexShaderHyperHDR, sizeof(g_VertexShaderHyperHDR), &_d3dVertexLayout); + if (CHECK(status)) + _d3dContext->IASetInputLayout(_d3dVertexLayout); + else + Error(_log, "Could not create vertex layout. Reason: %x", status); + } + if (CHECK(status)) + { + D3D11_SAMPLER_DESC sDesc{}; + CLEAR(sDesc); + + sDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + sDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + sDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + sDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sDesc.MinLOD = 0; + sDesc.MaxLOD = D3D11_FLOAT32_MAX; + status = _d3dDevice->CreateSamplerState(&sDesc, &_d3dSampler); + if (CHECK(status)) + _d3dContext->PSSetSamplers(0, 1, &_d3dSampler); + else + Error(_log, "Could not create the sampler. Reason: %x", status); + } - if (CHECK(_d3dContext->Map(_sourceTexture, 0, D3D11_MAP_READ, 0, &internalMap))) - { - uint8_t* data = (unsigned char*)internalMap.pData; + if (CHECK(status)) + { + DirectX::XMFLOAT4 params = { static_cast(_targetMonitorNits), 18.8515625f - 18.6875f * _targetMonitorNits, 0.0, 0.0 }; - _d3dCache = true; - processSystemFrameBGRA((uint8_t*)internalMap.pData, (int)internalMap.RowPitch); - _d3dContext->Unmap(_sourceTexture, 0); - } + D3D11_BUFFER_DESC cbDesc; + CLEAR(cbDesc); + cbDesc.ByteWidth = sizeof(params); + cbDesc.Usage = D3D11_USAGE_DYNAMIC; + cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - SafeRelease(&texDesktop); - } - else if (_warningCounter > 0) - { - Error(_log, "ResourceDesktop->QueryInterface failed. Reason: %i", status); - _warningCounter--; - } - } - else if (_warningCounter > 0) - { - Error(_log, "MapDesktopSurface failed. Reason: %i", status); - _warningCounter--; - } - } - else if (status == DXGI_ERROR_WAIT_TIMEOUT && _d3dCache) + D3D11_SUBRESOURCE_DATA initData; + CLEAR(initData); + initData.pSysMem = ¶ms; + + status = _d3dDevice->CreateBuffer(&cbDesc, &initData, &_d3dBuffer); + + if (CHECK(status)) { - D3D11_MAPPED_SUBRESOURCE internalMap; + _d3dContext->VSSetConstantBuffers(0, 1, &_d3dBuffer); + } + else + Error(_log, "Could not create constant buffer. Reason: %x", status); + } - CLEAR(internalMap); + if (CHECK(status)) + { + D3D11_VIEWPORT vp{}; + CLEAR(vp); + + vp.Width = static_cast(descConvert->Width); + vp.Height = static_cast(descConvert->Height); + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + + _d3dContext->RSSetViewports(1, &vp); + + return true; + } + + return false; +} + +HRESULT DxGrabber::deepScaledCopy(ID3D11Texture2D* source) +{ + HRESULT status = S_OK; + + if (_actualDivide >= 0) + { + _d3dContext->CopySubresourceRegion(_d3dConvertTexture, 0, 0, 0, 0, source, 0, NULL); + _d3dContext->GenerateMips(_d3dConvertTextureView); + _d3dContext->CopySubresourceRegion(_d3dSourceTexture, 0, 0, 0, 0, _d3dConvertTexture, _actualDivide, NULL); + } + else + { + D3D11_TEXTURE2D_DESC sourceDesc; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{}; + + CLEAR(srvDesc); + CLEAR(sourceDesc); + + source->GetDesc(&sourceDesc); + + if (sourceDesc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT && sourceDesc.Format != DXGI_FORMAT_R10G10B10A2_UNORM) + return E_INVALIDARG; + + srvDesc.Format = sourceDesc.Format; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = 1; + + ID3D11ShaderResourceView* rv = nullptr; + status = _d3dDevice->CreateShaderResourceView(source, &srvDesc, &rv); + + if (!CHECK(status)) + { if (_warningCounter > 0) { - Debug(_log, "AcquireNextFrame didn't return the frame. Just warning: the screen has not changed?"); - _warningCounter--; + Error(_log, "CreateShaderResourceView failed (%i). Reason: %i", _warningCounter--, status); } + return status; + } + else + _d3dContext->PSSetShaderResources(0, 1, &rv); + + _d3dContext->Draw(4, 0); + _d3dContext->CopyResource(_d3dSourceTexture, _d3dConvertTexture); + SafeRelease(&rv); + } + + return status; +} +void DxGrabber::grabFrame() +{ + DXGI_OUTDUPL_FRAME_INFO infoFrame{}; + IDXGIResource* resourceDesktop = nullptr; - if (_alternative) + auto status = _d3dDuplicate->AcquireNextFrame(0, &infoFrame, &resourceDesktop); + + if (CHECK(status)) + { + D3D11_MAPPED_SUBRESOURCE internalMap{}; + ID3D11Texture2D* texDesktop = nullptr; + + CLEAR(internalMap); + + status = resourceDesktop->QueryInterface(__uuidof(ID3D11Texture2D), (void**)&texDesktop); + + if (CHECK(status) && texDesktop != nullptr) + { + if (_hardware) { - if (_cacheImage.width() > 1) - { - emit newFrame(_cacheImage); - } + status = deepScaledCopy(texDesktop); } - else if (CHECK(_d3dContext->Map(_sourceTexture, 0, D3D11_MAP_READ, 0, &internalMap))) + else { - uint8_t* data = (unsigned char*)internalMap.pData; + _d3dContext->CopyResource(_d3dSourceTexture, texDesktop); + } - processSystemFrameBGRA((uint8_t*)internalMap.pData); - _d3dContext->Unmap(_sourceTexture, 0); + if (CHECK(status) && CHECK(_d3dContext->Map(_d3dSourceTexture, 0, D3D11_MAP_READ, 0, &internalMap))) + { + processSystemFrameBGRA((uint8_t*)internalMap.pData, (int)internalMap.RowPitch, !(_hardware && _wideGamut)); + _d3dContext->Unmap(_d3dSourceTexture, 0); } + + SafeRelease(&texDesktop); } - else if (status == DXGI_ERROR_ACCESS_LOST) + else if (_warningCounter > 0) { - Error(_log, "Lost DirectX capture context. Stopping."); - _dxRestartNow = true; + Error(_log, "ResourceDesktop->QueryInterface failed. Reason: %i", status); + _warningCounter--; } - else if (_warningCounter > 0) + } + else if (status == DXGI_ERROR_WAIT_TIMEOUT) + { + if (_warningCounter > 0) { - Error(_log, "AcquireNextFrame failed. Reason: %i", status); + Debug(_log, "AcquireNextFrame didn't return the frame. Just warning: the screen has not changed?"); _warningCounter--; } - SafeRelease(&resourceDesktop); + if (_cacheImage.width() > 1) + { + emit SignalNewCapturedFrame(_cacheImage); + } + } + else if (status == DXGI_ERROR_ACCESS_LOST) + { + Error(_log, "Lost DirectX capture context. Stopping."); + _dxRestartNow = true; + } + else if (_warningCounter > 0) + { + Error(_log, "AcquireNextFrame failed. Reason: %i", status); + _warningCounter--; + } - _d3dDuplicate->ReleaseFrame(); + SafeRelease(&resourceDesktop); - _semaphore.release(); + _d3dDuplicate->ReleaseFrame(); - if (_dxRestartNow) - { - uninit(); - } + if (_dxRestartNow) + { + uninit(); } } @@ -535,23 +830,7 @@ void DxGrabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned crop _cropBottom = cropBottom; } -void DxGrabber::alternativeCaching(bool alternative) -{ - _alternative = alternative; - - if (_alternative) - { - connect(this, &Grabber::newFrame, this, &DxGrabber::cacheHandler, Qt::UniqueConnection); - } - else - { - disconnect(this, &Grabber::newFrame, this, &DxGrabber::cacheHandler); - _cacheImage = Image(); - } -} - void DxGrabber::cacheHandler(const Image& image) { - if (_alternative) - _cacheImage = image; + _cacheImage = image; } diff --git a/sources/grabber/DX/DxWrapper.cpp b/sources/grabber/DX/DxWrapper.cpp index 841a566df..3dc83c4a0 100644 --- a/sources/grabber/DX/DxWrapper.cpp +++ b/sources/grabber/DX/DxWrapper.cpp @@ -1,5 +1,32 @@ +/* DxWrapper.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + #include -#include +#include DxWrapper::DxWrapper(const QString& device, @@ -8,7 +35,7 @@ DxWrapper::DxWrapper(const QString& device, , _grabber(device, configurationPath) { qRegisterMetaType>("Image"); - connect(&_grabber, &Grabber::newFrame, this, &SystemWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &Grabber::readError, this, &SystemWrapper::readError, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalNewCapturedFrame, this, &SystemWrapper::newCapturedFrameHandler, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalCapturingException, this, &SystemWrapper::capturingExceptionHandler, Qt::DirectConnection); } diff --git a/sources/grabber/MF/CMakeLists.txt b/sources/grabber/MF/CMakeLists.txt index 73daecf35..d95fa327f 100644 --- a/sources/grabber/MF/CMakeLists.txt +++ b/sources/grabber/MF/CMakeLists.txt @@ -1,8 +1,8 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/MF) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/MF) -FILE ( GLOB WMF_SOURCES "${CURRENT_HEADER_DIR}/MF*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) +FILE ( GLOB WMF_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) add_library(MF-grabber ${WMF_SOURCES} ) @@ -13,3 +13,7 @@ target_link_libraries(MF-grabber target_include_directories(MF-grabber PUBLIC ${TURBOJPEG_INCLUDE_DIRS}) target_link_libraries(MF-grabber ${TURBOJPEG_LINK_LIBRARIES}) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(MF-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/MF/MFCallback.cpp b/sources/grabber/MF/MFCallback.cpp index 6fa18891b..122e5017c 100644 --- a/sources/grabber/MF/MFCallback.cpp +++ b/sources/grabber/MF/MFCallback.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,17 +25,20 @@ * SOFTWARE. */ -#pragma once #include #include #include #include #include #include -#include +#pragma push_macro("Info") + #undef Info + #include +#pragma pop_macro("Info") -#include -#include + +#include +#include MFCallback::MFCallback(MFGrabber* grabber) : _referenceCounter(1), _grabber(grabber), _endOfStream(FALSE), _callbackStatus(S_OK) diff --git a/sources/grabber/MF/MFGrabber.cpp b/sources/grabber/MF/MFGrabber.cpp index 63e010c58..2d6276fb5 100644 --- a/sources/grabber/MF/MFGrabber.cpp +++ b/sources/grabber/MF/MFGrabber.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -24,14 +24,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #include #include #include #include #include #include -#include +#include +#pragma push_macro("Info") + #undef Info + #include +#pragma pop_macro("Info") + #include #include @@ -47,17 +51,17 @@ #include #include -#include #include #include #include -#include #include -#include - +#include +#include +#include +#include #pragma comment (lib, "ole32.lib") @@ -73,7 +77,14 @@ // some stuff for HDR tone mapping #define LUT_FILE_SIZE 50331648 -MFGrabber::VideoFormat fmt_array[] = +typedef struct +{ + const GUID format_id; + const QString format_name; + PixelFormat pixel; +} VideoFormat; + +const static VideoFormat fmt_array[] = { { MFVideoFormat_RGB32, "RGB32", PixelFormat::XRGB }, { MFVideoFormat_ARGB32, "ARGB32", PixelFormat::NO_CHANGE }, @@ -167,10 +178,10 @@ void MFGrabber::loadLutFile(PixelFormat color) void MFGrabber::setHdrToneMappingEnabled(int mode) { - if (_hdrToneMappingEnabled != mode || _lutBuffer == NULL) + if (_hdrToneMappingEnabled != mode || _lut.data() == nullptr) { _hdrToneMappingEnabled = mode; - if (_lutBuffer != NULL || !mode) + if (_lut.data() != nullptr || !mode) Debug(_log, "setHdrToneMappingMode to: %s", (mode == 0) ? "Disabled" : ((mode == 1) ? "Fullscreen" : "Border mode")); else Warning(_log, "setHdrToneMappingMode to: enable, but the LUT file is currently unloaded"); @@ -185,6 +196,7 @@ void MFGrabber::setHdrToneMappingEnabled(int mode) loadLutFile(PixelFormat::RGB24); _MFWorkerManager.Start(); } + emit SignalSetNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, (mode != 0)); } else Debug(_log, "setHdrToneMappingMode nothing changed: %s", (mode == 0) ? "Disabled" : ((mode == 1) ? "Fullscreen" : "Border mode")); @@ -954,10 +966,10 @@ bool MFGrabber::process_image(const void* frameImageBuffer, int size) { int total = (frameStat.badFrame + frameStat.goodFrame); int av = (frameStat.goodFrame > 0) ? frameStat.averageFrame / frameStat.goodFrame : 0; - + QString access = (frameStat.directAccess) ? " (direct)" : ""; if (diff >= 59000 && diff <= 65000) - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::VIDEO_GRABBER), frameStat.token, this->_actualDeviceName, total / qMax(diff / 1000.0, 1.0), av, frameStat.goodFrame, frameStat.badFrame)); + emit GlobalSignals::getInstance()->SignalPerformanceNewReport( + PerformanceReport(hyperhdr::PerformanceReportType::VIDEO_GRABBER, frameStat.token, this->_actualDeviceName + access, total / qMax(diff / 1000.0, 1.0), av, frameStat.goodFrame, frameStat.badFrame)); resetCounter(now); @@ -975,8 +987,8 @@ bool MFGrabber::process_image(const void* frameImageBuffer, int size) for (unsigned int i = 0; i < _MFWorkerManager.workersCount && _MFWorkerManager.workers != nullptr; i++) { MFWorker* _workerThread = _MFWorkerManager.workers[i]; - connect(_workerThread, SIGNAL(newFrameError(unsigned int, QString, quint64)), this, SLOT(newWorkerFrameError(unsigned int, QString, quint64))); - connect(_workerThread, SIGNAL(newFrame(unsigned int, Image, quint64, qint64)), this, SLOT(newWorkerFrame(unsigned int, Image, quint64, qint64))); + connect(_workerThread, &MFWorker::SignalNewFrameError, this, &MFGrabber::newWorkerFrameErrorHandler); + connect(_workerThread, &MFWorker::SignalNewFrame, this, &MFGrabber::newWorkerFrameHandler); } } @@ -994,13 +1006,14 @@ bool MFGrabber::process_image(const void* frameImageBuffer, int size) loadLutFile(); } + bool directAccess = !(_signalAutoDetectionEnabled || _signalDetectionEnabled || isCalibrating() || (_benchmarkStatus >= 0)); _workerThread->setup( i, _actualVideoFormat, (uint8_t*)frameImageBuffer, size, _actualWidth, _actualHeight, _lineLength, _cropLeft, _cropTop, _cropBottom, _cropRight, processFrameIndex, InternalClock::nowPrecise(), _hdrToneMappingEnabled, - (_lutBufferInit) ? _lutBuffer : NULL, _qframe); + (_lutBufferInit) ? _lut.data() : nullptr, _qframe, directAccess, _deviceName); if (_MFWorkerManager.workersCount > 1) _MFWorkerManager.workers[i]->start(); @@ -1018,7 +1031,7 @@ bool MFGrabber::process_image(const void* frameImageBuffer, int size) return frameSend; } -void MFGrabber::newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) +void MFGrabber::newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) { frameStat.badFrame++; @@ -1038,24 +1051,9 @@ void MFGrabber::newWorkerFrameError(unsigned int workerIndex, QString error, qui } -void MFGrabber::newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) +void MFGrabber::newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) { - frameStat.goodFrame++; - frameStat.averageFrame += InternalClock::nowPrecise() - _frameBegin; - - if (_signalAutoDetectionEnabled || isCalibrating()) - { - if (checkSignalDetectionAutomatic(image)) - emit newFrame(image); - } - else if (_signalDetectionEnabled) - { - if (checkSignalDetectionManual(image)) - emit newFrame(image); - } - else - emit newFrame(image); - + handleNewFrame(workerIndex, image, sourceCount, _frameBegin); // get next frame if (workerIndex > _MFWorkerManager.workersCount) diff --git a/sources/grabber/MF/MFWorker.cpp b/sources/grabber/MF/MFWorker.cpp index f98dc3bd7..0b36e6f00 100644 --- a/sources/grabber/MF/MFWorker.cpp +++ b/sources/grabber/MF/MFWorker.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -41,15 +41,12 @@ #include #include - -#include -#include - #include #include -#include - +#include +#include +#include std::atomic MFWorker::_isActive(false); @@ -127,11 +124,8 @@ bool MFWorkerManager::isActive() MFWorker::MFWorker() : _decompress(nullptr), _isBusy(false), - _semaphore(1), _workerIndex(0), _pixelFormat(PixelFormat::NO_CHANGE), - _localData(nullptr), - _localDataSize(0), _size(0), _width(0), _height(0), @@ -153,21 +147,14 @@ MFWorker::MFWorker() : MFWorker::~MFWorker() { if (_decompress != nullptr) - tjDestroy(_decompress); - - if (_localData != NULL) - { - free(_localData); - _localData = NULL; - _localDataSize = 0; - } + tjDestroy(_decompress); } void MFWorker::setup(unsigned int __workerIndex, PixelFormat __pixelFormat, uint8_t* __sharedData, int __size, int __width, int __height, int __lineLength, uint __cropLeft, uint __cropTop, uint __cropBottom, uint __cropRight, quint64 __currentFrame, qint64 __frameBegin, - int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe) + int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe, bool __directAccess, QString __deviceName) { _workerIndex = __workerIndex; _lineLength = __lineLength; @@ -184,21 +171,12 @@ void MFWorker::setup(unsigned int __workerIndex, PixelFormat __pixelFormat, _hdrToneMappingEnabled = __hdrToneMappingEnabled; _lutBuffer = __lutBuffer; _qframe = __qframe; + _directAccess = __directAccess; + _deviceName = __deviceName; - if (__size > _localDataSize) - { - if (_localData != NULL) - { - free(_localData); - _localData = NULL; - _localDataSize = 0; - } - _localData = (uint8_t*)malloc((size_t)__size + 1); - _localDataSize = __size; - } - - if (_localData != NULL) - memcpy(_localData, __sharedData, __size); + _localBuffer.resize((size_t)__size + 1); + + memcpy(_localBuffer.data(), __sharedData, __size); } void MFWorker::run() @@ -220,10 +198,16 @@ void MFWorker::runMe() { Image image(_width >> 1, _height >> 1); FrameDecoder::processQImage( - _localData, _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); - - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); - + _localBuffer.data(), _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); + + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } else { @@ -233,9 +217,16 @@ void MFWorker::runMe() FrameDecoder::processImage( _cropLeft, _cropRight, _cropTop, _cropBottom, - _localData, _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); - - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); + _localBuffer.data(), _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); + + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } } } @@ -266,16 +257,16 @@ void MFWorker::process_image_jpg_mt() if (_decompress == nullptr) _decompress = tjInitDecompress(); - if (tjDecompressHeader2(_decompress, const_cast(_localData), _size, &_width, &_height, &_subsamp) != 0 && + if (tjDecompressHeader2(_decompress, _localBuffer.data(), _size, &_width, &_height, &_subsamp) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } if ((_subsamp != TJSAMP_422 && _subsamp != TJSAMP_420) && _hdrToneMappingEnabled > 0) { - emit newFrameError(_workerIndex, QString("%1: %2").arg(UNSUPPORTED_DECODER).arg(_subsamp), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString("%1: %2").arg(UNSUPPORTED_DECODER).arg(_subsamp), _currentFrame); return; } @@ -289,50 +280,48 @@ void MFWorker::process_image_jpg_mt() if (_hdrToneMappingEnabled > 0) { size_t yuvSize = tjBufSizeYUV2(_width, 2, _height, _subsamp); - uint8_t* jpegBuffer = (uint8_t*)malloc(yuvSize); + MemoryBuffer jpgBuffer(yuvSize); - if (tjDecompressToYUV2(_decompress, const_cast(_localData), _size, jpegBuffer, _width, 2, _height, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && + if (tjDecompressToYUV2(_decompress, _localBuffer.data(), _size, jpgBuffer.data(), _width, 2, _height, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - free(jpegBuffer); - - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } FrameDecoder::processImage(_cropLeft, _cropRight, _cropTop, _cropBottom, - jpegBuffer, _width, _height, _width, (_subsamp == TJSAMP_422) ? PixelFormat::MJPEG : PixelFormat::I420, _lutBuffer, image); - - free(jpegBuffer); + jpgBuffer.data(), _width, _height, _width, (_subsamp == TJSAMP_422) ? PixelFormat::MJPEG : PixelFormat::I420, _lutBuffer, image); } else if (image.width() != (uint)_width || image.height() != (uint)_height) { - uint8_t* jpegBuffer = (uint8_t*)malloc(_width * _height * 3); + MemoryBuffer jpgBuffer(_width * _height * 3); - if (tjDecompress2(_decompress, const_cast(_localData), _size, (uint8_t*)jpegBuffer, _width, 0, _height, TJPF_BGR, TJFLAG_BOTTOMUP | TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && + if (tjDecompress2(_decompress, _localBuffer.data(), _size, jpgBuffer.data(), _width, 0, _height, TJPF_BGR, TJFLAG_BOTTOMUP | TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - free(jpegBuffer); - - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } FrameDecoder::processImage(_cropLeft, _cropRight, _cropTop, _cropBottom, - jpegBuffer, _width, _height, _width * 3, PixelFormat::RGB24, nullptr, image); - - free(jpegBuffer); + jpgBuffer.data(), _width, _height, _width * 3, PixelFormat::RGB24, nullptr, image); } else { - if (tjDecompress2(_decompress, const_cast(_localData), _size, image.rawMem(), _width, 0, _height, TJPF_RGB, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && + if (tjDecompress2(_decompress, _localBuffer.data(), _size, image.rawMem(), _width, 0, _height, TJPF_RGB, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } } - - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } diff --git a/sources/grabber/MF/MFWrapper.cpp b/sources/grabber/MF/MFWrapper.cpp index e05a77b62..252507521 100644 --- a/sources/grabber/MF/MFWrapper.cpp +++ b/sources/grabber/MF/MFWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,16 +26,18 @@ */ #include -#include +#include +#include MFWrapper::MFWrapper(const QString& device, const QString& configurationPath) - : GrabberWrapper("Media Foundation:" + device.left(14), &_grabber) - , _grabber(device, configurationPath) + : GrabberWrapper("Media Foundation:" + device.left(14)) { - qRegisterMetaType>("Image"); - connect(&_grabber, &Grabber::newFrame, this, &GrabberWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &Grabber::readError, this, &GrabberWrapper::readError, Qt::DirectConnection); + _grabber = std::unique_ptr(new MFGrabber(device, configurationPath)); + connect(_grabber.get(), &Grabber::SignalBenchmarkUpdate, this, &GrabberWrapper::SignalBenchmarkUpdate); + connect(_grabber.get(), &Grabber::SignalCapturingException, this, &GrabberWrapper::capturingExceptionHandler); + connect(_grabber.get(), &Grabber::SignalSetNewComponentStateToAllInstances, this, &GrabberWrapper::SignalSetNewComponentStateToAllInstances); + connect(_grabber.get(), &Grabber::SignalSaveCalibration, this, &GrabberWrapper::SignalSaveCalibration); } diff --git a/sources/grabber/SoundCapLinux/CMakeLists.txt b/sources/grabber/SoundCapLinux/CMakeLists.txt deleted file mode 100644 index 1cb1c703c..000000000 --- a/sources/grabber/SoundCapLinux/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/SoundCapLinux) - -FILE ( GLOB SOUNDCAPLINUX_SOURCES "${CURRENT_HEADER_DIR}/SoundCapL*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(SoundCapLinux ${SOUNDCAPLINUX_SOURCES} ) - -find_package(ALSA REQUIRED) - -include_directories(${ALSA_INCLUDE_DIRS}) - -target_link_libraries(SoundCapLinux - hyperhdr-base - ${QT_LIBRARIES} - ${ALSA_LIBRARIES} -) diff --git a/sources/grabber/SoundCapLinux/SoundCapLinux.cpp b/sources/grabber/SoundCapLinux/SoundCapLinux.cpp deleted file mode 100644 index 0322cb67d..000000000 --- a/sources/grabber/SoundCapLinux/SoundCapLinux.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* SoundCapLinux.cpp -* -* MIT License -* -* Copyright (c) 2023 awawa-dev -* -* Project homesite: https://github.com/awawa-dev/HyperHDR -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. - -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -int16_t SoundCapLinux::_soundBuffer[(1<= periodSize) { - int total = snd_pcm_readi(shandle, _soundBuffer, periodSize); - if (total == periodSize) { - if (AnaliseSpectrum(_soundBuffer, SOUNDCAPLINUX_BUF_LENP)) - _resultIndex++; - } - } - } - } - } - catch(...) - { - } -} - -void SoundCapLinux::Start() -{ - if (_isActive && !_isRunning) - { - int status; - bool error = false; - unsigned int exactRate = 22050; - - snd_pcm_uframes_t periodSize = (1 << SOUNDCAPLINUX_BUF_LENP) * 2; - snd_pcm_uframes_t bufferSize = periodSize * 2; - snd_pcm_hw_params_t *hw_params; - - QStringList deviceList = _selectedDevice.split('|'); - - if (deviceList.size() == 0) - { - Error(Logger::getInstance("HYPERHDR"), "Invalid device name: %s", QSTRING_CSTR(_selectedDevice)); - } - - QString device = deviceList.at(0).trimmed(); - Info(Logger::getInstance("HYPERHDR"), "Opening device: %s", QSTRING_CSTR(device)); - - - if ((status = snd_pcm_open (&_handle, QSTRING_CSTR(device), SND_PCM_STREAM_CAPTURE, 0)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot open input sound device '%s'. Error: '%s'", QSTRING_CSTR(device), snd_strerror (status)); - return; - } - - if ((status = snd_pcm_hw_params_malloc (&hw_params)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot allocate hardware parameter buffer: '%s'", snd_strerror (status)); - snd_pcm_close(_handle); - return; - } - - try - { - if ((status = snd_pcm_hw_params_any (_handle, hw_params)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params_any: '%s'", snd_strerror (status)); - throw 1; - } - - if ((status = snd_pcm_hw_params_set_access (_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params_set_access: '%s'", snd_strerror (status)); - throw 2; - } - - if ((status = snd_pcm_hw_params_set_format (_handle, hw_params, SND_PCM_FORMAT_S16_LE )) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params_set_format: '%s'", snd_strerror (status)); - throw 3; - } - - if ((status = snd_pcm_hw_params_set_rate_near (_handle, hw_params, &exactRate, 0)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params_set_rate_near: '%s'", snd_strerror (status)); - throw 4; - } - else if (exactRate != 22050) - { - Error(Logger::getInstance("HYPERHDR"), "Cannot set rate to 22050"); - throw 5; - } - - if ((status = snd_pcm_hw_params_set_channels (_handle, hw_params, 1)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params_set_channels: '%s'",snd_strerror (status)); - throw 6; - } - - if( (status = snd_pcm_hw_params_set_period_size_near(_handle, hw_params, &periodSize, 0)) < 0 ) - { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params_set_period_size_near: '%s'", snd_strerror(status) ); - throw 7; - } - else - Info(Logger::getInstance("HYPERHDR"), "Sound period size = %lu", (unsigned long)periodSize); - - if( (status = snd_pcm_hw_params_set_buffer_size_near(_handle, hw_params, &bufferSize)) < 0 ) - { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params_set_buffer_size_near: '%s'", snd_strerror(status) ); - throw 8; - } - else - Info(Logger::getInstance("HYPERHDR"), "Sound buffer size = %lu", (unsigned long)bufferSize); - - - if ((status = snd_pcm_hw_params (_handle, hw_params)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot set snd_pcm_hw_params: '%s'", snd_strerror (status)); - throw 9; - } - - if ((status = snd_pcm_prepare (_handle)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Cannot prepare device for use: '%s'", snd_strerror (status)); - throw 10; - } - - if ((status = snd_async_add_pcm_handler(&_pcmCallback, _handle, RecordCallback, NULL)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Registering record callback error, probably you chose wrong or virtual audio capture device: '%s'", snd_strerror(status)); - throw 11; - } - - if (snd_pcm_state(_handle) == SND_PCM_STATE_PREPARED) { - if ((status = snd_pcm_start(_handle)) < 0) { - Error(Logger::getInstance("HYPERHDR"), "Start failed: '%s'", snd_strerror(status)); - snd_async_del_handler(_pcmCallback); - _pcmCallback = NULL; - throw 12; - } - } - else{ - Error(Logger::getInstance("HYPERHDR"), "Preparing device failed: '%s'", snd_strerror(status)); - snd_async_del_handler(_pcmCallback); - _pcmCallback = NULL; - throw 13; - } - - } - catch(...) - { - error = true; - } - - snd_pcm_hw_params_free (hw_params); - - if (!error) - { - _isRunning = true; - } - else - { - snd_pcm_close(_handle); - _handle = NULL; - } - } -} - -void SoundCapLinux::Stop() -{ - if (!_isRunning) - return; - - Info(Logger::getInstance("HYPERHDR"), "Disconnecting from sound driver: '%s'", QSTRING_CSTR(_selectedDevice)); - - - _isRunning = false; - - if (_pcmCallback != NULL) - { - snd_pcm_drop(_handle); - snd_async_del_handler(_pcmCallback); - _pcmCallback = NULL; - } - - if (_handle != NULL) - { - snd_pcm_close(_handle); - _handle = NULL; - } -} diff --git a/sources/grabber/SoundCapMacOS/CMakeLists.txt b/sources/grabber/SoundCapMacOS/CMakeLists.txt deleted file mode 100644 index 884dfac41..000000000 --- a/sources/grabber/SoundCapMacOS/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/SoundCapMacOS) - -FILE ( GLOB SOUNDCAPMACOS_SOURCES "${CURRENT_HEADER_DIR}/SoundCapMac*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" "${CURRENT_SOURCE_DIR}/*.mm" ) - -add_library(SoundCapMacOS ${SOUNDCAPMACOS_SOURCES} ) - -target_link_libraries(SoundCapMacOS - hyperhdr-base - ${QT_LIBRARIES} -) diff --git a/sources/grabber/SoundCapWindows/CMakeLists.txt b/sources/grabber/SoundCapWindows/CMakeLists.txt deleted file mode 100644 index 59942e0ad..000000000 --- a/sources/grabber/SoundCapWindows/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/SoundCapWindows) - -FILE ( GLOB SOUNDCAPWINDOWS_SOURCES "${CURRENT_HEADER_DIR}/SoundCapW*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(SoundCapWindows ${SOUNDCAPWINDOWS_SOURCES} ) - -target_link_libraries(SoundCapWindows - hyperhdr-base - ${QT_LIBRARIES} -) diff --git a/sources/grabber/SoundCapWindows/SoundCapWindows.cpp b/sources/grabber/SoundCapWindows/SoundCapWindows.cpp deleted file mode 100644 index c1b975d03..000000000 --- a/sources/grabber/SoundCapWindows/SoundCapWindows.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* SoundCapWindows.cpp -* -* MIT License -* -* Copyright (c) 2023 awawa-dev -* -* Project homesite: https://github.com/awawa-dev/HyperHDR -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. - -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. - */ - -#include -#include -#include - -#pragma comment(lib, "winmm.lib") - -WAVEHDR SoundCapWindows::_header; -HWAVEIN SoundCapWindows::_hWaveIn; -int16_t SoundCapWindows::_soundBuffer[(1 << SOUNDCAPWINDOWS_BUF_LENP) + 64]; - -SoundCapWindows::SoundCapWindows(const QJsonDocument& effectConfig, QObject* parent) - : SoundCapture(effectConfig, parent) -{ - ListDevices(); -} - - -void SoundCapWindows::ListDevices() -{ - UINT deviceCount = waveInGetNumDevs(); - - for (UINT i = 0; i < deviceCount; i++) - { - WAVEINCAPSW waveCaps; - memset(&waveCaps, 0, sizeof(waveCaps)); - waveInGetDevCapsW(i, &waveCaps, sizeof(WAVEINCAPSW)); - - if (waveCaps.dwFormats & WAVE_FORMAT_2M16) - _availableDevices.append(QString::fromWCharArray(waveCaps.szPname)); - } -} - -SoundCapWindows::~SoundCapWindows() -{ - Stop(); -} - -void CALLBACK SoundCapWindows::soundInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) -{ - if (AnaliseSpectrum(_soundBuffer, SOUNDCAPWINDOWS_BUF_LENP)) - _resultIndex++; - - if (_isRunning) - waveInAddBuffer(_hWaveIn, &_header, sizeof(WAVEHDR)); -} - -void SoundCapWindows::Start() -{ - if (_isActive && !_isRunning) - { - UINT deviceCount = waveInGetNumDevs(), found = 0; - for (found = 0; found < deviceCount; found++) - { - WAVEINCAPSW waveCaps; - memset(&waveCaps, 0, sizeof(waveCaps)); - waveInGetDevCapsW(found, &waveCaps, sizeof(WAVEINCAPSW)); - - if (QString::fromWCharArray(waveCaps.szPname).compare(_selectedDevice) == 0) - break; - } - - if (found >= deviceCount) - { - Error(Logger::getInstance("HYPERHDR"), "Could not find '%s' device for open", QSTRING_CSTR(_selectedDevice)); - return; - } - - WAVEFORMATEX pFormat; - pFormat.wFormatTag = WAVE_FORMAT_PCM; - pFormat.nChannels = 1; - pFormat.nSamplesPerSec = 22050; - pFormat.wBitsPerSample = 16; - pFormat.nBlockAlign = (pFormat.nChannels * pFormat.wBitsPerSample) / 8; - pFormat.nAvgBytesPerSec = (pFormat.nSamplesPerSec * pFormat.nChannels * pFormat.wBitsPerSample) / 8; - pFormat.cbSize = 0; - - MMRESULT result = waveInOpen(&_hWaveIn, WAVE_MAPPER, &pFormat, (DWORD_PTR) & (soundInProc), 0L, CALLBACK_FUNCTION); - - if (result != MMSYSERR_NOERROR) - { - Error(Logger::getInstance("HYPERHDR"), "Error during opening sound device '%s'. Error code: %i", QSTRING_CSTR(_selectedDevice), result); - return; - } - - _header.lpData = (LPSTR)&_soundBuffer[0]; - _header.dwBufferLength = (1 << SOUNDCAPWINDOWS_BUF_LENP) * 2; - _header.dwFlags = 0L; - _header.dwLoops = 0L; - waveInPrepareHeader(_hWaveIn, &_header, sizeof(WAVEHDR)); - - waveInAddBuffer(_hWaveIn, &_header, sizeof(WAVEHDR)); - - result = waveInStart(_hWaveIn); - if (result != MMSYSERR_NOERROR) - { - Error(Logger::getInstance("HYPERHDR"), "Error during starting sound device '%s'. Error code: %i", QSTRING_CSTR(_selectedDevice), result); - } - else - _isRunning = true; - } -} - -void SoundCapWindows::Stop() -{ - if (_isRunning) - { - _isRunning = false; - waveInClose(_hWaveIn); - } -} diff --git a/sources/grabber/X11/CMakeLists.txt b/sources/grabber/X11/CMakeLists.txt index 610c107c2..1d3f749e7 100644 --- a/sources/grabber/X11/CMakeLists.txt +++ b/sources/grabber/X11/CMakeLists.txt @@ -1,5 +1,5 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/X11) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/X11) FILE ( GLOB SMARTX11_SOURCES "${CURRENT_HEADER_DIR}/smartX11*.h" "${CURRENT_SOURCE_DIR}/smartX11.cpp" ) @@ -18,5 +18,9 @@ target_link_libraries(X11-grabber ${QT_LIBRARIES} ) +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(X11-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() + diff --git a/sources/grabber/X11/X11Grabber.cpp b/sources/grabber/X11/X11Grabber.cpp index c1ced837f..14ffcaf6b 100644 --- a/sources/grabber/X11/X11Grabber.cpp +++ b/sources/grabber/X11/X11Grabber.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,14 +39,13 @@ #include #include -#include #include #include #include -#include -#include +#include +#include #include #include @@ -58,7 +57,7 @@ unsigned char* (*_getFrame)(x11Handle * retVal) = nullptr; void (*_releaseFrame)(x11Handle* retVal) = nullptr; X11Grabber::X11Grabber(const QString& device, const QString& configurationPath) - : Grabber("X11_SYSTEM:" + device.left(14)) + : Grabber(configurationPath, "X11_SYSTEM:" + device.left(14)) , _configurationPath(configurationPath) , _semaphore(1) , _library(nullptr) diff --git a/sources/grabber/X11/X11Wrapper.cpp b/sources/grabber/X11/X11Wrapper.cpp index 9054b1436..4cee6fa88 100644 --- a/sources/grabber/X11/X11Wrapper.cpp +++ b/sources/grabber/X11/X11Wrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,7 +26,7 @@ */ #include -#include +#include X11Wrapper::X11Wrapper(const QString &device, @@ -35,8 +35,8 @@ X11Wrapper::X11Wrapper(const QString &device, , _grabber(device, configurationPath) { qRegisterMetaType>("Image"); - connect(&_grabber, &Grabber::newFrame, this, &SystemWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &Grabber::readError, this, &SystemWrapper::readError, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalNewCapturedFrame, this, &SystemWrapper::newCapturedFrameHandler, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalCapturingException, this, &SystemWrapper::capturingExceptionHandler, Qt::DirectConnection); } bool X11Wrapper::isActivated(bool forced) diff --git a/sources/grabber/X11/smartX11.cpp b/sources/grabber/X11/smartX11.cpp index ded32195d..d41e92f18 100644 --- a/sources/grabber/X11/smartX11.cpp +++ b/sources/grabber/X11/smartX11.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,7 +25,7 @@ * SOFTWARE. */ -#include +#include #include #include #include diff --git a/sources/grabber/framebuffer/CMakeLists.txt b/sources/grabber/framebuffer/CMakeLists.txt index 27ffc3bc7..bc22a9e3d 100644 --- a/sources/grabber/framebuffer/CMakeLists.txt +++ b/sources/grabber/framebuffer/CMakeLists.txt @@ -1,10 +1,14 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/framebuffer) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/framebuffer) # Grabber -FILE ( GLOB FRAMEBUFFER_SOURCES "${CURRENT_HEADER_DIR}/FrameBuf*.h" "${CURRENT_SOURCE_DIR}/FrameBufGrabber.cpp" "${CURRENT_SOURCE_DIR}/FrameBufWrapper.cpp" ) +FILE ( GLOB FRAMEBUFFER_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) add_library(framebuffer-grabber ${FRAMEBUFFER_SOURCES} ) target_link_libraries(framebuffer-grabber hyperhdr-base ${QT_LIBRARIES} ) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(framebuffer-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/framebuffer/FrameBufGrabber.cpp b/sources/grabber/framebuffer/FrameBufGrabber.cpp index 5f4a6fe96..8020b828e 100644 --- a/sources/grabber/framebuffer/FrameBufGrabber.cpp +++ b/sources/grabber/framebuffer/FrameBufGrabber.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -42,18 +42,17 @@ #include #include -#include -#include +#include #include #include #include -#include +#include #include FrameBufGrabber::FrameBufGrabber(const QString& device, const QString& configurationPath) - : Grabber("FRAMEBUFFER_SYSTEM:" + device.left(14)) + : Grabber(configurationPath, "FRAMEBUFFER_SYSTEM:" + device.left(14)) , _configurationPath(configurationPath) , _semaphore(1) , _handle(-1) diff --git a/sources/grabber/framebuffer/FrameBufWrapper.cpp b/sources/grabber/framebuffer/FrameBufWrapper.cpp index 55febec99..322436ecd 100644 --- a/sources/grabber/framebuffer/FrameBufWrapper.cpp +++ b/sources/grabber/framebuffer/FrameBufWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,7 +26,7 @@ */ #include -#include +#include FrameBufWrapper::FrameBufWrapper(const QString &device, @@ -35,8 +35,8 @@ FrameBufWrapper::FrameBufWrapper(const QString &device, , _grabber(device, configurationPath) { qRegisterMetaType>("Image"); - connect(&_grabber, &Grabber::newFrame, this, &SystemWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &Grabber::readError, this, &SystemWrapper::readError, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalNewCapturedFrame, this, &SystemWrapper::newCapturedFrameHandler, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalCapturingException, this, &SystemWrapper::capturingExceptionHandler, Qt::DirectConnection); } QString FrameBufWrapper::getGrabberInfo() diff --git a/sources/grabber/macOS/CMakeLists.txt b/sources/grabber/macOS/CMakeLists.txt index faa3e48a0..b9c56e381 100644 --- a/sources/grabber/macOS/CMakeLists.txt +++ b/sources/grabber/macOS/CMakeLists.txt @@ -1,8 +1,8 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/macOS) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/macOS) -FILE ( GLOB MACOS_SOURCES "${CURRENT_HEADER_DIR}/macOs*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" "${CURRENT_SOURCE_DIR}/*.mm" ) +FILE ( GLOB MACOS_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" "${CURRENT_SOURCE_DIR}/*.mm" ) add_library(MACOS-grabber ${MACOS_SOURCES} ) @@ -10,3 +10,7 @@ target_link_libraries(MACOS-grabber hyperhdr-base ${QT_LIBRARIES} ) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(MACOS-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/macOS/macOsGrabber.mm b/sources/grabber/macOS/macOsGrabber.mm index 05672f796..c55963f4e 100644 --- a/sources/grabber/macOS/macOsGrabber.mm +++ b/sources/grabber/macOS/macOsGrabber.mm @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,13 +39,12 @@ #include #include -#include #include #include #include -#include +#include #include #import @@ -54,7 +53,7 @@ id activity; macOsGrabber::macOsGrabber(const QString& device, const QString& configurationPath) - : Grabber("MACOS_SYSTEM:" + device.left(14)) + : Grabber(configurationPath, "MACOS_SYSTEM:" + device.left(14)) , _configurationPath(configurationPath) , _semaphore(1) , _actualDisplay(0) diff --git a/sources/grabber/macOS/macOsWrapper.cpp b/sources/grabber/macOS/macOsWrapper.cpp index 8e8756811..4fe7d5ab2 100644 --- a/sources/grabber/macOS/macOsWrapper.cpp +++ b/sources/grabber/macOS/macOsWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,7 +26,7 @@ */ #include -#include +#include macOsWrapper::macOsWrapper(const QString &device, @@ -35,7 +35,7 @@ macOsWrapper::macOsWrapper(const QString &device, , _grabber(device, configurationPath) { qRegisterMetaType>("Image"); - connect(&_grabber, &Grabber::newFrame, this, &SystemWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &Grabber::readError, this, &SystemWrapper::readError, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalNewCapturedFrame, this, &SystemWrapper::newCapturedFrameHandler, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalCapturingException, this, &SystemWrapper::capturingExceptionHandler, Qt::DirectConnection); } diff --git a/sources/grabber/pipewire/CMakeLists.txt b/sources/grabber/pipewire/CMakeLists.txt index 0fb1d13a4..bb220aaab 100644 --- a/sources/grabber/pipewire/CMakeLists.txt +++ b/sources/grabber/pipewire/CMakeLists.txt @@ -1,8 +1,8 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/pipewire) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/pipewire) -FILE ( GLOB SMARTPIPEWIRE_SOURCES "${CURRENT_HEADER_DIR}/smartPipewire*.h" "${CURRENT_SOURCE_DIR}/PipewireHandler.h" "${CURRENT_SOURCE_DIR}/smartPipewire.cpp" "${CURRENT_SOURCE_DIR}/PipewireHandler.cpp" ) +FILE ( GLOB SMARTPIPEWIRE_SOURCES "${CURRENT_HEADER_DIR}/smartPipewire.h" "${CURRENT_HEADER_DIR}/PipewireHandler.h" "${CURRENT_SOURCE_DIR}/smartPipewire.cpp" "${CURRENT_SOURCE_DIR}/PipewireHandler.cpp" ) add_library(smartPipewire SHARED ${SMARTPIPEWIRE_SOURCES} ) set_target_properties(smartPipewire PROPERTIES VERSION 1) @@ -12,8 +12,13 @@ target_include_directories(smartPipewire PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${PI target_link_libraries(smartPipewire PUBLIC ${PIPEWIRE_LIBRARIES} Qt${Qt_VERSION}::Core Qt${Qt_VERSION}::DBus ) # Grabber -FILE ( GLOB PIPEWIRE_SOURCES "${CURRENT_HEADER_DIR}/smartPipewire*.h" "${CURRENT_HEADER_DIR}/Pipewire*.h" "${CURRENT_SOURCE_DIR}/PipewireGrabber.cpp" "${CURRENT_SOURCE_DIR}/PipewireWrapper.cpp" ) +FILE ( GLOB PIPEWIRE_SOURCES "${CURRENT_HEADER_DIR}/smartPipewire.h" "${CURRENT_HEADER_DIR}/PipewireGrabber.h" "${CURRENT_HEADER_DIR}/PipewireWrapper.h" + "${CURRENT_SOURCE_DIR}/PipewireGrabber.cpp" "${CURRENT_SOURCE_DIR}/PipewireWrapper.cpp" ) add_library(Pipewire-grabber ${PIPEWIRE_SOURCES} ) target_link_libraries(Pipewire-grabber hyperhdr-base ${QT_LIBRARIES} ) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(Pipewire-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/pipewire/PipewireGrabber.cpp b/sources/grabber/pipewire/PipewireGrabber.cpp index 6edf5729e..c0e34dd9e 100644 --- a/sources/grabber/pipewire/PipewireGrabber.cpp +++ b/sources/grabber/pipewire/PipewireGrabber.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -39,16 +39,16 @@ #include #include -#include -#include +#include #include #include #include -#include -#include +#include +#include #include +#include #include bool (*_hasPipewire)() = nullptr; @@ -60,14 +60,14 @@ void (*_releaseFramePipewire)() = nullptr; const char* (*_getPipewireToken)() = nullptr; PipewireGrabber::PipewireGrabber(const QString& device, const QString& configurationPath) - : Grabber("PIPEWIRE_SYSTEM:" + device.left(14)) + : Grabber(configurationPath, "PIPEWIRE_SYSTEM:" + device.left(14)) , _configurationPath(configurationPath) - , _semaphore(1) , _library(nullptr) , _actualDisplay(0) , _isActive(false) , _storedToken(false) , _versionCheck(false) + , _accessManager(nullptr) { _timer.setTimerType(Qt::PreciseTimer); connect(&_timer, &QTimer::timeout, this, &PipewireGrabber::grabFrame); @@ -161,6 +161,12 @@ bool PipewireGrabber::init() { Debug(_log, "init"); + emit GlobalSignals::getInstance()->SignalGetAccessManager(_accessManager); + if (_accessManager == nullptr) + { + Error(_log, "Access manager is already removed"); + return false; + } if (!_initialized) { @@ -254,14 +260,12 @@ void PipewireGrabber::stop() { if (_initialized) { - _semaphore.acquire(); _timer.stop(); _uninitPipewireDisplay(); _isActive = false; _initialized = false; - _semaphore.release(); Info(_log, "Stopped"); } } @@ -272,9 +276,7 @@ bool PipewireGrabber::init_device(int _display) _storedToken = false; _versionCheck = false; - - AuthManager* instance = AuthManager::getInstance(); - QString token = instance->loadPipewire(); + QString token = (_accessManager != nullptr) ? _accessManager->loadPipewire() : nullptr; if (token.isNull()) token = ""; else @@ -303,79 +305,72 @@ QString PipewireGrabber::maskToken(const QString& token) const void PipewireGrabber::stateChanged(bool state) { - if (!state) + if (!state && _accessManager != nullptr) { - AuthManager* instance = AuthManager::getInstance(); - Info(_log, "Removing restoration token"); - instance->savePipewire(""); + _accessManager->savePipewire(""); } } void PipewireGrabber::grabFrame() { bool stopNow = false; + - if (_semaphore.tryAcquire()) + if (_initialized && _isActive) { - if (_initialized && _isActive) + PipewireImage data = _getFramePipewire(); + + if (!_versionCheck) { - PipewireImage data = _getFramePipewire(); + if (data.version >= 4) + Info(_log, "Portal protocol version: %i", data.version); + else + Warning(_log, "Legacy portal protocol version: %i. To enjoy persistant autorization since version 4, you should update xdg-desktop-portal at least to version 1.12.1 *AND* provide backend that can implement it (for example newest xdg-desktop-portal-gnome).", data.version); - if (!_versionCheck) - { - if (data.version >= 4) - Info(_log, "Portal protocol version: %i", data.version); - else - Warning(_log, "Legacy portal protocol version: %i. To enjoy persistant autorization since version 4, you should update xdg-desktop-portal at least to version 1.12.1 *AND* provide backend that can implement it (for example newest xdg-desktop-portal-gnome).", data.version); + _versionCheck = true; + } - _versionCheck = true; - } + if (!_storedToken && !data.isError) + { + QString token = QString("%1").arg(_getPipewireToken()); - if (!_storedToken && !data.isError) + if (!token.isEmpty() && _accessManager != nullptr) { - QString token = QString("%1").arg(_getPipewireToken()); - - if (!token.isEmpty()) - { - AuthManager* instance = AuthManager::getInstance(); + Info(_log, "Saving restoration token: %s", QSTRING_CSTR(maskToken(token))); - Info(_log, "Saving restoration token: %s", QSTRING_CSTR(maskToken(token))); + _accessManager->savePipewire(token); - instance->savePipewire(token); - - _storedToken = true; - } - } + _storedToken = true; + } + } - if (data.data == nullptr) + if (data.data == nullptr) + { + if (data.isError) { - if (data.isError) - { - QString err = QString("%1").arg(_getPipewireError()); - Error(_log, "Could not capture pipewire frame: %s", QSTRING_CSTR(err)); - stopNow = true; - } + QString err = QString("%1").arg(_getPipewireError()); + Error(_log, "Could not capture pipewire frame: %s", QSTRING_CSTR(err)); + stopNow = true; } - else - { - _actualWidth = data.width; - _actualHeight = data.height; + } + else + { + _actualWidth = data.width; + _actualHeight = data.height; - if (data.isOrderRgb) - processSystemFrameRGBA(data.data); - else - processSystemFrameBGRA(data.data); + if (data.isOrderRgb) + processSystemFrameRGBA(data.data); + else + processSystemFrameBGRA(data.data); - _releaseFramePipewire(); - } + _releaseFramePipewire(); } - _semaphore.release(); } - + if (stopNow) { uninit(); diff --git a/sources/grabber/pipewire/PipewireHandler.cpp b/sources/grabber/pipewire/PipewireHandler.cpp index f7582d92c..4f84cefb7 100644 --- a/sources/grabber/pipewire/PipewireHandler.cpp +++ b/sources/grabber/pipewire/PipewireHandler.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -60,8 +60,8 @@ #include #include -#include -#include "PipewireHandler.h" +#include +#include // Pipewire screen grabber using Portal access interface @@ -199,7 +199,7 @@ void PipewireHandler::closeSession() _sessionHandle = ""; } - + _pwStreamListener = {}; _pwCoreListener = {}; _portalStatus = false; @@ -237,7 +237,9 @@ void PipewireHandler::closeSession() supVal.hasDma = false; releaseWorkingFrame(); - + + createMemory(0); + if (_version > 0) { std::cout << "Pipewire: driver is closed now" << std::endl; @@ -247,11 +249,6 @@ void PipewireHandler::closeSession() void PipewireHandler::releaseWorkingFrame() { - if (_image.data != nullptr) - { - free(_image.data); - _image.data = nullptr; - } } QString PipewireHandler::getSessionToken() @@ -311,6 +308,7 @@ void PipewireHandler::startSession(QString restorationToken, uint32_t requestedF _restorationToken = QString("%1").arg(restorationToken); _version = PipewireHandler::readVersion(); + _image.version = _version; if (_version < 0) { @@ -662,11 +660,8 @@ void PipewireHandler::onParamsChanged(uint32_t id, const struct spa_pod* param) void PipewireHandler::onProcessFrame() { - if (_image.data == nullptr) - { - captureFrame(); - _hasFrame = (_image.data != nullptr); - } + captureFrame(); + _hasFrame = (_image.data != nullptr); }; void PipewireHandler::getImage(PipewireImage& retVal) @@ -793,7 +788,7 @@ void PipewireHandler::captureFrame() } else { - frameBuffer = (uint8_t*) malloc(static_cast(newFrame->buffer->datas[0].chunk->stride) * _frameHeight); + frameBuffer = createMemory(newFrame->buffer->datas[0].chunk->stride * _frameHeight); for (supportedDmaFormat& supVal : _supportedDmaFormatsList) if (_frameDrmFormat == supVal.drmFormat) @@ -846,14 +841,14 @@ void PipewireHandler::captureFrame() } else { - _image.data = (uint8_t*) malloc(_image.stride * _frameHeight); - memcpy(_image.data, mappedMemory, _image.stride * _frameHeight); + _image.data = createMemory(_image.stride * _frameHeight); + memcpy(_image.data, mappedMemory, static_cast(_image.stride) * _frameHeight); } } else if (newFrame->buffer->datas->type == SPA_DATA_MemPtr) { - _image.data = (uint8_t*) malloc(_image.stride * _frameHeight); - memcpy(_image.data, static_cast(newFrame->buffer->datas[0].data), _image.stride * _frameHeight); + _image.data = createMemory(_image.stride * _frameHeight); + memcpy(_image.data, static_cast(newFrame->buffer->datas[0].data), static_cast(_image.stride) * _frameHeight); } } } @@ -869,6 +864,14 @@ void PipewireHandler::captureFrame() _infoUpdated = true; }; +uint8_t* PipewireHandler::createMemory(int size) +{ + _image.data = nullptr; + _memoryCache.resize(size); + + return _memoryCache.data(); +} + void PipewireHandler::onCoreError(uint32_t id, int seq, int res, const char *message) { reportError(QString("Pipewire: core reports error '%1'").arg(message)); @@ -1110,8 +1113,11 @@ void PipewireHandler::initEGL() { bool x11session = qgetenv("XDG_SESSION_TYPE") == QByteArrayLiteral("x11") && !qEnvironmentVariableIsSet("WAYLAND_DISPLAY"); printf("Session type: %s , X11 detected: %s\n", qgetenv("XDG_SESSION_TYPE").constData(), (x11session) ? "yes" : "no"); - if (((x11session || (displayEgl = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, (void*)EGL_DEFAULT_DISPLAY, nullptr)) == EGL_NO_DISPLAY)) && - ((!x11session || (displayEgl = eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, (void*)EGL_DEFAULT_DISPLAY, nullptr)) == EGL_NO_DISPLAY))) + + displayEgl = (x11session) ? eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, (void*)EGL_DEFAULT_DISPLAY, nullptr) : + eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, (void*)EGL_DEFAULT_DISPLAY, nullptr); + + if (displayEgl == EGL_NO_DISPLAY) { printf("PipewireEGL: no EGL display\n"); return; diff --git a/sources/grabber/pipewire/PipewireWrapper.cpp b/sources/grabber/pipewire/PipewireWrapper.cpp index 6473376fe..7ae610e7c 100644 --- a/sources/grabber/pipewire/PipewireWrapper.cpp +++ b/sources/grabber/pipewire/PipewireWrapper.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -26,17 +26,17 @@ */ #include -#include +#include -PipewireWrapper::PipewireWrapper(const QString &device, - const QString & configurationPath ) - : SystemWrapper("PIPEWIRE_SYSTEM:"+device.left(14), &_grabber) +PipewireWrapper::PipewireWrapper(const QString& device, + const QString& configurationPath) + : SystemWrapper("PIPEWIRE_SYSTEM:" + device.left(14), &_grabber) , _grabber(device, configurationPath) -{ +{ qRegisterMetaType>("Image"); - connect(&_grabber, &Grabber::newFrame, this, &SystemWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &Grabber::readError, this, &SystemWrapper::readError, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalNewCapturedFrame, this, &SystemWrapper::newCapturedFrameHandler, Qt::DirectConnection); + connect(&_grabber, &Grabber::SignalCapturingException, this, &SystemWrapper::capturingExceptionHandler, Qt::DirectConnection); } QString PipewireWrapper::getGrabberInfo() diff --git a/sources/grabber/pipewire/smartPipewire.cpp b/sources/grabber/pipewire/smartPipewire.cpp index 335ade0b1..259cc9b4c 100644 --- a/sources/grabber/pipewire/smartPipewire.cpp +++ b/sources/grabber/pipewire/smartPipewire.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,7 +25,7 @@ * SOFTWARE. */ -#include +#include #include #include #include @@ -46,21 +46,20 @@ #include #include -#include "PipewireHandler.h" +#include -std::unique_ptr _pipewireHandler(nullptr); +PipewireHandler pipewireHandler; void initPipewireDisplay(const char* restorationToken, uint32_t requestedFPS) { QString qRestorationToken = QString("%1").arg(restorationToken); - _pipewireHandler = std::unique_ptr(new PipewireHandler()); - _pipewireHandler->startSession(qRestorationToken, requestedFPS); + pipewireHandler.startSession(qRestorationToken, requestedFPS); } void releaseFramePipewire() { - _pipewireHandler->releaseWorkingFrame(); + pipewireHandler.releaseWorkingFrame(); } const char* getPipewireToken() @@ -68,8 +67,7 @@ const char* getPipewireToken() static QByteArray tokenData; QString token; - if (_pipewireHandler != nullptr) - token = _pipewireHandler->getToken(); + token = pipewireHandler.getToken(); tokenData = token.toLatin1(); @@ -78,22 +76,13 @@ const char* getPipewireToken() const char* getPipewireError() { - if (_pipewireHandler != nullptr) - { - QString err = _pipewireHandler->getError(); - return err.toLatin1().constData(); - } - - return nullptr; + QString err = pipewireHandler.getError(); + return err.toLatin1().constData(); } void uninitPipewireDisplay() { - if (_pipewireHandler != nullptr) - { - _pipewireHandler->closeSession(); - _pipewireHandler = nullptr; - } + pipewireHandler.closeSession(); } bool hasPipewire() @@ -135,10 +124,7 @@ PipewireImage getFramePipewire() { PipewireImage retVal; - if (_pipewireHandler != nullptr) - { - _pipewireHandler->getImage(retVal); - } + pipewireHandler.getImage(retVal); return retVal; } diff --git a/sources/grabber/v4l2/CMakeLists.txt b/sources/grabber/v4l2/CMakeLists.txt index ddac1b036..dcd9aeeb0 100644 --- a/sources/grabber/v4l2/CMakeLists.txt +++ b/sources/grabber/v4l2/CMakeLists.txt @@ -1,8 +1,8 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber/v4l2) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/grabber/v4l2) -FILE ( GLOB V4L2_SOURCES "${CURRENT_HEADER_DIR}/V4L2*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) +FILE ( GLOB V4L2_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) add_library(v4l2-grabber ${V4L2_SOURCES} ) @@ -13,3 +13,7 @@ target_link_libraries(v4l2-grabber target_include_directories(v4l2-grabber PUBLIC ${TURBOJPEG_INCLUDE_DIRS}) target_link_libraries(v4l2-grabber ${TURBOJPEG_LINK_LIBRARIES}) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(v4l2-grabber REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/v4l2/V4L2Grabber.cpp b/sources/grabber/v4l2/V4L2Grabber.cpp index 1a7f5567b..5219aff9f 100644 --- a/sources/grabber/v4l2/V4L2Grabber.cpp +++ b/sources/grabber/v4l2/V4L2Grabber.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,6 +25,10 @@ * SOFTWARE. */ +#include +#include +#include + #include #include #include @@ -44,12 +48,9 @@ #include #include -#include +#include -#include -#include - -#include +#include #include #define CLEAR(x) memset(&(x), 0, sizeof(x)) @@ -61,7 +62,7 @@ // some stuff for HDR tone mapping #define LUT_FILE_SIZE 50331648 -const V4L2Grabber::HyperHdrFormat supportedFormats[] = +static const V4L2Grabber::HyperHdrFormat supportedFormats[] = { { V4L2_PIX_FMT_YUYV, PixelFormat::YUYV }, { V4L2_PIX_FMT_XRGB32, PixelFormat::XRGB }, @@ -117,10 +118,10 @@ void V4L2Grabber::loadLutFile(PixelFormat color) void V4L2Grabber::setHdrToneMappingEnabled(int mode) { - if (_hdrToneMappingEnabled != mode || _lutBuffer == NULL) + if (_hdrToneMappingEnabled != mode || _lut.data() == nullptr) { _hdrToneMappingEnabled = mode; - if (_lutBuffer != NULL || !mode) + if (_lut.data() != nullptr || !mode) Debug(_log, "setHdrToneMappingMode to: %s", (mode == 0) ? "Disabled" : ((mode == 1) ? "Fullscreen" : "Border mode")); else Warning(_log, "setHdrToneMappingMode to: enable, but the LUT file is currently unloaded"); @@ -135,6 +136,7 @@ void V4L2Grabber::setHdrToneMappingEnabled(int mode) loadLutFile(PixelFormat::RGB24); _V4L2WorkerManager.Start(); } + emit SignalSetNewComponentStateToAllInstances(hyperhdr::Components::COMP_HDR, (mode != 0)); } else Debug(_log, "setHdrToneMappingMode nothing changed: %s", (mode == 0) ? "Disabled" : ((mode == 1) ? "Fullscreen" : "Border mode")); @@ -171,7 +173,7 @@ bool V4L2Grabber::init() if (!autoDiscovery && !_deviceProperties.contains(_deviceName)) { - for (auto it = _deviceProperties.begin(); it != _deviceProperties.end(); it++) + for (auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) if (it.value().name == _deviceName) { foundDevice = it.key(); @@ -189,7 +191,7 @@ bool V4L2Grabber::init() if (autoDiscovery) { Debug(_log, "Forcing auto discovery device"); - for (auto it = _deviceProperties.begin(); it != _deviceProperties.end(); it++) + for (auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) if (it.value().valid.count() > 0) { foundDevice = it.key(); @@ -1104,7 +1106,7 @@ int V4L2Grabber::read_frame() } catch (std::exception& e) { - emit readError(e.what()); + emit SignalCapturingException(e.what()); rc = false; } @@ -1143,10 +1145,10 @@ bool V4L2Grabber::process_image(v4l2_buffer* buf, const void* frameImageBuffer, { int total = (frameStat.badFrame + frameStat.goodFrame); int av = (frameStat.goodFrame > 0) ? frameStat.averageFrame / frameStat.goodFrame : 0; - + QString access = (frameStat.directAccess) ? " (direct)" : ""; if (diff >= 59000 && diff <= 65000) - emit PerformanceCounters::getInstance()->newCounter( - PerformanceReport(static_cast(PerformanceReportType::VIDEO_GRABBER), frameStat.token, this->_actualDeviceName, total / qMax(diff / 1000.0, 1.0), av, frameStat.goodFrame, frameStat.badFrame)); + emit GlobalSignals::getInstance()->SignalPerformanceNewReport( + PerformanceReport(hyperhdr::PerformanceReportType::VIDEO_GRABBER, frameStat.token, this->_actualDeviceName + access, total / qMax(diff / 1000.0, 1.0), av, frameStat.goodFrame, frameStat.badFrame)); resetCounter(now); @@ -1164,8 +1166,8 @@ bool V4L2Grabber::process_image(v4l2_buffer* buf, const void* frameImageBuffer, for (unsigned int i = 0; i < _V4L2WorkerManager.workersCount && _V4L2WorkerManager.workers != nullptr; i++) { V4L2Worker* _workerThread = _V4L2WorkerManager.workers[i]; - connect(_workerThread, SIGNAL(newFrameError(unsigned int, QString, quint64)), this, SLOT(newWorkerFrameError(unsigned int, QString, quint64))); - connect(_workerThread, SIGNAL(newFrame(unsigned int, Image, quint64, qint64)), this, SLOT(newWorkerFrame(unsigned int, Image, quint64, qint64))); + connect(_workerThread, &V4L2Worker::SignalNewFrameError, this, &V4L2Grabber::newWorkerFrameErrorHandler); + connect(_workerThread, &V4L2Worker::SignalNewFrame, this, &V4L2Grabber::newWorkerFrameHandler); } } @@ -1185,6 +1187,7 @@ bool V4L2Grabber::process_image(v4l2_buffer* buf, const void* frameImageBuffer, loadLutFile(); } + bool directAccess = !(_signalAutoDetectionEnabled || _signalDetectionEnabled || isCalibrating() || (_benchmarkStatus >= 0)); _workerThread->setup( i, buf, @@ -1192,7 +1195,7 @@ bool V4L2Grabber::process_image(v4l2_buffer* buf, const void* frameImageBuffer, (uint8_t*)frameImageBuffer, size, _actualWidth, _actualHeight, _lineLength, _cropLeft, _cropTop, _cropBottom, _cropRight, processFrameIndex, InternalClock::nowPrecise(), _hdrToneMappingEnabled, - (_lutBufferInit) ? _lutBuffer : NULL, _qframe); + (_lutBufferInit) ? _lut.data() : nullptr, _qframe, directAccess, _deviceName); if (_V4L2WorkerManager.workersCount > 1) _V4L2WorkerManager.workers[i]->start(); @@ -1210,7 +1213,7 @@ bool V4L2Grabber::process_image(v4l2_buffer* buf, const void* frameImageBuffer, return frameSend; } -void V4L2Grabber::newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) +void V4L2Grabber::newWorkerFrameErrorHandler(unsigned int workerIndex, QString error, quint64 sourceCount) { frameStat.badFrame++; if (error.indexOf(QString(UNSUPPORTED_DECODER)) == 0) @@ -1236,23 +1239,9 @@ void V4L2Grabber::newWorkerFrameError(unsigned int workerIndex, QString error, q } -void V4L2Grabber::newWorkerFrame(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) +void V4L2Grabber::newWorkerFrameHandler(unsigned int workerIndex, Image image, quint64 sourceCount, qint64 _frameBegin) { - frameStat.goodFrame++; - frameStat.averageFrame += InternalClock::nowPrecise() - _frameBegin; - - if (_signalAutoDetectionEnabled || isCalibrating()) - { - if (checkSignalDetectionAutomatic(image)) - emit newFrame(image); - } - else if (_signalDetectionEnabled) - { - if (checkSignalDetectionManual(image)) - emit newFrame(image); - } - else - emit newFrame(image); + handleNewFrame(workerIndex, image, sourceCount, _frameBegin); // get next frame if (workerIndex > _V4L2WorkerManager.workersCount) diff --git a/sources/grabber/v4l2/V4L2Worker.cpp b/sources/grabber/v4l2/V4L2Worker.cpp index 846f7b463..34004cd51 100644 --- a/sources/grabber/v4l2/V4L2Worker.cpp +++ b/sources/grabber/v4l2/V4L2Worker.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -48,14 +48,12 @@ #include #include -#include #include #include -#include - - +#include +#include std::atomic V4L2Worker::_isActive(false); @@ -63,6 +61,8 @@ V4L2WorkerManager::V4L2WorkerManager() : workers(nullptr) { workersCount = std::max(QThread::idealThreadCount(), 1); + workersCount = (workersCount > 3) ? workersCount - 1 : workersCount; + workersCount = std::min(workersCount, 4u); } V4L2WorkerManager::~V4L2WorkerManager() @@ -123,7 +123,6 @@ bool V4L2WorkerManager::isActive() V4L2Worker::V4L2Worker() : _decompress(nullptr), _isBusy(false), - _semaphore(1), _workerIndex(0), _pixelFormat(PixelFormat::NO_CHANGE), _sharedData(nullptr), @@ -155,7 +154,7 @@ void V4L2Worker::setup(unsigned int __workerIndex, v4l2_buffer* __v4l2Buf, Pixel uint8_t* __sharedData, int __size, int __width, int __height, int __lineLength, uint __cropLeft, uint __cropTop, uint __cropBottom, uint __cropRight, quint64 __currentFrame, qint64 __frameBegin, - int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe) + int __hdrToneMappingEnabled, uint8_t* __lutBuffer, bool __qframe, bool __directAccess, QString __deviceName) { _workerIndex = __workerIndex; memcpy(&_v4l2Buf, __v4l2Buf, sizeof(v4l2_buffer)); @@ -174,6 +173,8 @@ void V4L2Worker::setup(unsigned int __workerIndex, v4l2_buffer* __v4l2Buf, Pixel _hdrToneMappingEnabled = __hdrToneMappingEnabled; _lutBuffer = __lutBuffer; _qframe = __qframe; + _directAccess = __directAccess; + _deviceName = __deviceName; } v4l2_buffer* V4L2Worker::GetV4L2Buffer() @@ -202,7 +203,14 @@ void V4L2Worker::runMe() FrameDecoder::processQImage( _sharedData, _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } else @@ -216,7 +224,14 @@ void V4L2Worker::runMe() _cropLeft, _cropRight, _cropTop, _cropBottom, _sharedData, _width, _height, _lineLength, _pixelFormat, _lutBuffer, image); - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } } } @@ -251,13 +266,13 @@ void V4L2Worker::process_image_jpg_mt() if (tjDecompressHeader2(_decompress, const_cast(_sharedData), _size, &_width, &_height, &_subsamp) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } if ((_subsamp != TJSAMP_422 && _subsamp != TJSAMP_420) && _hdrToneMappingEnabled > 0) { - emit newFrameError(_workerIndex, QString("%1: %2").arg(UNSUPPORTED_DECODER).arg(_subsamp), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString("%1: %2").arg(UNSUPPORTED_DECODER).arg(_subsamp), _currentFrame); return; } @@ -271,50 +286,48 @@ void V4L2Worker::process_image_jpg_mt() if (_hdrToneMappingEnabled > 0) { size_t yuvSize = tjBufSizeYUV2(_width, 2, _height, _subsamp); - uint8_t* jpegBuffer = (uint8_t*)malloc(yuvSize); + MemoryBuffer jpgBuffer(yuvSize); - if (tjDecompressToYUV2(_decompress, const_cast(_sharedData), _size, jpegBuffer, _width, 2, _height, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && + if (tjDecompressToYUV2(_decompress, const_cast(_sharedData), _size, jpgBuffer.data(), _width, 2, _height, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - free(jpegBuffer); - - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } FrameDecoder::processImage(_cropLeft, _cropRight, _cropTop, _cropBottom, - jpegBuffer, _width, _height, _width, (_subsamp == TJSAMP_422) ? PixelFormat::MJPEG : PixelFormat::I420, _lutBuffer, image); - - free(jpegBuffer); + jpgBuffer.data(), _width, _height, _width, (_subsamp == TJSAMP_422) ? PixelFormat::MJPEG : PixelFormat::I420, _lutBuffer, image); } else if (image.width() != (uint)_width || image.height() != (uint)_height) { - uint8_t* jpegBuffer = (uint8_t*)malloc(static_cast(_width) * _height * 3); + MemoryBuffer jpgBuffer(static_cast(_width) * _height * 3); - if (tjDecompress2(_decompress, const_cast(_sharedData), _size, (uint8_t*)jpegBuffer, _width, 0, _height, TJPF_BGR, TJFLAG_BOTTOMUP | TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && + if (tjDecompress2(_decompress, const_cast(_sharedData), _size, jpgBuffer.data(), _width, 0, _height, TJPF_BGR, TJFLAG_BOTTOMUP | TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - free(jpegBuffer); - - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } FrameDecoder::processImage(_cropLeft, _cropRight, _cropTop, _cropBottom, - jpegBuffer, _width, _height, _width * 3, PixelFormat::RGB24, nullptr, image); - - free(jpegBuffer); + jpgBuffer.data(), _width, _height, _width * 3, PixelFormat::RGB24, nullptr, image); } else { if (tjDecompress2(_decompress, const_cast(_sharedData), _size, image.rawMem(), _width, 0, _height, TJPF_RGB, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0 && tjGetErrorCode(_decompress) == TJERR_FATAL) { - emit newFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); + emit SignalNewFrameError(_workerIndex, QString(tjGetErrorStr()), _currentFrame); return; } } - - emit newFrame(_workerIndex, image, _currentFrame, _frameBegin); + image.setBufferCacheSize(); + if (!_directAccess) + emit SignalNewFrame(_workerIndex, image, _currentFrame, _frameBegin); + else + { + emit GlobalSignals::getInstance()->SignalNewVideoImage(_deviceName, image); + emit SignalNewFrame(_workerIndex, Image(), _currentFrame, _frameBegin); + } } diff --git a/sources/grabber/v4l2/V4L2Wrapper.cpp b/sources/grabber/v4l2/V4L2Wrapper.cpp index 6ef9b5320..f285035d8 100644 --- a/sources/grabber/v4l2/V4L2Wrapper.cpp +++ b/sources/grabber/v4l2/V4L2Wrapper.cpp @@ -1,12 +1,41 @@ +/* V4L2Wrapper.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + #include -#include +#include +#include V4L2Wrapper::V4L2Wrapper(const QString& device, const QString& configurationPath) - : GrabberWrapper("V4L2:" + device.left(14), &_grabber) - , _grabber(device, configurationPath) + : GrabberWrapper("V4L2:" + device.left(14)) { - qRegisterMetaType>("Image"); - connect(&_grabber, &V4L2Grabber::newFrame, this, &GrabberWrapper::newFrame, Qt::DirectConnection); - connect(&_grabber, &V4L2Grabber::readError, this, &GrabberWrapper::readError, Qt::DirectConnection); + _grabber = std::unique_ptr(new V4L2Grabber(device, configurationPath)); + connect(_grabber.get(), &Grabber::SignalBenchmarkUpdate, this, &GrabberWrapper::SignalBenchmarkUpdate); + connect(_grabber.get(), &Grabber::SignalCapturingException, this, &GrabberWrapper::capturingExceptionHandler); + connect(_grabber.get(), &Grabber::SignalSetNewComponentStateToAllInstances, this, &GrabberWrapper::SignalSetNewComponentStateToAllInstances); + connect(_grabber.get(), &Grabber::SignalSaveCalibration, this, &GrabberWrapper::SignalSaveCalibration); } diff --git a/sources/hyperhdr-remote/CMakeLists.txt b/sources/hyperhdr-remote/CMakeLists.txt index 6d0b47d37..2d31db507 100644 --- a/sources/hyperhdr-remote/CMakeLists.txt +++ b/sources/hyperhdr-remote/CMakeLists.txt @@ -41,7 +41,9 @@ target_link_libraries(${PROJECT_NAME} ssdp Qt${Qt_VERSION}::Gui Qt${Qt_VERSION}::Core - Qt${Qt_VERSION}::Network) + Qt${Qt_VERSION}::Network + ${TURBOJPEG_LINK_LIBRARIES} +) if(NOT WIN32) install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperhdr/bin" COMPONENT "HyperHDR_remote" ) diff --git a/sources/hyperhdr-remote/JsonConnection.cpp b/sources/hyperhdr-remote/JsonConnection.cpp index b9791f8d3..e88e0a54f 100644 --- a/sources/hyperhdr-remote/JsonConnection.cpp +++ b/sources/hyperhdr-remote/JsonConnection.cpp @@ -1,20 +1,17 @@ -// stl includes -#include -#include -#include -#include - -// Qt includes -#include -#include -#include -#include -#include - -// hyperhdr-remote includes -#include "JsonConnection.h" +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + + #include + #include + #include + #include +#endif -// util includes +#include "JsonConnection.h" #include JsonConnection::JsonConnection(const QString& address, bool printJson) diff --git a/sources/hyperhdr-remote/JsonConnection.h b/sources/hyperhdr-remote/JsonConnection.h index 5d0f0d691..b185caf2e 100644 --- a/sources/hyperhdr-remote/JsonConnection.h +++ b/sources/hyperhdr-remote/JsonConnection.h @@ -1,10 +1,12 @@ #pragma once -// Qt includes -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif + #include -#include //forward class decl class Logger; diff --git a/sources/hyperhdr-remote/hyperhdr-remote.cpp b/sources/hyperhdr-remote/hyperhdr-remote.cpp index a28913ef7..063ba373b 100644 --- a/sources/hyperhdr-remote/hyperhdr-remote.cpp +++ b/sources/hyperhdr-remote/hyperhdr-remote.cpp @@ -1,20 +1,19 @@ -// stl includes -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + + #include + #include + #include + #include +#endif -// Qt includes #include #include -// hyperhdr-remote include -#include "JsonConnection.h" +#include -// ssdp discover +#include "JsonConnection.h" #include - #include "HyperhdrConfig.h" #include #include diff --git a/sources/hyperhdr/CMakeLists.txt b/sources/hyperhdr/CMakeLists.txt index d672371b6..cd5d5bcf3 100644 --- a/sources/hyperhdr/CMakeLists.txt +++ b/sources/hyperhdr/CMakeLists.txt @@ -4,20 +4,21 @@ if (WIN32) generate_win_rc_file(hyperhdr) endif() -if (WIN32) - set(hyperhdr_POWER_MNG "WinSuspend.cpp;WinSuspend.h") +if (NOT ENABLE_POWER_MANAGEMENT) +elseif (WIN32) + set(hyperhdr_POWER_MNG "SuspendHandlerWindows.cpp;SuspendHandlerWindows.h") elseif (APPLE) - set(hyperhdr_POWER_MNG "MacSuspend.mm;MacSuspend.h") + set(hyperhdr_POWER_MNG "SuspendHandlerMacOS.mm;SuspendHandlerMacOS.h") find_library(APPKIT_FRAMEWORK AppKit REQUIRED) elseif (Qt${Qt_VERSION}DBus_FOUND) - set(hyperhdr_POWER_MNG "LinuxSuspend.cpp;LinuxSuspend.h") + set(hyperhdr_POWER_MNG "SuspendHandlerLinux.cpp;SuspendHandlerLinux.h") set(hyperhdr_POWER_MNG_DBUS "Qt${Qt_VERSION}::DBus") endif() add_executable(hyperhdr - hyperhdr.h + HyperHdrDaemon.h systray.h - hyperhdr.cpp + HyperHdrDaemon.cpp systray.cpp main.cpp ${hyperhdr_WIN_RC_PATH} @@ -46,6 +47,7 @@ target_link_libraries(hyperhdr Qt${Qt_VERSION}::Widgets ${hyperhdr_POWER_MNG_DBUS} ${APPKIT_FRAMEWORK} + ${TURBOJPEG_LINK_LIBRARIES} ) if (USE_STATIC_QT_PLUGINS) @@ -78,12 +80,10 @@ endif () if (ENABLE_V4L2) target_link_libraries(hyperhdr v4l2-grabber) - target_link_libraries(hyperhdr ${TURBOJPEG_LINK_LIBRARIES}) endif () if (ENABLE_MF) target_link_libraries(hyperhdr MF-grabber) - target_link_libraries(hyperhdr ${TURBOJPEG_LINK_LIBRARIES}) endif () if (ENABLE_DX) @@ -122,15 +122,15 @@ if (ENABLE_AVF) endif () if (ENABLE_SOUNDCAPWINDOWS) - target_link_libraries(hyperhdr SoundCapWindows) + target_link_libraries(hyperhdr sound-capture-windows) endif () if (ENABLE_SOUNDCAPLINUX) - target_link_libraries(hyperhdr SoundCapLinux) + target_link_libraries(hyperhdr sound-capture-linux) endif () if (ENABLE_SOUNDCAPMACOS) - target_link_libraries(hyperhdr SoundCapMacOS) + target_link_libraries(hyperhdr sound-capture-macos) endif () diff --git a/sources/hyperhdr/HyperHdrDaemon.cpp b/sources/hyperhdr/HyperHdrDaemon.cpp new file mode 100644 index 000000000..12d5d5ecf --- /dev/null +++ b/sources/hyperhdr/HyperHdrDaemon.cpp @@ -0,0 +1,664 @@ +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_STATIC_QT_PLUGINS + #include + Q_IMPORT_PLUGIN(QJpegPlugin) + Q_IMPORT_PLUGIN(QGifPlugin) +#endif + +#include +#include + +// Flatbuffer Server +#include + +// ProtoNanoBuffer Server +#include + +// ssdp +#include + +// instance zero default configuration +#include + +// AccessManager +#include + +// InstanceManager HyperHDR +#include + +// NetOrigin checks +#include + +#include + +#include "HyperHdrDaemon.h" + +HyperHdrDaemon::HyperHdrDaemon(const QString& rootPath, QApplication* parent, bool logLvlOverwrite, bool readonlyMode, QStringList params, bool isGuiApp) + : QObject(parent) + , _log(Logger::getInstance("DAEMON")) + , _instanceManager(nullptr) + , _accessManager(nullptr) + , _netOrigin(nullptr) + , _videoGrabber(nullptr) + , _systemGrabber(nullptr) + , _soundCapture(nullptr) + , _performanceCounters(nullptr) + , _discoveryWrapper(nullptr) + , _flatProtoBuffersThread(nullptr) + , _networkThread(nullptr) + , _mqttThread(nullptr) + , _suspendHandler(nullptr) + , _wrapperCEC(nullptr) + , _rootPath(rootPath) + , _params(params) + , _isGuiApp(isGuiApp) +{ + + // Register metas for thread queued connection + qRegisterMetaType>("Image"); + qRegisterMetaType("hyperhdr::Components"); + qRegisterMetaType("settings::type"); + qRegisterMetaType>("QMap"); + qRegisterMetaType>("std::vector"); + + // First load default configuration for other objects + _instanceZeroConfig = std::unique_ptr(new InstanceConfig(true, 0, this, readonlyMode)); + + // Instance manager + _instanceManager = std::shared_ptr(new HyperHdrManager(rootPath, readonlyMode), + [](HyperHdrManager* instanceManager) { + SMARTPOINTER_MESSAGE("HyperHdrManager"); + delete instanceManager; + }); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalGetInstanceManager, this, &HyperHdrDaemon::getInstanceManager, Qt::DirectConnection); + + // Access Manager + _accessManager = std::shared_ptr(new AccessManager(this, readonlyMode), + [](AccessManager* accessManager) { + SMARTPOINTER_MESSAGE("AccessManager"); + delete accessManager; + }); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalGetAccessManager, this, &HyperHdrDaemon::getAccessManager, Qt::DirectConnection); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _accessManager.get(), &AccessManager::handleSettingsUpdate); + _accessManager->handleSettingsUpdate(settings::type::NETWORK, getSetting(settings::type::NETWORK)); + + // Performance Counters + _performanceCounters = std::shared_ptr(new PerformanceCounters(), + [](PerformanceCounters* performanceCounters) { + SMARTPOINTER_MESSAGE("PerformanceCounters"); + delete performanceCounters; + }); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalGetPerformanceCounters, this, &HyperHdrDaemon::getPerformanceCounters, Qt::DirectConnection); + + // Discovery Wrapper + #ifdef ENABLE_BONJOUR + _discoveryWrapper = std::shared_ptr(new DiscoveryWrapper(this), + [](DiscoveryWrapper* bonjourBrowserWrapper) { + SMARTPOINTER_MESSAGE("DiscoveryWrapper"); + delete bonjourBrowserWrapper; + }); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalGetDiscoveryWrapper, this, &HyperHdrDaemon::getDiscoveryWrapper, Qt::DirectConnection); + #endif + + _netOrigin = std::shared_ptr(new NetOrigin(), + [](NetOrigin* netOrigin) { + SMARTPOINTER_MESSAGE("NetOrigin"); + delete netOrigin; + }); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _netOrigin.get(), &NetOrigin::settingsChangedHandler); + _netOrigin->settingsChangedHandler(settings::type::NETWORK, getSetting(settings::type::NETWORK)); + + // set inital log lvl if the loglvl wasn't overwritten by arg + if (!logLvlOverwrite) + { + settingsChangedHandler(settings::type::LOGGER, getSetting(settings::type::LOGGER)); + } + +#if defined(ENABLE_SOUNDCAPWINDOWS) || defined(ENABLE_SOUNDCAPLINUX) || defined(ENABLE_SOUNDCAPMACOS) + _soundCapture = std::shared_ptr( + new SoundGrabber(getSetting(settings::type::SNDEFFECT), this), + [](SoundGrabber* soundGrabber) { + SMARTPOINTER_MESSAGE("SoundGrabber"); + delete soundGrabber; + }); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalGetSoundCapture, this, &HyperHdrDaemon::getSoundCapture, Qt::DirectConnection); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _soundCapture.get(), &SoundGrabber::settingsChangedHandler); +#endif + + + // CEC wrapper + #ifdef ENABLE_CEC + _wrapperCEC = std::unique_ptr(new WrapperCEC()); + connect(_wrapperCEC.get(), &WrapperCEC::SignalStateChange, _instanceManager.get(), &HyperHdrManager::setSignalStateByCEC); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalRequestComponent, _wrapperCEC.get(), &WrapperCEC::sourceRequestHandler); + #endif + + // spawn all Hyperhdr instances (non blocking) + settingsChangedHandler(settings::type::VIDEOGRABBER, getSetting(settings::type::VIDEOGRABBER)); + settingsChangedHandler(settings::type::SYSTEMGRABBER, getSetting(settings::type::SYSTEMGRABBER)); + _instanceManager->startAll(); + + //Cleaning up Hyperhdr before quit + connect(parent, &QCoreApplication::aboutToQuit, this, &HyperHdrDaemon::freeObjects); + + // pipe settings changes and component state changes from HyperHDRIManager to Daemon + connect(_instanceManager.get(), &HyperHdrManager::SignalInstanceStateChanged, this, &HyperHdrDaemon::instanceStateChangedHandler); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, this, &HyperHdrDaemon::settingsChangedHandler); + + // power management + #if defined(HAVE_POWER_MANAGEMENT) + _suspendHandler = std::unique_ptr(new SuspendHandler()); + connect(_suspendHandler.get(), &SuspendHandler::SignalHibernate, _instanceManager.get(), &HyperHdrManager::hibernate); + + #ifdef _WIN32 + if (QAbstractEventDispatcher::instance() != nullptr) + QAbstractEventDispatcher::instance()->installNativeEventFilter(_suspendHandler.get()); + #endif + #endif + + // ---- network services ----- + startNetworkServices(); +} + +HyperHdrDaemon::~HyperHdrDaemon() = default; + +void HyperHdrDaemon::instanceStateChangedHandler(InstanceState state, quint8 instance, const QString& name) +{ + // start web server if needed + if (state == InstanceState::START) + { + if (_instanceManager->areInstancesReady()) + { + if (_systemGrabber != nullptr) + { + _systemGrabber->linker.acquire(1); + } + + if (_videoGrabber != nullptr) + { + _videoGrabber->linker.acquire(1); + } + + if (_flatProtoBuffersThread != nullptr && !_flatProtoBuffersThread->isRunning()) + { + _flatProtoBuffersThread->start(); + } + + if (_mqttThread != nullptr && !_mqttThread->isRunning()) + { + _mqttThread->start(); + } + + if (_networkThread != nullptr && !_networkThread->isRunning()) + { + _networkThread->start(); + } + } + } +} + +QJsonDocument HyperHdrDaemon::getSetting(settings::type type) const +{ + if (_instanceZeroConfig == nullptr) + { + Error(_log, "Default configuration is not initialized"); + return QJsonDocument(); + } + else + return _instanceZeroConfig->getSetting(type); +} + +void HyperHdrDaemon::freeObjects() +{ + Info(_log, "Cleaning up HyperHdr before quit [preparing]"); + + disconnect(GlobalSignals::getInstance(), nullptr, nullptr, nullptr); + disconnect(_instanceManager.get(), nullptr, nullptr, nullptr); + HyperHdrInstance::signalTerminateTriggered(); + + Info(_log, "Releasing SmartPointer: CEC [ 1/15]"); + _wrapperCEC = nullptr; + + Info(_log, "Releasing SmartPointer: SuspendHandler [ 2/15]"); + #if defined(_WIN32) && defined(HAVE_POWER_MANAGEMENT) + if (QAbstractEventDispatcher::instance() != nullptr && _suspendHandler != nullptr) + QAbstractEventDispatcher::instance()->removeNativeEventFilter(_suspendHandler.get()); + #endif + _suspendHandler = nullptr; + + Info(_log, "Releasing SmartPointer: FlatBuffer/ProtoServer/Forwarder [ 3/15]"); + _flatProtoBuffersThread = nullptr; + + Info(_log, "Releasing SmartPointer: MQTT [ 4/15]"); + _mqttThread = nullptr; + + Info(_log, "Releasing SmartPointer: WebServers/SSDP/JSonServer [ 5/15]"); + _networkThread = nullptr; + + Info(_log, "Stopping: InstanceManager [ 6/15]"); + _instanceManager->stopAllonExit(); + + Info(_log, "Releasing SmartPointer: SoundGrabber [ 7/15]"); + _soundCapture = nullptr; + + Info(_log, "Releasing SmartPointer: BonjourBrowserWrapper [ 8/15]"); + _discoveryWrapper = nullptr; + + Info(_log, "Releasing SmartPointer: VideoGrabber [ 9/15]"); + _videoGrabber = nullptr; + + Info(_log, "Releasing SmartPointer: SoftwareGrabber [10/15]"); + _systemGrabber = nullptr; + + Info(_log, "Releasing SmartPointer: InstanceZeroConfiguration [11/15]"); + _instanceZeroConfig = nullptr; + + Info(_log, "Releasing SmartPointer: InstanceManager [12/15]"); + _instanceManager = nullptr; + + Info(_log, "Releasing SmartPointer: AccessManager [13/15]"); + _accessManager = nullptr; + + Info(_log, "Releasing SmartPointer: PerformanceCounters [14/15]"); + _performanceCounters = nullptr; + + Info(_log, "Releasing SmartPointer: NetOrigin [15/15]"); + _netOrigin = nullptr; + + Info(_log, "Resources are freed [finished]"); +} + +void HyperHdrDaemon::startNetworkServices() +{ + ////////////////////////////////////////// + // PROTO/FLATBUFFERS THREAD // + ////////////////////////////////////////// + + QThread* _flatProtoThread = new QThread(); + std::vector flatProtoThreadClients; + + _flatProtoThread->setObjectName("FlatProtoThread"); + + FlatBufferServer* _flatBufferServer = new FlatBufferServer(_netOrigin, getSetting(settings::type::FLATBUFSERVER), _rootPath); + _flatBufferServer->moveToThread(_flatProtoThread); + flatProtoThreadClients.push_back(_flatBufferServer); + if (_videoGrabber == nullptr) + { + Warning(_log, "The USB grabber was disabled during build. FlatbufferServer now controlls the HDR state."); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalRequestComponent, _flatBufferServer, &FlatBufferServer::signalRequestSourceHandler); + connect(_flatBufferServer, &FlatBufferServer::SignalSetNewComponentStateToAllInstances, _instanceManager.get(), &HyperHdrManager::SignalSetNewComponentStateToAllInstances); + } + connect(_flatProtoThread, &QThread::started, _flatBufferServer, &FlatBufferServer::initServer); + connect(_flatProtoThread, &QThread::finished, _flatBufferServer, &FlatBufferServer::deleteLater); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _flatBufferServer, &FlatBufferServer::handleSettingsUpdate); + + NetworkForwarder* _networkForwarder = new NetworkForwarder(); + _networkForwarder->moveToThread(_flatProtoThread); + connect(_flatProtoThread, &QThread::started, _networkForwarder, &NetworkForwarder::startedHandler); + connect(_flatProtoThread, &QThread::finished, _networkForwarder, &NetworkForwarder::deleteLater); + flatProtoThreadClients.push_back(_networkForwarder); + +#if defined(ENABLE_PROTOBUF) + ProtoServer* _protoServer = new ProtoServer(_netOrigin, getSetting(settings::type::PROTOSERVER)); + _protoServer->moveToThread(_flatProtoThread); + flatProtoThreadClients.push_back(_protoServer); + connect(_flatProtoThread, &QThread::started, _protoServer, &ProtoServer::initServer); + connect(_flatProtoThread, &QThread::finished, _protoServer, &ProtoServer::deleteLater); + connect(_protoServer, &ProtoServer::SignalImportFromProto, _flatBufferServer, &FlatBufferServer::SignalImportFromProto); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate); +#endif + + _flatProtoBuffersThread = std::unique_ptr>( + _flatProtoThread, [flatProtoThreadClients](QThread* protoThread) { + THREAD_MULTI_REMOVER(QString("FlatProtoBufferThread and children"), protoThread, flatProtoThreadClients); + }); + + ///////////////////////////////////////// + // NETWORK THREAD // + ///////////////////////////////////////// + QThread* _netThread = new QThread(); + std::vector networkThreadClients; + + _netThread->setObjectName("NetworkThread"); + + JsonServer* _jsonServer = new JsonServer(_netOrigin, getSetting(settings::type::JSONSERVER)); + _jsonServer->moveToThread(_netThread); + networkThreadClients.push_back(_jsonServer); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _jsonServer, &JsonServer::handleSettingsUpdate); + + WebServer* _webserver = new WebServer(_netOrigin, getSetting(settings::type::WEBSERVER), false); + _webserver->moveToThread(_netThread); + networkThreadClients.push_back(_webserver); + connect(_netThread, &QThread::started, _webserver, &WebServer::initServer); + connect(_netThread, &QThread::finished, _webserver, &WebServer::deleteLater); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _webserver, &WebServer::handleSettingsUpdate); + + WebServer* _sslWebserver = new WebServer(_netOrigin, getSetting(settings::type::WEBSERVER), true); + _sslWebserver->moveToThread(_netThread); + networkThreadClients.push_back(_sslWebserver); + connect(_netThread, &QThread::started, _sslWebserver, &WebServer::initServer); + connect(_netThread, &QThread::finished, _sslWebserver, &WebServer::deleteLater); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _sslWebserver, &WebServer::handleSettingsUpdate); + + SSDPHandler* _ssdp = new SSDPHandler( + _accessManager->getID(), + getSetting(settings::type::FLATBUFSERVER).object()["port"].toInt(), + getSetting(settings::type::PROTOSERVER).object()["port"].toInt(), + getSetting(settings::type::JSONSERVER).object()["port"].toInt(), + getSetting(settings::type::WEBSERVER).object()["sslPort"].toInt(), + getSetting(settings::type::WEBSERVER).object()["port"].toInt(), + getSetting(settings::type::GENERAL).object()["name"].toString()); + _ssdp->moveToThread(_netThread); + networkThreadClients.push_back(_ssdp); + connect(_ssdp, &SSDPHandler::newSsdpXmlDesc, _webserver, &WebServer::setSsdpXmlDesc); + connect(_netThread, &QThread::started, _ssdp, &SSDPHandler::initServer); + connect(_netThread, &QThread::finished, _ssdp, &SSDPHandler::deleteLater); + connect(_webserver, &WebServer::stateChange, _ssdp, &SSDPHandler::handleWebServerStateChange); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _ssdp, &SSDPHandler::handleSettingsUpdate); + + _networkThread = std::unique_ptr>(_netThread, + [networkThreadClients](QThread* networkThread) { + THREAD_MULTI_REMOVER(QString("NetworkThread and children"), networkThread, networkThreadClients); + }); + +#ifdef ENABLE_MQTT + QThread* mqttThread = new QThread(); + mqtt* _mqtt = new mqtt(getSetting(settings::type::MQTT)); + _mqtt->moveToThread(mqttThread); + connect(mqttThread, &QThread::started, _mqtt, &mqtt::begin); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, _mqtt, &mqtt::handleSettingsUpdate); + connect(mqttThread, &QThread::finished, _mqtt, &mqtt::deleteLater); + + _mqttThread = std::unique_ptr>( mqttThread, + [_mqtt](QThread* mqttThread) { + THREAD_REMOVER(QString("MQTT"), mqttThread, _mqtt); + }); +#endif +} + +quint16 HyperHdrDaemon::getWebPort() +{ + return (quint16)getSetting(settings::type::WEBSERVER).object()["port"].toInt(8090); +} + +template +void HyperHdrDaemon::createVideoGrabberHelper(QJsonDocument config, QString deviceName, QString rootPath) +{ + auto videoDetection = getSetting(settings::type::VIDEODETECTION); + + _videoGrabber = std::shared_ptr( + new GrabberHelper(), + [](GrabberHelper* vgrabber) { + THREAD_REMOVER(QString("VideoGrabber"), vgrabber->thread(), vgrabber); + } + ); + + QThread* _videoGrabberThread = new QThread(); + + _videoGrabber->moveToThread(_videoGrabberThread); + connect(_videoGrabberThread, &QThread::started, _videoGrabber.get(), &GrabberHelper::SignalCreateGrabber); + connect(_videoGrabberThread, &QThread::finished, _videoGrabber.get(), &GrabberHelper::deleteLater); + connect(_videoGrabber.get(), &GrabberHelper::SignalCreateGrabber, _videoGrabber.get(), [this, videoDetection, config, deviceName, rootPath]() { + _videoGrabber->linker.release(1); + auto videoGrabberInstance = new T(deviceName, rootPath); + _videoGrabber->setGrabber(videoGrabberInstance); + connect(videoGrabberInstance, &GrabberWrapper::SignalSetNewComponentStateToAllInstances, _instanceManager.get(), &HyperHdrManager::SignalSetNewComponentStateToAllInstances); + connect(videoGrabberInstance, &GrabberWrapper::SignalSaveCalibration, _instanceManager.get(), &HyperHdrManager::saveCalibration); + connect(_videoGrabber->thread(), &QThread::finished, videoGrabberInstance, &GrabberWrapper::deleteLater); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, videoGrabberInstance, &GrabberWrapper::handleSettingsUpdate); + connect(_instanceManager.get(), &HyperHdrManager::SignalInstancePauseChanged, videoGrabberInstance, &T::SignalInstancePauseChanged); + #ifdef ENABLE_CEC + connect(_wrapperCEC.get(), &WrapperCEC::SignalKeyPressed, videoGrabberInstance, &GrabberWrapper::SignalCecKeyPressed); + #endif + videoGrabberInstance->GrabberWrapper::handleSettingsUpdate(settings::type::VIDEOGRABBER, config); + videoGrabberInstance->GrabberWrapper::handleSettingsUpdate(settings::type::VIDEODETECTION, videoDetection); + _videoGrabber->linker.release(1); + }); + + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalGetVideoGrabber, this, &HyperHdrDaemon::getVideoGrabber, Qt::DirectConnection); + _videoGrabberThread->start(); + _videoGrabber->linker.acquire(1); +} + +void HyperHdrDaemon::createSoftwareGrabberHelper(QJsonDocument config, QString deviceName, QString rootPath) +{ + _systemGrabber = std::shared_ptr( + new GrabberHelper(), + [](GrabberHelper* sgrabber) { + THREAD_REMOVER(QString("SystemGrabber"), sgrabber->thread(), sgrabber); + } + ); + + QThread* _systemGrabberThread = new QThread(); + + _systemGrabber->moveToThread(_systemGrabberThread); + connect(_systemGrabberThread, &QThread::started, _systemGrabber.get(), &GrabberHelper::SignalCreateGrabber); + connect(_systemGrabberThread, &QThread::finished, _systemGrabber.get(), &GrabberHelper::deleteLater); + connect(_systemGrabber.get(), &GrabberHelper::SignalCreateGrabber, _systemGrabber.get(), [this, config, deviceName, rootPath]() { + bool force = false; + + _systemGrabber->linker.release(1); + SystemWrapper* softwareGrabberInstance = nullptr; + +#if defined(ENABLE_DX) + if (softwareGrabberInstance == nullptr) + { + auto candidate = new DxWrapper(deviceName, _rootPath); + if (!candidate->isActivated(force)) + { + Warning(_log, "The system doesn't support the DirectX11 system grabber"); + candidate->deleteLater(); + } + else + softwareGrabberInstance = candidate; + } +#endif +#if defined(ENABLE_PIPEWIRE) + bool forcePipewire = _params.contains("pipewire"); + if (softwareGrabberInstance == nullptr && (_isGuiApp || forcePipewire)) + { + auto candidate = new PipewireWrapper(deviceName, _rootPath); + if (!candidate->isActivated(force)) + { + Warning(_log, "The system doesn't support the Pipewire/Portal grabber"); + candidate->deleteLater(); + } + else + softwareGrabberInstance = candidate; + } +#endif +#if defined(ENABLE_X11) + if (softwareGrabberInstance == nullptr) + { + auto candidate = new X11Wrapper(deviceName, _rootPath); + if (!candidate->isActivated(force)) + { + Warning(_log, "The system doesn't support the X11 system grabber"); + candidate->deleteLater(); + } + else + softwareGrabberInstance = candidate; + } +#endif +#if defined(ENABLE_FRAMEBUFFER) + if (softwareGrabberInstance == nullptr) + { + auto candidate = new FrameBufWrapper(deviceName, _rootPath); + if (!candidate->isActivated(force)) + { + Warning(_log, "The system doesn't support the framebuffer system grabber"); + candidate->deleteLater(); + } + else + softwareGrabberInstance = candidate; + } +#endif +#if defined(ENABLE_MAC_SYSTEM) + if (softwareGrabberInstance == nullptr) + { + auto candidate = new macOsWrapper(deviceName, _rootPath); + if (!candidate->isActivated(force)) + { + Warning(_log, "The system doesn't support the macOS system grabber"); + candidate->deleteLater(); + } + else + softwareGrabberInstance = candidate; + } +#endif + + if (softwareGrabberInstance != nullptr) + { + _systemGrabber->setGrabber(softwareGrabberInstance); + + connect(_systemGrabber->thread(), &QThread::finished, softwareGrabberInstance, &SystemWrapper::deleteLater); + connect(_instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, softwareGrabberInstance, &SystemWrapper::handleSettingsUpdate); + softwareGrabberInstance->SystemWrapper::handleSettingsUpdate(settings::type::SYSTEMGRABBER, config); + } + _systemGrabber->linker.release(1); + }); + + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalGetSystemGrabber, this, &HyperHdrDaemon::getSystemGrabber, Qt::DirectConnection); + _systemGrabberThread->start(); + _systemGrabber->linker.acquire(1); +} + +void HyperHdrDaemon::getInstanceManager(std::shared_ptr& retVal) +{ + retVal = _instanceManager; +}; + +void HyperHdrDaemon::getAccessManager(std::shared_ptr& retVal) +{ + retVal = _accessManager; +}; + +void HyperHdrDaemon::getSoundCapture(std::shared_ptr& retVal) +{ + retVal = _soundCapture; +} + +void HyperHdrDaemon::getSystemGrabber(std::shared_ptr& systemGrabber) +{ + systemGrabber = _systemGrabber; +} + +void HyperHdrDaemon::getVideoGrabber(std::shared_ptr& videoGrabber) +{ + videoGrabber = _videoGrabber; +} + +void HyperHdrDaemon::getPerformanceCounters(std::shared_ptr& performanceCounters) +{ + performanceCounters = _performanceCounters; +} + +void HyperHdrDaemon::getDiscoveryWrapper(std::shared_ptr& discoveryWrapper) +{ + discoveryWrapper = _discoveryWrapper; +} + +void HyperHdrDaemon::settingsChangedHandler(settings::type settingsType, const QJsonDocument& config) +{ + if (settingsType == settings::type::LOGGER) + { + const QJsonObject& logConfig = config.object(); + + std::string level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug + if (level == "silent") + { + Logger::setLogLevel(Logger::OFF); + } + else if (level == "warn") + { + Logger::setLogLevel(Logger::LogLevel::WARNING); + } + else if (level == "verbose") + { + Logger::setLogLevel(Logger::INFO); + } + else if (level == "debug") + { + Logger::setLogLevel(Logger::DEBUG); + } + } + + if (settingsType == settings::type::SNDEFFECT) + { + } + + if (settingsType == settings::type::VIDEOGRABBER && _videoGrabber == nullptr) + { + const QJsonObject& grabberConfig = config.object(); + const QString deviceName = grabberConfig["device"].toString("auto"); + +#if defined(ENABLE_AVF) + if (_videoGrabber == nullptr) + { + createVideoGrabberHelper(config, deviceName, _rootPath); + } +#elif !defined(ENABLE_MF) && !defined(ENABLE_V4L2) + Warning(_log, "The AVF grabber can not be instantiated, because it has been left out from the build"); +#endif + + +#if defined(ENABLE_MF) + if (_videoGrabber == nullptr) + { + createVideoGrabberHelper(config, deviceName, _rootPath); + } +#elif !defined(ENABLE_V4L2) && !defined(ENABLE_AVF) + Warning(_log, "The MF grabber can not be instantiated, because it has been left out from the build"); +#endif + + +#if defined(ENABLE_V4L2) + if (_videoGrabber == nullptr) + { + createVideoGrabberHelper(config, deviceName, _rootPath); + } +#elif !defined(ENABLE_MF) && !defined(ENABLE_AVF) + Warning(_log, "!The v4l2 grabber can not be instantiated, because it has been left out from the build"); +#endif + + } + + if (settingsType == settings::type::SYSTEMGRABBER && _systemGrabber == nullptr) + { + #if defined(ENABLE_DX) || defined(ENABLE_PIPEWIRE) || defined(ENABLE_X11) || defined(ENABLE_FRAMEBUFFER) || defined(ENABLE_MAC_SYSTEM) + const QJsonObject& grabberConfig = config.object(); + const QString deviceName = grabberConfig["device"].toString("auto"); + createSoftwareGrabberHelper(config, deviceName, _rootPath); + #endif + } +} diff --git a/sources/hyperhdr/HyperHdrDaemon.h b/sources/hyperhdr/HyperHdrDaemon.h new file mode 100644 index 000000000..17497bc83 --- /dev/null +++ b/sources/hyperhdr/HyperHdrDaemon.h @@ -0,0 +1,174 @@ +#pragma once + +#include + +#ifndef PCH_ENABLED + #include + #include + #include +#endif + +#ifdef ENABLE_V4L2 + #include +#else + typedef QObject V4L2Wrapper; +#endif + +#ifdef ENABLE_MF + #include +#else + typedef QObject MFWrapper; +#endif + +#ifdef ENABLE_AVF + #include +#else + typedef QObject AVFWrapper; +#endif + +#ifdef ENABLE_DX +#include +#else + typedef QObject DxWrapper; +#endif + +#ifdef ENABLE_X11 +#include +#else + typedef QObject X11Wrapper; +#endif + +#ifdef ENABLE_FRAMEBUFFER +#include +#else + typedef QObject FrameBufWrapper; +#endif + +#ifdef ENABLE_PIPEWIRE +#include +#else + typedef QObject PipewireWrapper; +#endif + +#ifdef ENABLE_MQTT +#include +#else + typedef QObject mqtt; +#endif + + +#ifdef ENABLE_MAC_SYSTEM +#include +#else + typedef QObject macOsWrapper; +#endif + +#ifdef ENABLE_BONJOUR +#include +#else + typedef QObject DiscoveryWrapper; +#endif + +#ifdef ENABLE_CEC + #include +#else + typedef QObject WrapperCEC; +#endif + +#if defined(ENABLE_SOUNDCAPWINDOWS) + #include +#elif defined(ENABLE_SOUNDCAPLINUX) + #include +#elif defined(ENABLE_SOUNDCAPMACOS) + #include +#else + class SoundGrabber : public QObject { public: void ForcedClose() {} }; +#endif + +#include + +// settings management +#include +#include +#include + +class HyperHdrManager; +class PerformanceCounters; +class SysTray; +class JsonServer; +class WebServer; +class InstanceConfig; +class SSDPHandler; +class FlatBufferServer; +class ProtoServer; +class AccessManager; +class NetOrigin; +class VideoThread; +class GrabberHelper; +class QApplication; + +#if defined(_WIN32) && defined(ENABLE_POWER_MANAGEMENT) + #include "SuspendHandlerWindows.h" +#elif defined(__APPLE__) && defined(ENABLE_POWER_MANAGEMENT) + #include "SuspendHandlerMacOS.h" +#elif defined(__linux__) && defined(HYPERHDR_HAVE_DBUS) && defined(ENABLE_POWER_MANAGEMENT) + #include "SuspendHandlerLinux.h" +#else + typedef QObject SuspendHandler; +#endif + +namespace hyperhdr { enum class InstanceState; } + +class HyperHdrDaemon : public QObject +{ + Q_OBJECT + + +public: + HyperHdrDaemon(const QString& rootPath, QApplication* parent, bool logLvlOverwrite, bool readonlyMode = false, QStringList params = QStringList(), bool isGuiApp = true); + ~HyperHdrDaemon(); + + QJsonDocument getSetting(settings::type type) const; + +public slots: + void freeObjects(); + quint16 getWebPort(); + +public slots: + void instanceStateChangedHandler(hyperhdr::InstanceState state, quint8 instance, const QString& name = QString()); + void settingsChangedHandler(settings::type type, const QJsonDocument& config); + void getInstanceManager(std::shared_ptr& retVal); + void getAccessManager(std::shared_ptr& retVal); + void getSoundCapture(std::shared_ptr& retVal); + void getSystemGrabber(std::shared_ptr& systemGrabber); + void getVideoGrabber(std::shared_ptr& videoGrabber); + void getPerformanceCounters(std::shared_ptr& performanceCounters); + void getDiscoveryWrapper(std::shared_ptr& discoveryWrapper); + +private: + void startNetworkServices(); + template void createVideoGrabberHelper(QJsonDocument config, QString deviceName, QString rootPath); + void createSoftwareGrabberHelper(QJsonDocument config, QString deviceName, QString rootPath); + + Logger* _log; + std::shared_ptr _instanceManager; + std::shared_ptr _accessManager; + std::shared_ptr _netOrigin; + std::shared_ptr _videoGrabber; + std::shared_ptr _systemGrabber; + std::shared_ptr _soundCapture; + std::shared_ptr _performanceCounters; + std::shared_ptr _discoveryWrapper; + std::unique_ptr> _flatProtoBuffersThread; + std::unique_ptr> _networkThread; + std::unique_ptr> _mqttThread; + std::unique_ptr _suspendHandler; + std::unique_ptr _wrapperCEC; + std::unique_ptr _instanceZeroConfig; + + QString _rootPath; + bool _readonlyMode; + QStringList _params; + bool _isGuiApp; +}; + diff --git a/sources/hyperhdr/LinuxSuspend.cpp b/sources/hyperhdr/SuspendHandlerLinux.cpp similarity index 56% rename from sources/hyperhdr/LinuxSuspend.cpp rename to sources/hyperhdr/SuspendHandlerLinux.cpp index 771c090f8..6d05ac7c8 100644 --- a/sources/hyperhdr/LinuxSuspend.cpp +++ b/sources/hyperhdr/SuspendHandlerLinux.cpp @@ -1,8 +1,8 @@ -/* LinuxSuspend.cpp +/* SuspendHandlerLinux.cpp * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -33,38 +33,49 @@ #include #include #include -#include "LinuxSuspend.h" +#include "SuspendHandlerLinux.h" #include #include #include -#include +#include #include -const QString UPOWER_SERVICE = QStringLiteral("org.freedesktop.login1"); -const QString UPOWER_PATH = QStringLiteral("/org/freedesktop/login1"); -const QString UPOWER_INTER = QStringLiteral("org.freedesktop.login1.Manager"); +namespace { + const QString UPOWER_SERVICE = QStringLiteral("org.freedesktop.login1"); + const QString UPOWER_PATH = QStringLiteral("/org/freedesktop/login1"); + const QString UPOWER_INTER = QStringLiteral("org.freedesktop.login1.Manager"); +} SuspendHandler::SuspendHandler() { - QDBusConnection bus = QDBusConnection::systemBus(); - - if (!bus.isConnected()) + if (!QDBusConnection::sessionBus().isConnected()) { - std::cout << "SYSTEM BUS IS NOT CONNECTED!\n"; + std::cout << "SYSTEM BUS IS NOT CONNECTED!" << std::endl; return; } - if (!bus.connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_INTER, "PrepareForSleep", this, SLOT(sleeping(bool)))) - std::cout << "COULD NOT REGISTER SLEEP HANDLER!\n"; + if (!QDBusConnection::sessionBus().connect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_INTER, "PrepareForSleep", this, SLOT(sleeping(bool)))) + std::cout << "COULD NOT REGISTER SLEEP HANDLER!" << std::endl; else - std::cout << "SLEEP HANDLER REGISTERED!\n"; + std::cout << "SLEEP HANDLER REGISTERED!" << std::endl; +} + +SuspendHandler::~SuspendHandler() +{ + if (QDBusConnection::sessionBus().isConnected()) + { + if (!QDBusConnection::sessionBus().disconnect(UPOWER_SERVICE, UPOWER_PATH, UPOWER_INTER, "PrepareForSleep", this, SLOT(sleeping(bool)))) + std::cout << "COULD NOT DEREGISTER SLEEP HANDLER!" << std::endl; + else + std::cout << "SLEEP HANDLER DEREGISTERED!" << std::endl; + } } void SuspendHandler::sleeping(bool sleep) { if (sleep) - AUTO_CALL_1(HyperHdrIManager::getInstance(), hibernate, bool, false) + emit SignalHibernate(false); else - AUTO_CALL_1(HyperHdrIManager::getInstance(), hibernate, bool, true) + emit SignalHibernate(true); } diff --git a/sources/hyperhdr/LinuxSuspend.h b/sources/hyperhdr/SuspendHandlerLinux.h similarity index 89% rename from sources/hyperhdr/LinuxSuspend.h rename to sources/hyperhdr/SuspendHandlerLinux.h index 56ad3ccf2..9f76d6f10 100644 --- a/sources/hyperhdr/LinuxSuspend.h +++ b/sources/hyperhdr/SuspendHandlerLinux.h @@ -1,10 +1,10 @@ #pragma once -/* LinuxSuspend.h +/* SuspendHandlerLinux.h * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -28,11 +28,17 @@ */ #include +#define HAVE_POWER_MANAGEMENT + class SuspendHandler : public QObject { Q_OBJECT +signals: + void SignalHibernate(bool wakeUp); + public: SuspendHandler(); + ~SuspendHandler(); public slots: void sleeping(bool sleep); diff --git a/sources/hyperhdr/MacSuspend.h b/sources/hyperhdr/SuspendHandlerMacOS.h similarity index 88% rename from sources/hyperhdr/MacSuspend.h rename to sources/hyperhdr/SuspendHandlerMacOS.h index 090392ca8..aca0904ed 100644 --- a/sources/hyperhdr/MacSuspend.h +++ b/sources/hyperhdr/SuspendHandlerMacOS.h @@ -1,10 +1,10 @@ #pragma once -/* MacSuspend.h +/* SuspendHandlerMacOS.h * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -28,9 +28,15 @@ */ #include +#define HAVE_POWER_MANAGEMENT + class SuspendHandler : public QObject { Q_OBJECT +signals: + void SignalHibernate(bool wakeUp); + public: SuspendHandler(); + ~SuspendHandler(); }; diff --git a/sources/hyperhdr/MacSuspend.mm b/sources/hyperhdr/SuspendHandlerMacOS.mm similarity index 80% rename from sources/hyperhdr/MacSuspend.mm rename to sources/hyperhdr/SuspendHandlerMacOS.mm index 95c9d3551..018410c2f 100644 --- a/sources/hyperhdr/MacSuspend.mm +++ b/sources/hyperhdr/SuspendHandlerMacOS.mm @@ -1,8 +1,8 @@ -/* MacSuspend.cpp +/* SuspendHandlerMacOS.cpp * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -33,16 +33,15 @@ #include #include #include -#include "LinuxSuspend.h" +#include "SuspendHandlerMacOS.h" #include #include #include -#include +#include #include #include #import - @interface MacSuspendHandler : NSObject { } @@ -52,6 +51,12 @@ -(void)goingWake: (NSNotification*)note; @end +namespace { + MacSuspendHandler* _macSuspendHandlerInstance = nil; + SuspendHandler* _suspendHandler = nullptr; +} + + @implementation MacSuspendHandler - (id)init @@ -71,21 +76,28 @@ - (id)init - (void)goingSleep: (NSNotification*)note { - AUTO_CALL_1(HyperHdrIManager::getInstance(), hibernate, bool, false) + if (_suspendHandler != nullptr) + emit _suspendHandler->SignalHibernate(false); } - (void)goingWake: (NSNotification*)note { - AUTO_CALL_1(HyperHdrIManager::getInstance(), hibernate, bool, true) + if (_suspendHandler != nullptr) + emit _suspendHandler->SignalHibernate(true); } @end -MacSuspendHandler* _macSuspendHandlerInstance = nullptr; - SuspendHandler::SuspendHandler() { _macSuspendHandlerInstance = [MacSuspendHandler new]; + _suspendHandler = this; +} + +SuspendHandler::~SuspendHandler() +{ + _macSuspendHandlerInstance = nil; + _suspendHandler = nullptr; } diff --git a/sources/hyperhdr/WinSuspend.cpp b/sources/hyperhdr/SuspendHandlerWindows.cpp similarity index 83% rename from sources/hyperhdr/WinSuspend.cpp rename to sources/hyperhdr/SuspendHandlerWindows.cpp index 28d7b073b..9e910b05b 100644 --- a/sources/hyperhdr/WinSuspend.cpp +++ b/sources/hyperhdr/SuspendHandlerWindows.cpp @@ -1,8 +1,8 @@ -/* WinSuspend.cpp +/* SuspendHandlerWindows.cpp * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -31,23 +31,31 @@ #include #include #include -#include "WinSuspend.h" +#include "SuspendHandlerWindows.h" #include #include #include -#include +#include #include SuspendHandler::SuspendHandler() { auto handle = reinterpret_cast (_widget.winId()); _notifyHandle = RegisterSuspendResumeNotification(handle, DEVICE_NOTIFY_WINDOW_HANDLE); + + if (_notifyHandle == NULL) + std::cout << "COULD NOT REGISTER SLEEP HANDLER!" << std::endl; + else + std::cout << "SLEEP HANDLER REGISTERED!" << std::endl; } SuspendHandler::~SuspendHandler() -{ +{ if (_notifyHandle != NULL) + { UnregisterSuspendResumeNotification(_notifyHandle); + std::cout << "SLEEP HANDLER DEREGISTERED!" << std::endl; + } _notifyHandle = NULL; } @@ -64,11 +72,11 @@ bool SuspendHandler::nativeEventFilter(const QByteArray& eventType, void* messag switch (msg->wParam) { case PBT_APMRESUMESUSPEND: - AUTO_CALL_1(HyperHdrIManager::getInstance(), hibernate, bool, true) + emit SignalHibernate(true); return true; break; case PBT_APMSUSPEND: - AUTO_CALL_1(HyperHdrIManager::getInstance(), hibernate, bool, false) + emit SignalHibernate(false); return true; break; } diff --git a/sources/hyperhdr/WinSuspend.h b/sources/hyperhdr/SuspendHandlerWindows.h similarity index 88% rename from sources/hyperhdr/WinSuspend.h rename to sources/hyperhdr/SuspendHandlerWindows.h index 636a72bd1..0b7903d01 100644 --- a/sources/hyperhdr/WinSuspend.h +++ b/sources/hyperhdr/SuspendHandlerWindows.h @@ -1,10 +1,10 @@ #pragma once -/* WinSuspend.h +/* SuspendHandlerWindows.h * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -33,10 +33,17 @@ #define NOMINMAX #include -class SuspendHandler : public QAbstractNativeEventFilter { +#define HAVE_POWER_MANAGEMENT + +class SuspendHandler : public QObject, public QAbstractNativeEventFilter { + Q_OBJECT + QWidget _widget; HPOWERNOTIFY _notifyHandle; +signals: + void SignalHibernate(bool wakeUp); + public: SuspendHandler(); ~SuspendHandler(); diff --git a/sources/hyperhdr/hyperhdr.cpp b/sources/hyperhdr/hyperhdr.cpp deleted file mode 100644 index e840e0a96..000000000 --- a/sources/hyperhdr/hyperhdr.cpp +++ /dev/null @@ -1,654 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include // Required to determine the cmake options - -#ifdef USE_STATIC_QT_PLUGINS - #include - Q_IMPORT_PLUGIN(QJpegPlugin) - Q_IMPORT_PLUGIN(QGifPlugin) -#endif - -// bonjour browser -#ifdef ENABLE_BONJOUR -#include -#endif - -#include -#include - -// Flatbuffer Server -#include - -// ProtoNanoBuffer Server -#include - -// ssdp -#include - -// settings -#include - -// AuthManager -#include - -// InstanceManager HyperHDR -#include - -// NetOrigin checks -#include - - -// EffectFileHandler -#include - -#ifdef ENABLE_SOUNDCAPWINDOWS -#include -#endif - -#ifdef ENABLE_SOUNDCAPLINUX -#include -#endif - -#ifdef ENABLE_SOUNDCAPMACOS -#include -#endif - -#include - -#include "hyperhdr.h" - -HyperHdrDaemon* HyperHdrDaemon::daemon = nullptr; - -HyperHdrDaemon::HyperHdrDaemon(const QString& rootPath, QObject* parent, bool logLvlOverwrite, bool readonlyMode, QStringList params) - : QObject(parent), _log(Logger::getInstance("DAEMON")) - , _instanceManager(new HyperHdrIManager(rootPath, this, readonlyMode)) - , _authManager(new AuthManager(this, readonlyMode)) -#ifdef ENABLE_BONJOUR - , _bonjourBrowserWrapper(new DiscoveryWrapper()) -#endif - , _netOrigin(new NetOrigin(this)) - , _webserver(nullptr) - , _sslWebserver(nullptr) - , _jsonServer(nullptr) - , _v4l2Grabber(nullptr) - , _mfGrabber(nullptr) - , _avfGrabber(nullptr) - , _macGrabber(nullptr) - , _dxGrabber(nullptr) - , _x11Grabber(nullptr) - , _fbGrabber(nullptr) - , _pipewireGrabber(nullptr) - , _cecHandler(nullptr) - , _ssdp(nullptr) - , _flatBufferServer(nullptr) - , _mqtt(nullptr) -#if defined(ENABLE_PROTOBUF) - , _protoServer(nullptr) -#endif -#if defined(ENABLE_SOUNDCAPWINDOWS) || defined(ENABLE_SOUNDCAPLINUX) || defined(ENABLE_SOUNDCAPMACOS) - , _snd(nullptr) -#endif - , _suspendHandler(nullptr) - , _rootPath(rootPath) - , _params(params) -{ - HyperHdrDaemon::daemon = this; - - // Register metas for thread queued connection - qRegisterMetaType>("Image"); - qRegisterMetaType("hyperhdr::Components"); - qRegisterMetaType("settings::type"); - qRegisterMetaType>("QMap"); - qRegisterMetaType>("std::vector"); - - // init settings - _settingsManager = new SettingsManager(0, this, readonlyMode); - - // set inital log lvl if the loglvl wasn't overwritten by arg - if (!logLvlOverwrite) - { - handleSettingsUpdate(settings::type::LOGGER, getSetting(settings::type::LOGGER)); - } - - // performance counter - PerformanceCounters::getInstance(); - -#if defined(ENABLE_SOUNDCAPWINDOWS) - // init SoundHandler - _snd = new SoundCapWindows(getSetting(settings::type::SNDEFFECT), this); - connect(this, &HyperHdrDaemon::settingsChanged, _snd, &SoundCapWindows::handleSettingsUpdate); -#elif defined(ENABLE_SOUNDCAPLINUX) - // init SoundHandler - _snd = new SoundCapLinux(getSetting(settings::type::SNDEFFECT), this); - connect(this, &HyperHdrDaemon::settingsChanged, _snd, &SoundCapLinux::handleSettingsUpdate); -#elif defined(ENABLE_SOUNDCAPMACOS) - _snd = new SoundCapMacOS(getSetting(settings::type::SNDEFFECT), this); - connect(this, &HyperHdrDaemon::settingsChanged, _snd, &SoundCapMacOS::handleSettingsUpdate); -#endif - // init EffectFileHandler - EffectDBHandler* efh = new EffectDBHandler(rootPath, getSetting(settings::type::EFFECTS), this); - connect(this, &HyperHdrDaemon::settingsChanged, efh, &EffectDBHandler::handleSettingsUpdate); - - // connect and apply settings for AuthManager - connect(this, &HyperHdrDaemon::settingsChanged, _authManager, &AuthManager::handleSettingsUpdate); - _authManager->handleSettingsUpdate(settings::type::NETWORK, _settingsManager->getSetting(settings::type::NETWORK)); - - // connect and apply settings for NetOrigin - connect(this, &HyperHdrDaemon::settingsChanged, _netOrigin, &NetOrigin::handleSettingsUpdate); - _netOrigin->handleSettingsUpdate(settings::type::NETWORK, _settingsManager->getSetting(settings::type::NETWORK)); - - // spawn all Hyperhdr instances (non blocking) - handleSettingsUpdate(settings::type::VIDEOGRABBER, getSetting(settings::type::VIDEOGRABBER)); - handleSettingsUpdate(settings::type::SYSTEMGRABBER, getSetting(settings::type::SYSTEMGRABBER)); - _instanceManager->startAll(); - - //Cleaning up Hyperhdr before quit - connect(parent, SIGNAL(aboutToQuit()), this, SLOT(freeObjects())); - - // pipe settings changes and component state changes from HyperHDRIManager to Daemon - connect(_instanceManager, &HyperHdrIManager::settingsChanged, this, &HyperHdrDaemon::settingsChanged); - connect(_instanceManager, &HyperHdrIManager::compStateChangeRequest, this, &HyperHdrDaemon::compStateChangeRequest); - connect(_instanceManager, &HyperHdrIManager::instanceStateChanged, this, &HyperHdrDaemon::instanceStateChanged); - connect(_instanceManager, &HyperHdrIManager::settingsChanged, this, &HyperHdrDaemon::handleSettingsUpdateGlobal); - - // listen for setting changes of framegrabber and v4l2 - connect(this, &HyperHdrDaemon::settingsChanged, this, &HyperHdrDaemon::handleSettingsUpdate); - - // ---- network services ----- - startNetworkServices(); - - _suspendHandler = std::unique_ptr(new SuspendHandler()); - -#ifdef _WIN32 - if (QAbstractEventDispatcher::instance() != nullptr) - QAbstractEventDispatcher::instance()->installNativeEventFilter(_suspendHandler.get()); -#endif -} - -void HyperHdrDaemon::instanceStateChanged(InstanceState state, quint8 instance, const QString& name) -{ - // start web server if needed - if (state == InstanceState::H_STARTED) - { - if (_instanceManager->areInstancesReady()) - { - if (_webserver != nullptr && !_webserver->thread()->isRunning()) - _webserver->thread()->start(); - if (_sslWebserver != nullptr && !_sslWebserver->thread()->isRunning()) - _sslWebserver->thread()->start(); - } - } - - // cec - updateCEC(); -} - -HyperHdrDaemon::~HyperHdrDaemon() -{ -#if defined(ENABLE_SOUNDCAPWINDOWS) || defined(ENABLE_SOUNDCAPLINUX) || defined(ENABLE_SOUNDCAPMACOS) - delete _snd; -#endif - delete _settingsManager; -} - -QJsonDocument HyperHdrDaemon::getSetting(settings::type type) const -{ - return _settingsManager->getSetting(type); -} - -void HyperHdrDaemon::freeObjects() -{ - Debug(_log, "Cleaning up HyperHdr before quit."); - - LedDevice::signalTerminateTriggered(); - - // unload cec - unloadCEC(); - -#if defined(ENABLE_SOUNDCAPWINDOWS) || defined(ENABLE_SOUNDCAPLINUX) || defined(ENABLE_SOUNDCAPMACOS) - if (_snd) - _snd->ForcedClose(); -#endif - - // destroy network first as a client might want to access hyperhdr - delete _jsonServer; - _jsonServer = nullptr; - - if (_flatBufferServer != nullptr) - { - auto flatBufferServerThread = _flatBufferServer->thread(); - flatBufferServerThread->quit(); - flatBufferServerThread->wait(); - delete flatBufferServerThread; - _flatBufferServer = nullptr; - } - -#if defined(ENABLE_PROTOBUF) - if (_protoServer != nullptr) - { - auto protoServerThread = _protoServer->thread(); - protoServerThread->quit(); - protoServerThread->wait(); - delete protoServerThread; - _protoServer = nullptr; - } -#endif - - //ssdp before webserver - if (_ssdp != nullptr) - { - auto ssdpThread = _ssdp->thread(); - ssdpThread->quit(); - ssdpThread->wait(); - delete ssdpThread; - _ssdp = nullptr; - } - - if (_webserver != nullptr) - { - auto webserverThread = _webserver->thread(); - webserverThread->quit(); - webserverThread->wait(); - delete webserverThread; - _webserver = nullptr; - } - - if (_sslWebserver != nullptr) - { - auto sslWebserverThread = _sslWebserver->thread(); - sslWebserverThread->quit(); - sslWebserverThread->wait(); - delete sslWebserverThread; - _sslWebserver = nullptr; - } - - // stop HyperHDRs (non blocking) - _instanceManager->stopAll(); - -#ifdef ENABLE_BONJOUR - delete _bonjourBrowserWrapper; - _bonjourBrowserWrapper = nullptr; -#endif - - delete _mqtt; - delete _v4l2Grabber; - delete _mfGrabber; - delete _dxGrabber; - delete _avfGrabber; - delete _macGrabber; - delete _x11Grabber; - delete _fbGrabber; - delete _pipewireGrabber; - - _v4l2Grabber = nullptr; - _mfGrabber = nullptr; - _dxGrabber = nullptr; - _avfGrabber = nullptr; - _macGrabber = nullptr; - _x11Grabber = nullptr; - _fbGrabber = nullptr; - _pipewireGrabber = nullptr; -} - -void HyperHdrDaemon::startNetworkServices() -{ - // Create Json server - _jsonServer = new JsonServer(getSetting(settings::type::JSONSERVER)); - connect(this, &HyperHdrDaemon::settingsChanged, _jsonServer, &JsonServer::handleSettingsUpdate); - - // Create FlatBuffer server in thread - _flatBufferServer = new FlatBufferServer(getSetting(settings::type::FLATBUFSERVER), _rootPath); - QThread* fbThread = new QThread(this); - fbThread->setObjectName("FlatBufferServerThread"); - _flatBufferServer->moveToThread(fbThread); - connect(fbThread, &QThread::started, _flatBufferServer, &FlatBufferServer::initServer); - connect(fbThread, &QThread::finished, _flatBufferServer, &FlatBufferServer::deleteLater); - connect(this, &HyperHdrDaemon::settingsChanged, _flatBufferServer, &FlatBufferServer::handleSettingsUpdate); - fbThread->start(); - -#if defined(ENABLE_PROTOBUF) - // Create Proto server in thread - _protoServer = new ProtoServer(getSetting(settings::type::PROTOSERVER)); - QThread* pThread = new QThread(this); - pThread->setObjectName("ProtoServerThread"); - _protoServer->moveToThread(pThread); - connect(pThread, &QThread::started, _protoServer, &ProtoServer::initServer); - connect(pThread, &QThread::finished, _protoServer, &ProtoServer::deleteLater); - connect(this, &HyperHdrDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate); - pThread->start(); -#endif - - // Create Webserver in thread - _webserver = new WebServer(getSetting(settings::type::WEBSERVER), false); - QThread* wsThread = new QThread(this); - wsThread->setObjectName("WebServerThread"); - _webserver->moveToThread(wsThread); - connect(wsThread, &QThread::started, _webserver, &WebServer::initServer); - connect(wsThread, &QThread::finished, _webserver, &WebServer::deleteLater); - connect(this, &HyperHdrDaemon::settingsChanged, _webserver, &WebServer::handleSettingsUpdate); - - // Create SSL Webserver in thread - _sslWebserver = new WebServer(getSetting(settings::type::WEBSERVER), true); - QThread* sslWsThread = new QThread(this); - sslWsThread->setObjectName("SSLWebServerThread"); - _sslWebserver->moveToThread(sslWsThread); - connect(sslWsThread, &QThread::started, _sslWebserver, &WebServer::initServer); - connect(sslWsThread, &QThread::finished, _sslWebserver, &WebServer::deleteLater); - connect(this, &HyperHdrDaemon::settingsChanged, _sslWebserver, &WebServer::handleSettingsUpdate); - - // Create SSDP server in thread - _ssdp = new SSDPHandler(_webserver, - getSetting(settings::type::FLATBUFSERVER).object()["port"].toInt(), - getSetting(settings::type::PROTOSERVER).object()["port"].toInt(), - getSetting(settings::type::JSONSERVER).object()["port"].toInt(), - getSetting(settings::type::WEBSERVER).object()["sslPort"].toInt(), - getSetting(settings::type::GENERAL).object()["name"].toString()); - QThread* ssdpThread = new QThread(this); - ssdpThread->setObjectName("SSDPThread"); - _ssdp->moveToThread(ssdpThread); - connect(ssdpThread, &QThread::started, _ssdp, &SSDPHandler::initServer); - connect(ssdpThread, &QThread::finished, _ssdp, &SSDPHandler::deleteLater); - connect(_webserver, &WebServer::stateChange, _ssdp, &SSDPHandler::handleWebServerStateChange); - connect(this, &HyperHdrDaemon::settingsChanged, _ssdp, &SSDPHandler::handleSettingsUpdate); - ssdpThread->start(); - -#ifdef ENABLE_MQTT - _mqtt = new mqtt(this); - connect(this, &HyperHdrDaemon::settingsChanged, _mqtt, &mqtt::handleSettingsUpdate); - emit _mqtt->handleSettingsUpdate(settings::type::WEBSERVER, _settingsManager->getSetting(settings::type::WEBSERVER)); - QTimer::singleShot(1500, [this]() {emit _mqtt->handleSettingsUpdate(settings::type::MQTT, _settingsManager->getSetting(settings::type::MQTT)); }); -#endif -} - -quint16 HyperHdrDaemon::getWebPort() -{ - return (quint16)getSetting(settings::type::WEBSERVER).object()["port"].toInt(8090); -} - -void HyperHdrDaemon::handleSettingsUpdateGlobal(settings::type settingsType, const QJsonDocument& config) -{ - if (settingsType == settings::type::SYSTEMCONTROL || settingsType == settings::type::VIDEOCONTROL || settingsType == settings::type::VIDEOGRABBER) - { - // cec - updateCEC(); - } -} - -void HyperHdrDaemon::handleSettingsUpdate(settings::type settingsType, const QJsonDocument& config) -{ - if (settingsType == settings::type::LOGGER) - { - const QJsonObject& logConfig = config.object(); - - std::string level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug - if (level == "silent") - { - Logger::setLogLevel(Logger::OFF); - } - else if (level == "warn") - { - Logger::setLogLevel(Logger::LogLevel::WARNING); - } - else if (level == "verbose") - { - Logger::setLogLevel(Logger::INFO); - } - else if (level == "debug") - { - Logger::setLogLevel(Logger::DEBUG); - } - } - - if (settingsType == settings::type::SNDEFFECT) - { - } - - if (settingsType == settings::type::VIDEOGRABBER) - { - const QJsonObject& grabberConfig = config.object(); - -#if defined(ENABLE_AVF) - - if (_avfGrabber == nullptr) - { - _avfGrabber = new AVFWrapper(grabberConfig["device"].toString("auto"), _rootPath); - - _avfGrabber->handleSettingsUpdate(settings::type::VIDEOGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _avfGrabber, &AVFWrapper::handleSettingsUpdate); - } - -#elif !defined(ENABLE_MF) && !defined(ENABLE_V4L2) - Warning(_log, "The AVF grabber can not be instantiated, because it has been left out from the build"); -#endif - - -#if defined(ENABLE_MF) - if (_mfGrabber == nullptr) - { - _mfGrabber = new MFWrapper(grabberConfig["device"].toString("auto"), _rootPath); - - _mfGrabber->handleSettingsUpdate(settings::type::VIDEOGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _mfGrabber, &MFWrapper::handleSettingsUpdate); - } -#elif !defined(ENABLE_V4L2) && !defined(ENABLE_AVF) - Warning(_log, "The MF grabber can not be instantiated, because it has been left out from the build"); -#endif - - -#if defined(ENABLE_V4L2) - if (_v4l2Grabber == nullptr) - { - _v4l2Grabber = new V4L2Wrapper(grabberConfig["device"].toString("auto"), _rootPath); - - _v4l2Grabber->handleSettingsUpdate(settings::type::VIDEOGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _v4l2Grabber, &V4L2Wrapper::handleSettingsUpdate); - } -#elif !defined(ENABLE_MF) && !defined(ENABLE_AVF) - Warning(_log, "!The v4l2 grabber can not be instantiated, because it has been left out from the build"); -#endif - - emit settingsChanged(settings::type::VIDEODETECTION, getSetting(settings::type::VIDEODETECTION)); - } - - if (settingsType == settings::type::SYSTEMGRABBER) - { - const QJsonObject& grabberConfig = config.object(); - -#if defined(ENABLE_MAC_SYSTEM) - - if (_macGrabber == nullptr) - { - _macGrabber = new macOsWrapper(grabberConfig["device"].toString("auto"), _rootPath); - - _macGrabber->handleSettingsUpdate(settings::type::SYSTEMGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _macGrabber, &macOsWrapper::handleSettingsUpdate); - } - -#elif !defined(ENABLE_DX) && !defined(ENABLE_X11) - Warning(_log, "The MACOS system grabber can not be instantiated, because it has been left out from the build"); -#endif - -#if defined(ENABLE_DX) - if (_dxGrabber == nullptr) - { - _dxGrabber = new DxWrapper(grabberConfig["device"].toString("auto"), _rootPath); - - _dxGrabber->handleSettingsUpdate(settings::type::SYSTEMGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _dxGrabber, &DxWrapper::handleSettingsUpdate); - } -#elif !defined(ENABLE_MAC_SYSTEM) && !defined(ENABLE_X11) - Warning(_log, "The DX Windows system grabber can not be instantiated, because it has been left out from the build"); -#endif - -#if defined(ENABLE_PIPEWIRE) - if (_fbGrabber == nullptr && _x11Grabber == nullptr && _pipewireGrabber == nullptr) - { - auto backupInst = SystemWrapper::instance; - - bool force = _params.contains("pipewire"); - - if (force) - Info(_log, "User chose forced Pipewire grabber activation"); - - _pipewireGrabber = new PipewireWrapper(grabberConfig["device"].toString("auto"), _rootPath); - if (_pipewireGrabber->isActivated(force)) - { - _pipewireGrabber->handleSettingsUpdate(settings::type::SYSTEMGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _pipewireGrabber, &PipewireWrapper::handleSettingsUpdate); - } - else - { - SystemWrapper::instance = backupInst; - - _pipewireGrabber->deleteLater(); - _pipewireGrabber = nullptr; - - Warning(_log, "The system doesn't support the Pipewire/Portal grabber"); - } - } -#elif !defined(ENABLE_DX) && !defined(ENABLE_MAC_SYSTEM) - Warning(_log, "The pipewire Linux system grabber can not be instantiated, because it has been left out from the build"); -#endif - -#if defined(ENABLE_X11) - if (_fbGrabber == nullptr && _x11Grabber == nullptr && _pipewireGrabber == nullptr) - { - auto backupInst = SystemWrapper::instance; - - _x11Grabber = new X11Wrapper(grabberConfig["device"].toString("auto"), _rootPath); - - if (_x11Grabber->isActivated(false)) - { - _x11Grabber->handleSettingsUpdate(settings::type::SYSTEMGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _x11Grabber, &X11Wrapper::handleSettingsUpdate); - } - else - { - SystemWrapper::instance = backupInst; - - _x11Grabber->deleteLater(); - _x11Grabber = nullptr; - - Warning(_log, "The system doesn't support the X11 grabber"); - } - } -#elif !defined(ENABLE_DX) && !defined(ENABLE_MAC_SYSTEM) - Warning(_log, "The X11 Linux system grabber can not be instantiated, because it has been left out from the build"); -#endif - -#if defined(ENABLE_FRAMEBUFFER) - if (_fbGrabber == nullptr && _x11Grabber == nullptr && _pipewireGrabber == nullptr) - { - auto backupInst = SystemWrapper::instance; - - _fbGrabber = new FrameBufWrapper(grabberConfig["device"].toString("auto"), _rootPath); - - if (_fbGrabber->isActivated(false)) - { - _fbGrabber->handleSettingsUpdate(settings::type::SYSTEMGRABBER, config); - connect(this, &HyperHdrDaemon::settingsChanged, _fbGrabber, &FrameBufWrapper::handleSettingsUpdate); - } - else - { - SystemWrapper::instance = backupInst; - - _fbGrabber->deleteLater(); - _fbGrabber = nullptr; - - Warning(_log, "The system doesn't support the FrameBuffer grabber"); - } - } -#elif !defined(ENABLE_DX) && !defined(ENABLE_MAC_SYSTEM) - Warning(_log, "The FrameBuffer Linux system grabber can not be instantiated, because it has been left out from the build"); -#endif - } -} - -void HyperHdrDaemon::updateCEC() -{ - if (_instanceManager->isCEC()) - { - Info(_log, "Request CEC"); - loadCEC(); - } - else - { - Info(_log, "Unload CEC"); - unloadCEC(); - } -} - -void HyperHdrDaemon::loadCEC() -{ -#if defined(ENABLE_CEC) - if (_cecHandler != nullptr) - return; - - Info(_log, "Opening libCEC library."); - - _cecHandler = new cecHandler(); - connect(_cecHandler, &cecHandler::stateChange, this, &HyperHdrDaemon::enableCEC); - connect(_cecHandler, &cecHandler::keyPressed, this, &HyperHdrDaemon::keyPressedCEC); - if (_cecHandler->start()) - Info(_log, "Success: libCEC library loaded."); - else - { - Error(_log, "Could not open libCEC library"); - unloadCEC(); - } -#endif -} - -void HyperHdrDaemon::unloadCEC() -{ -#if defined(ENABLE_CEC) - if (_cecHandler != nullptr) - { - disconnect(_cecHandler, &cecHandler::stateChange, this, &HyperHdrDaemon::enableCEC); - disconnect(_cecHandler, &cecHandler::keyPressed, this, &HyperHdrDaemon::keyPressedCEC); - Info(_log, "Stopping CEC"); - _cecHandler->stop(); - Info(_log, "Cleaning up CEC"); - delete _cecHandler; - _cecHandler = nullptr; - } -#endif -} - -void HyperHdrDaemon::enableCEC(bool enabled, QString info) -{ - auto manager = HyperHdrIManager::getInstance(); - - if (manager != nullptr) - { - Info(_log, "Received CEC command: %s, %s", (enabled) ? "enable" : "disable", QSTRING_CSTR(info)); - manager->setSignalStateByCEC(enabled); - } -} - -void HyperHdrDaemon::keyPressedCEC(int keyCode) -{ -#if defined(ENABLE_V4L2) - emit GrabberWrapper::getInstance()->cecKeyPressed(keyCode); -#endif -} diff --git a/sources/hyperhdr/hyperhdr.h b/sources/hyperhdr/hyperhdr.h deleted file mode 100644 index c50de7a86..000000000 --- a/sources/hyperhdr/hyperhdr.h +++ /dev/null @@ -1,209 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#ifdef ENABLE_V4L2 - #include -#else - typedef QObject V4L2Wrapper; -#endif - -#ifdef ENABLE_MF - #include -#else - typedef QObject MFWrapper; -#endif - -#ifdef ENABLE_AVF - #include -#else - typedef QObject AVFWrapper; -#endif - -#ifdef ENABLE_DX -#include -#else - typedef QObject DxWrapper; -#endif - -#ifdef ENABLE_X11 -#include -#else - typedef QObject X11Wrapper; -#endif - -#ifdef ENABLE_FRAMEBUFFER -#include -#else - typedef QObject FrameBufWrapper; -#endif - -#ifdef ENABLE_PIPEWIRE -#include -#else - typedef QObject PipewireWrapper; -#endif - -#ifdef ENABLE_MQTT -#include -#else - typedef QObject mqtt; -#endif - - -#ifdef ENABLE_MAC_SYSTEM -#include -#else - typedef QObject macOsWrapper; -#endif - -#ifdef ENABLE_CEC -#include -#else - typedef QObject cecHandler; -#endif - -#include - -// settings management -#include -#include -#include - -class HyperHdrIManager; -class SysTray; -class JsonServer; -class DiscoveryWrapper; -class WebServer; -class SettingsManager; -class SSDPHandler; -class FlatBufferServer; -class ProtoServer; -class AuthManager; -class NetOrigin; - -#ifdef ENABLE_SOUNDCAPWINDOWS - class SoundCapWindows; -#endif -#ifdef ENABLE_SOUNDCAPLINUX - class SoundCapLinux; -#endif -#ifdef ENABLE_SOUNDCAPMACOS - class SoundCapMacOS; -#endif - -#if defined(_WIN32) - #include "WinSuspend.h" -#elif defined(__APPLE__) - #include "MacSuspend.h" -#elif defined(__linux__) && defined(HYPERHDR_HAVE_DBUS) - #include "LinuxSuspend.h" -#else - typedef QObject SuspendHandler; -#endif - -namespace hyperhdr { enum class InstanceState; } - -class HyperHdrDaemon : public QObject -{ - Q_OBJECT - - friend SysTray; - -public: - HyperHdrDaemon(const QString& rootPath, QObject* parent, bool logLvlOverwrite, bool readonlyMode = false, QStringList params = QStringList()); - ~HyperHdrDaemon(); - - /// - /// @brief get the settings - /// - QJsonDocument getSetting(settings::type type) const; - - void startNetworkServices(); - - static HyperHdrDaemon* getInstance() { return daemon; } - static HyperHdrDaemon* daemon; - - - -public slots: - void freeObjects(); - void enableCEC(bool enabled, QString info); - void keyPressedCEC(int keyCode); - quint16 getWebPort(); - -signals: - - /////////////////////////////////////// - /// FROM HYPERHDR TO HYPERHDRDAEMON /// - /////////////////////////////////////// - - /// - /// @brief PIPE settings events from HyperHDR class to HyperHDRDaemon components - /// - void settingsChanged(settings::type type, const QJsonDocument& data); - - /// - /// @brief PIPE component state changes events from HyperHDR class to HyperHDRDaemon components - /// - void compStateChangeRequest(hyperhdr::Components component, bool enable); - -private slots: - /// - /// @brief Handle settings update from HyperHDR Settingsmanager emit or this constructor - /// @param type settingyType from enum - /// @param config configuration object - /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); -public slots: - void instanceStateChanged(hyperhdr::InstanceState state, quint8 instance, const QString& name = QString()); - void handleSettingsUpdateGlobal(settings::type type, const QJsonDocument& config); -private: - void loadCEC(); - void unloadCEC(); - void updateCEC(); - - Logger* _log; - HyperHdrIManager* _instanceManager; - AuthManager* _authManager; - DiscoveryWrapper* _bonjourBrowserWrapper; - NetOrigin* _netOrigin; - WebServer* _webserver; - WebServer* _sslWebserver; - JsonServer* _jsonServer; - V4L2Wrapper* _v4l2Grabber; - MFWrapper* _mfGrabber; - AVFWrapper* _avfGrabber; - macOsWrapper* _macGrabber; - DxWrapper* _dxGrabber; - X11Wrapper* _x11Grabber; - FrameBufWrapper* _fbGrabber; - PipewireWrapper* _pipewireGrabber; - cecHandler* _cecHandler; - SSDPHandler* _ssdp; - FlatBufferServer* _flatBufferServer; - mqtt* _mqtt; - -#if defined(ENABLE_PROTOBUF) - ProtoServer* _protoServer; -#endif - -#if defined(ENABLE_SOUNDCAPWINDOWS) - SoundCapWindows* _snd; -#elif defined(ENABLE_SOUNDCAPLINUX) - SoundCapLinux* _snd; -#elif defined(ENABLE_SOUNDCAPMACOS) - SoundCapMacOS* _snd; -#endif - - std::unique_ptr _suspendHandler; - - SettingsManager* _settingsManager; - - // application root path - QString _rootPath; - QStringList _params; -}; diff --git a/sources/hyperhdr/main.cpp b/sources/hyperhdr/main.cpp index ce76bd6a1..ad56ab1ee 100644 --- a/sources/hyperhdr/main.cpp +++ b/sources/hyperhdr/main.cpp @@ -1,36 +1,41 @@ -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include +#endif + +#include #include -#include -#include #if !defined(__APPLE__) && !defined(_WIN32) -/* prctl is Linux only */ -#include + #include #endif -// getpid() + #ifdef _WIN32 -#include -#include + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include #else -#include + #include #endif -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "HyperhdrConfig.h" +#include #include #include @@ -39,8 +44,7 @@ #include #include "detectProcess.h" - -#include "hyperhdr.h" +#include "HyperHdrDaemon.h" #include "systray.h" using namespace commandline; @@ -64,36 +68,7 @@ void CreateConsole() #define PERM0664 QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther | QFileDevice::WriteOwner | QFileDevice::WriteGroup -#ifndef _WIN32 -void signal_handler(int signum) -{ - // HyperHDR Managment instance - HyperHdrIManager* _hyperhdr = HyperHdrIManager::getInstance(); - - if (signum == SIGCHLD) - { - // only quit when a registered child process is gone - // currently this feature is not active ... - return; - } - else if (signum == SIGUSR1) - { - if (_hyperhdr != nullptr) - { - _hyperhdr->toggleStateAllInstances(false); - } - return; - } - else if (signum == SIGUSR2) - { - if (_hyperhdr != nullptr) - { - _hyperhdr->toggleStateAllInstances(true); - } - return; - } -} -#endif +HyperHdrDaemon* hyperhdrd = nullptr; QCoreApplication* createApplication(int& argc, char* argv[]) { @@ -119,8 +94,9 @@ QCoreApplication* createApplication(int& argc, char* argv[]) #else if (!forceNoGui) { - isGuiApp = (getenv("DISPLAY") != NULL && (getenv("XDG_SESSION_TYPE") != NULL || getenv("WAYLAND_DISPLAY") != NULL)); - std::cout << "GUI application"; + isGuiApp = (getenv("DISPLAY") != NULL && + ((getenv("XDG_SESSION_TYPE") != NULL && strcmp(getenv("XDG_SESSION_TYPE"), "tty") != 0) || getenv("WAYLAND_DISPLAY") != NULL)); + std::cout << ((isGuiApp) ? "GUI" : "Console") << " application: " << std::endl; } #endif @@ -166,11 +142,6 @@ int main(int argc, char** argv) DefaultSignalHandler::install(); -#ifndef _WIN32 - signal(SIGCHLD, signal_handler); - signal(SIGUSR1, signal_handler); - signal(SIGUSR2, signal_handler); -#endif // force the locale setlocale(LC_ALL, "C"); QLocale::setDefault(QLocale::c()); @@ -306,6 +277,8 @@ int main(int argc, char** argv) } } + DBManager::initializeDatabaseFilename(dbFile); + // reset Password without spawning daemon if (parser.isSet(resetPassword)) { @@ -316,15 +289,13 @@ int main(int argc, char** argv) } else { - AuthTable* table = new AuthTable(userDataDirectory.absolutePath()); + std::unique_ptr table = std::unique_ptr(new AuthTable(false)); if (table->resetHyperhdrUser()) { Info(log, "Password reset successful"); - delete table; exit(0); } else { Error(log, "Failed to reset password!"); - delete table; exit(1); } } @@ -370,11 +341,10 @@ int main(int argc, char** argv) { Warning(log, "The user data path '%s' is not writeable. HyperHdr starts in read-only mode. Configuration updates will not be persisted!", QSTRING_CSTR(userDataDirectory.absolutePath())); } - - HyperHdrDaemon* hyperhdrd = nullptr; + try { - hyperhdrd = new HyperHdrDaemon(userDataDirectory.absolutePath(), qApp, bool(logLevelCheck), readonlyMode, params); + hyperhdrd = new HyperHdrDaemon(userDataDirectory.absolutePath(), qApp, bool(logLevelCheck), readonlyMode, params, isGuiApp); } catch (std::exception& e) { @@ -387,8 +357,8 @@ int main(int argc, char** argv) { Info(log, "start systray"); QApplication::setQuitOnLastWindowClosed(false); - SysTray tray(hyperhdrd); - tray.hide(); + SysTray* tray = new SysTray(hyperhdrd, hyperhdrd->getWebPort()); + QObject::connect(qApp, &QGuiApplication::aboutToQuit, tray, &SysTray::deleteLater); rc = (qobject_cast(app.data()))->exec(); } else @@ -397,6 +367,7 @@ int main(int argc, char** argv) } Info(log, "Application closed with code %d", rc); delete hyperhdrd; + hyperhdrd = nullptr; } catch (std::exception& e) { diff --git a/sources/hyperhdr/systray.cpp b/sources/hyperhdr/systray.cpp index bb63834b3..e73dda716 100644 --- a/sources/hyperhdr/systray.cpp +++ b/sources/hyperhdr/systray.cpp @@ -1,44 +1,85 @@ +#ifndef PCH_ENABLED + #include + #include + #include +#endif -#include #ifndef _WIN32 -#include + #include #endif -// QT includes + #include #include -#include -#include -#include +#include #include +#include +#include +#include +#include #include +#include + #include #include #include #include -#include "hyperhdr.h" +#include "HyperHdrDaemon.h" #include "systray.h" -SysTray::SysTray(HyperHdrDaemon* hyperhdrd) - : QWidget(), +SysTray::SysTray(HyperHdrDaemon* hyperhdrDaemon, quint16 webPort) + : QObject(), + _quitAction(nullptr), + _startAction(nullptr), + _stopAction(nullptr), + _colorAction(nullptr), + _settingsAction(nullptr), + _clearAction(nullptr), + _autorunAction(nullptr), + _trayIcon(nullptr), + _trayIconMenu(nullptr), + _trayIconEfxMenu(nullptr), _colorDlg(nullptr), - _hyperhdrd(hyperhdrd), - _hyperhdr(nullptr), - _instanceManager(HyperHdrIManager::getInstance()), - _webPort(8090) + _hyperhdrHandle(), + _webPort(webPort) { Q_INIT_RESOURCE(resources); - // instance changes - connect(_instanceManager, &HyperHdrIManager::instanceStateChanged, this, &SysTray::handleInstanceStateChange); + std::shared_ptr instanceManager; + hyperhdrDaemon->getInstanceManager(instanceManager); + _instanceManager = instanceManager; + connect(instanceManager.get(), &HyperHdrManager::SignalInstanceStateChanged, this, &SysTray::signalInstanceStateChangedHandler); + connect(instanceManager.get(), &HyperHdrManager::SignalSettingsChanged, this, &SysTray::signalSettingsChangedHandler); } SysTray::~SysTray() { + printf("Releasing SysTray\n"); + + if (_trayIconEfxMenu != nullptr) + { + for (QAction*& effect : _trayIconEfxMenu->actions()) + delete effect; + _trayIconEfxMenu->clear(); + } + if (_trayIconMenu != nullptr) _trayIconMenu->clear(); + + delete _quitAction; + delete _startAction; + delete _stopAction; + delete _colorAction; + delete _settingsAction; + delete _clearAction; + delete _autorunAction; + + delete _trayIcon; + delete _trayIconEfxMenu; + delete _trayIconMenu; + delete _colorDlg; } void SysTray::iconActivated(QSystemTrayIcon::ActivationReason reason) @@ -70,66 +111,60 @@ void SysTray::createTrayIcon() QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif - _trayIconMenu = std::unique_ptr(new QMenu(this)); - _trayIcon = std::unique_ptr(new QSystemTrayIcon(this)); - _trayIcon->setContextMenu(_trayIconMenu.get()); + _trayIconMenu = new QMenu(); + _trayIcon = new QSystemTrayIcon(); + _trayIcon->setContextMenu(_trayIconMenu); - _quitAction = std::unique_ptr(new QAction(tr("&Quit"), this)); - _quitIcon = std::unique_ptr(new QPixmap(":/quit.svg")); - _quitAction->setIcon(*_quitIcon.get()); - connect(_quitAction.get(), SIGNAL(triggered()), qApp, SLOT(quit())); + _quitAction = new QAction(tr("&Quit")); + _quitAction->setIcon(QPixmap(":/quit.svg")); + connect(_quitAction, &QAction::triggered, QApplication::instance(), &QApplication::quit); - _colorAction = std::unique_ptr(new QAction(tr("&Color"), this)); - _colorIcon = std::unique_ptr(new QPixmap(":/color.svg")); - _colorAction->setIcon(*_colorIcon.get()); - connect(_colorAction.get(), SIGNAL(triggered()), this, SLOT(showColorDialog())); + _colorAction = new QAction(tr("&Color")); + _colorAction->setIcon(QPixmap(":/color.svg")); + connect(_colorAction, &QAction::triggered, this, &SysTray::showColorDialog); - _settingsAction = std::unique_ptr(new QAction(tr("&Settings"), this)); - _settingsIcon = std::unique_ptr(new QPixmap(":/settings.svg")); - _settingsAction->setIcon(*_settingsIcon.get()); - connect(_settingsAction.get(), SIGNAL(triggered()), this, SLOT(settings())); + _settingsAction = new QAction(tr("&Settings")); + _settingsAction->setIcon(QPixmap(":/settings.svg")); + connect(_settingsAction, &QAction::triggered, this, &SysTray::settings); - _clearAction = std::unique_ptr(new QAction(tr("&Clear"), this)); - _clearIcon = std::unique_ptr(new QPixmap(":/clear.svg")); - _clearAction->setIcon(*_clearIcon.get()); - connect(_clearAction.get(), SIGNAL(triggered()), this, SLOT(clearEfxColor())); + _clearAction = new QAction(tr("&Clear")); + _clearAction->setIcon(QPixmap(":/clear.svg")); + connect(_clearAction, &QAction::triggered, this, &SysTray::clearEfxColor); std::list efxs; - SAFE_CALL_0_RET(_hyperhdr, getEffects, std::list, efxs); + auto _hyperhdr = _hyperhdrHandle.lock(); + if (_hyperhdr) + SAFE_CALL_0_RET(_hyperhdr.get(), getEffects, std::list, efxs); - _trayIconEfxMenu = std::unique_ptr(new QMenu(_trayIconMenu.get())); - _effectsIcon = std::unique_ptr(new QPixmap(":/effects.svg")); - _trayIconEfxMenu->setIcon(*_effectsIcon.get()); + _trayIconEfxMenu = new QMenu(); + _trayIconEfxMenu->setIcon(QPixmap(":/effects.svg")); _trayIconEfxMenu->setTitle(tr("Effects")); - _effects.clear(); for (const EffectDefinition& efx : efxs) { - std::shared_ptr efxAction = std::shared_ptr(new QAction(efx.name, this)); - connect(efxAction.get(), SIGNAL(triggered()), this, SLOT(setEffect())); - _trayIconEfxMenu->addAction(efxAction.get()); - _effects.push_back(efxAction); + QString effectName = efx.name; + QAction* efxAction = new QAction(effectName); + connect(efxAction, &QAction::triggered, this, &SysTray::setEffect); + _trayIconEfxMenu->addAction(efxAction); } #ifdef _WIN32 - _autorunAction = std::unique_ptr(new QAction(tr("&Disable autostart"), this)); - _autorunIcon = std::unique_ptr(new QPixmap(":/autorun.svg")); - - _autorunAction->setIcon(*_autorunIcon.get()); - connect(_autorunAction.get(), SIGNAL(triggered()), this, SLOT(setAutorunState())); + _autorunAction = new QAction(tr("&Disable autostart")); + _autorunAction->setIcon(QPixmap(":/autorun.svg")); + connect(_autorunAction, &QAction::triggered, this, &SysTray::setAutorunState); - _trayIconMenu->addAction(_autorunAction.get()); + _trayIconMenu->addAction(_autorunAction); _trayIconMenu->addSeparator(); #endif - _trayIconMenu->addAction(_settingsAction.get()); + _trayIconMenu->addAction(_settingsAction); _trayIconMenu->addSeparator(); - _trayIconMenu->addAction(_colorAction.get()); - _trayIconMenu->addMenu(_trayIconEfxMenu.get()); - _trayIconMenu->addAction(_clearAction.get()); + _trayIconMenu->addAction(_colorAction); + _trayIconMenu->addMenu(_trayIconEfxMenu); + _trayIconMenu->addAction(_clearAction); _trayIconMenu->addSeparator(); - _trayIconMenu->addAction(_quitAction.get()); + _trayIconMenu->addAction(_quitAction); } #ifdef _WIN32 @@ -162,14 +197,16 @@ void SysTray::setColor(const QColor& color) { std::vector rgbColor{ ColorRgb{ (uint8_t)color.red(), (uint8_t)color.green(), (uint8_t)color.blue() } }; - QUEUE_CALL_3(_hyperhdr, setColor, int, 1, std::vector, rgbColor, int, 0); + auto _hyperhdr = _hyperhdrHandle.lock(); + if (_hyperhdr) + QUEUE_CALL_3(_hyperhdr.get(), setColor, int, 1, std::vector, rgbColor, int, 0); } void SysTray::showColorDialog() { if (_colorDlg == nullptr) { - _colorDlg = new QColorDialog(this); + _colorDlg = new QColorDialog(); _colorDlg->setOptions(QColorDialog::NoButtons); connect(_colorDlg, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(setColor(const QColor&))); } @@ -184,11 +221,6 @@ void SysTray::showColorDialog() } } -void SysTray::closeEvent(QCloseEvent* event) -{ - event->ignore(); -} - void SysTray::settings() { #ifndef _WIN32 @@ -212,11 +244,6 @@ void SysTray::settings() } #endif - if (_hyperhdrd) - { - _webPort = _hyperhdrd->getWebPort(); - } - QDesktopServices::openUrl(QUrl("http://localhost:" + QString::number(_webPort) + "/", QUrl::TolerantMode)); #ifndef _WIN32 @@ -230,37 +257,40 @@ void SysTray::settings() void SysTray::setEffect() { QString efxName = qobject_cast(sender())->text(); - _hyperhdr->setEffect(efxName, 1); + auto _hyperhdr = _hyperhdrHandle.lock(); + if (_hyperhdr) + QUEUE_CALL_2(_hyperhdr.get(), setEffect, QString, efxName, int, 1); } void SysTray::clearEfxColor() { - _hyperhdr->clear(1); + auto _hyperhdr = _hyperhdrHandle.lock(); + if (_hyperhdr) + QUEUE_CALL_1(_hyperhdr.get(), clear, int, 1); } -void SysTray::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name) +void SysTray::signalInstanceStateChangedHandler(InstanceState state, quint8 instance, const QString& name) { switch (state) { - case InstanceState::H_STARTED: + case InstanceState::START: if (instance == 0) { - _hyperhdr = _instanceManager->getHyperHdrInstance(0); + std::shared_ptr instanceManager = _instanceManager.lock(); - createTrayIcon(); + if (instanceManager == nullptr) + return; - connect(_trayIcon.get(), SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); -#if !defined(__APPLE__) - connect(_quitAction.get(), &QAction::triggered, _trayIcon.get(), &QSystemTrayIcon::hide, Qt::DirectConnection); -#endif + std::shared_ptr retVal; + SAFE_CALL_1_RET(instanceManager.get(), getHyperHdrInstance, std::shared_ptr, retVal, quint8, 0); + + _hyperhdrHandle = retVal; + createTrayIcon(); - _appIcon = std::unique_ptr(new QIcon(":/hyperhdr-icon-32px.png")); - _trayIcon->setIcon(*_appIcon.get()); - _trayIcon->show(); -#if !defined(__APPLE__) - setWindowIcon(*_appIcon.get()); -#endif + connect(_trayIcon, &QSystemTrayIcon::activated, this, &SysTray::iconActivated); + + _trayIcon->setIcon(QIcon(":/hyperhdr-icon-32px.png")); + _trayIcon->show(); } break; @@ -268,3 +298,11 @@ void SysTray::handleInstanceStateChange(InstanceState state, quint8 instance, co break; } } + +void SysTray::signalSettingsChangedHandler(settings::type type, const QJsonDocument& data) +{ + if (type == settings::type::WEBSERVER) + { + _webPort = data.object()["port"].toInt(); + } +} diff --git a/sources/hyperhdr/systray.h b/sources/hyperhdr/systray.h index 433f80570..15f91a5b6 100644 --- a/sources/hyperhdr/systray.h +++ b/sources/hyperhdr/systray.h @@ -1,32 +1,32 @@ #pragma once -#include -#include -#include -#include -#include - -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif #include -#include +#include +#include +#include class HyperHdrDaemon; +class QMenu; +class QAction; +class QColorDialog; + -class SysTray : public QWidget +class SysTray : public QObject { Q_OBJECT public: - SysTray(HyperHdrDaemon* hyperhdrd); + SysTray(HyperHdrDaemon* hyperhdrDaemon, quint16 webPort); ~SysTray(); - public slots: void showColorDialog(); void setColor(const QColor& color); - void closeEvent(QCloseEvent* event); void settings(); void setEffect(); void clearEfxColor(); @@ -38,7 +38,8 @@ private slots: /// /// @brief is called whenever a hyperhdr instance state changes /// - void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name); + void signalInstanceStateChangedHandler(InstanceState state, quint8 instance, const QString& name); + void signalSettingsChangedHandler(settings::type type, const QJsonDocument& data); private: void createTrayIcon(); @@ -51,32 +52,21 @@ private slots: bool getCurrentAutorunState(); #endif - std::unique_ptr _quitAction; - std::unique_ptr _startAction; - std::unique_ptr _stopAction; - std::unique_ptr _colorAction; - std::unique_ptr _settingsAction; - std::unique_ptr _clearAction; - std::unique_ptr _autorunAction; - - std::unique_ptr _trayIcon; - std::unique_ptr _trayIconMenu; - std::unique_ptr _trayIconEfxMenu; - - std::unique_ptr _quitIcon; - std::unique_ptr _colorIcon; - std::unique_ptr _settingsIcon; - std::unique_ptr _clearIcon; - std::unique_ptr _effectsIcon; - std::unique_ptr _autorunIcon; - std::unique_ptr _appIcon; + QAction* _quitAction; + QAction* _startAction; + QAction* _stopAction; + QAction* _colorAction; + QAction* _settingsAction; + QAction* _clearAction; + QAction* _autorunAction; - std::vector> _effects; + QSystemTrayIcon* _trayIcon; + QMenu* _trayIconMenu; + QMenu* _trayIconEfxMenu; QColorDialog* _colorDlg; - HyperHdrDaemon* _hyperhdrd; - HyperHdrInstance* _hyperhdr; - HyperHdrIManager* _instanceManager; + std::weak_ptr _instanceManager; + std::weak_ptr _hyperhdrHandle; quint16 _webPort; }; diff --git a/sources/jsonserver/JsonClientConnection.cpp b/sources/jsonserver/JsonClientConnection.cpp index 5e9a80b90..8b4832e2a 100644 --- a/sources/jsonserver/JsonClientConnection.cpp +++ b/sources/jsonserver/JsonClientConnection.cpp @@ -1,6 +1,6 @@ // project includes #include "JsonClientConnection.h" -#include +#include // qt inc #include @@ -15,12 +15,12 @@ JsonClientConnection::JsonClientConnection(QTcpSocket* socket, bool localConnect connect(_socket, &QTcpSocket::disconnected, this, &JsonClientConnection::disconnected); connect(_socket, &QTcpSocket::readyRead, this, &JsonClientConnection::readRequest); // create a new instance of JsonAPI - _jsonAPI = new JsonAPI(socket->peerAddress().toString(), _log, localConnection, this); + _hyperAPI = new HyperAPI(socket->peerAddress().toString(), _log, localConnection, this); // get the callback messages from JsonAPI and send it to the client - connect(_jsonAPI, &JsonAPI::callbackMessage, this, &JsonClientConnection::sendMessage); - connect(_jsonAPI, &JsonAPI::forceClose, this, [&]() { _socket->close(); }); + connect(_hyperAPI, &HyperAPI::SignalCallbackJsonMessage, this, &JsonClientConnection::sendMessage); + connect(_hyperAPI, &HyperAPI::SignalPerformClientDisconnection, this, [&]() { _socket->close(); }); - _jsonAPI->initialize(); + _hyperAPI->initialize(); } void JsonClientConnection::readRequest() @@ -37,7 +37,7 @@ void JsonClientConnection::readRequest() _receiveBuffer = _receiveBuffer.mid(bytes); // handle message - _jsonAPI->handleMessage(message); + _hyperAPI->handleMessage(message); // try too look up '\n' again bytes = _receiveBuffer.indexOf('\n') + 1; @@ -49,11 +49,13 @@ qint64 JsonClientConnection::sendMessage(QJsonObject message) QJsonDocument writer(message); QByteArray data = writer.toJson(QJsonDocument::Compact) + "\n"; - if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) return 0; - return _socket->write(data.data(), data.size()); + if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) + return 0; + else + return _socket->write(data.data(), data.size()); } void JsonClientConnection::disconnected() { - emit connectionClosed(); + emit SignalClientConnectionClosed(this); } diff --git a/sources/jsonserver/JsonClientConnection.h b/sources/jsonserver/JsonClientConnection.h index 35c5b8258..148f4416a 100644 --- a/sources/jsonserver/JsonClientConnection.h +++ b/sources/jsonserver/JsonClientConnection.h @@ -1,52 +1,37 @@ #pragma once -// Qt includes -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif -// util includes #include -class JsonAPI; +class HyperAPI; class QTcpSocket; +class HyperHdrManager; -/// -/// The Connection object created by \a JsonServer when a new connection is established -/// class JsonClientConnection : public QObject { Q_OBJECT public: - /// - /// Constructor - /// @param socket The Socket object for this connection - /// JsonClientConnection(QTcpSocket* socket, bool localConnection); signals: - void connectionClosed(); + void SignalClientConnectionClosed(JsonClientConnection* client); public slots: qint64 sendMessage(QJsonObject); private slots: - /// - /// Slot called when new data has arrived - /// void readRequest(); - void disconnected(); private: QTcpSocket* _socket; - /// new instance of JsonAPI - JsonAPI* _jsonAPI; - - /// The buffer used for reading data from the socket + HyperAPI* _hyperAPI; QByteArray _receiveBuffer; - - /// The logger instance Logger* _log; }; diff --git a/sources/jsonserver/JsonServer.cpp b/sources/jsonserver/JsonServer.cpp index 44000ad1a..43a004b5d 100644 --- a/sources/jsonserver/JsonServer.cpp +++ b/sources/jsonserver/JsonServer.cpp @@ -7,6 +7,7 @@ #include "JsonClientConnection.h" #include +#include // qt includes #include @@ -14,14 +15,15 @@ #include #include -JsonServer::JsonServer(const QJsonDocument& config) +JsonServer::JsonServer(std::shared_ptr netOrigin, const QJsonDocument& config) : QObject() , _server(new QTcpServer(this)) , _openConnections() , _log(Logger::getInstance("JSONSERVER")) - , _netOrigin(NetOrigin::getInstance()) + , _netOrigin(netOrigin) + , _port(0) { - Debug(_log, "Created instance"); + Debug(_log, "Created new instance"); // Set trigger for incoming connections connect(_server, &QTcpServer::newConnection, this, &JsonServer::newConnection); @@ -32,6 +34,7 @@ JsonServer::JsonServer(const QJsonDocument& config) JsonServer::~JsonServer() { + Debug(_log, "The instance is deleted"); qDeleteAll(_openConnections); } @@ -90,7 +93,7 @@ void JsonServer::newConnection() _openConnections.insert(connection); // register slot for cleaning up after the connection closed - connect(connection, &JsonClientConnection::connectionClosed, this, &JsonServer::closedConnection); + connect(connection, &JsonClientConnection::SignalClientConnectionClosed, this, &JsonServer::signalClientConnectionClosedHandler); } else socket->close(); @@ -98,12 +101,12 @@ void JsonServer::newConnection() } } -void JsonServer::closedConnection() +void JsonServer::signalClientConnectionClosedHandler(JsonClientConnection* client) { - JsonClientConnection* connection = qobject_cast(sender()); - Debug(_log, "Connection closed"); - _openConnections.remove(connection); - - // schedule to delete the connection object - connection->deleteLater(); + if (client != nullptr) + { + Debug(_log, "Connection closed"); + _openConnections.remove(client); + client->deleteLater(); + } } diff --git a/sources/leddevice/CMakeLists.txt b/sources/leddevice/CMakeLists.txt index 36fe52c8b..ac563bed4 100644 --- a/sources/leddevice/CMakeLists.txt +++ b/sources/leddevice/CMakeLists.txt @@ -44,7 +44,7 @@ if ( ENABLE_SPIDEV ) endif() if ( ENABLE_WS281XPWM ) - include_directories(../../dependencies/external/rpi_ws281x) + include_directories(../../external/rpi_ws281x) FILE ( GLOB Leddevice_PWM_SOURCES "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.h" "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.cpp") endif() diff --git a/sources/leddevice/LedDevice.cpp b/sources/leddevice/LedDevice.cpp index e129520a9..b290cfea1 100644 --- a/sources/leddevice/LedDevice.cpp +++ b/sources/leddevice/LedDevice.cpp @@ -1,17 +1,17 @@ -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include -//QT include -#include -#include -#include -#include + #include + #include +#endif +#include #include #include - -//std includes -#include -#include +#include std::atomic LedDevice::_signalTerminate(false); @@ -21,7 +21,10 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent) , _log(Logger::getInstance("LEDDEVICE_" + deviceConfig["type"].toString("unknown").toUpper())) , _ledBuffer(0) , _refreshTimer(nullptr) - , _refreshTimerInterval_ms(0) + , _currentInterval(0) + , _defaultInterval(0) + , _forcedInterval(0) + , _smoothingInterval(0) , _ledCount(0) , _isRestoreOrigState(false) , _isEnabled(false) @@ -29,22 +32,119 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent) , _isDeviceReady(false) , _isOn(false) , _isDeviceInError(false) - , _retryMode(false) , _maxRetry(60) , _currentRetry(0) + , _retryTimer(nullptr) , _isRefreshEnabled(false) , _newFrame2Send(false) - , _newFrame2SendTime(0) , _blinkIndex(-1) + , _blinkTime(0) + , _instanceIndex(-1) { + std::shared_ptr discoveryWrapper = nullptr; + emit GlobalSignals::getInstance()->SignalGetDiscoveryWrapper(discoveryWrapper); + _discoveryWrapper = discoveryWrapper; + _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower(); - connect(this, &LedDevice::manualUpdate, this, &LedDevice::rewriteLEDs, Qt::QueuedConnection); + connect(this, &LedDevice::SignalManualUpdate, this, &LedDevice::rewriteLEDs, Qt::QueuedConnection); } LedDevice::~LedDevice() { stopRefreshTimer(); + stopRetryTimer(); +} + +bool LedDevice::switchOn() +{ + bool rc = false; + + Debug(_log, "Switch on"); + + if (_isOn) + { + rc = true; + } + else + { + if (_isDeviceInitialised) + { + storeState(); + + if (powerOn()) + { + _isOn = true; + rc = true; + } + } + } + return rc; +} + +bool LedDevice::switchOff() +{ + bool rc = false; + + Debug(_log, "Switch off"); + + if (!_isOn && !_isRestoreOrigState) + { + rc = true; + } + else + { + if (_isDeviceInitialised) + { + // Disable device to ensure no standard Led updates are written/processed + _isOn = false; + + rc = true; + + if (_isDeviceReady) + { + if (_isRestoreOrigState) + { + //Restore devices state + restoreState(); + } + else + { + powerOff(); + } + } + } + } + return rc; +} + +bool LedDevice::powerOff() +{ + bool rc = false; + + Debug(_log, "Power Off"); + + // Simulate power-off by writing a final "Black" to have a defined outcome + if (writeBlack() >= 0) + { + rc = true; + } + return rc; +} + +bool LedDevice::powerOn() +{ + bool rc = true; + + Debug(_log, "Power On"); + + return rc; +} + +void LedDevice::setInstanceIndex(int instanceIndex) +{ + _instanceIndex = instanceIndex; + _log = Logger::getInstance(QString("LEDDEVICE%1_%2").arg(_instanceIndex).arg(_activeDeviceType.toUpper())); } int LedDevice::open() @@ -63,11 +163,35 @@ int LedDevice::close() return retval; } +void LedDevice::setupRetry(int interval) +{ + if (_retryTimer == nullptr) + { + _currentRetry = _maxRetry; + _retryTimer = std::unique_ptr(new QTimer()); + connect(_retryTimer.get(), &QTimer::timeout, this, [this](){ + if (_currentRetry > 0 && !_signalTerminate) + { + Warning(_log, "The LED device is not ready... trying to reconnect (try %i/%i).", (_maxRetry - _currentRetry + 1), _maxRetry); + _currentRetry--; + enableDevice(true); + } + else + { + Error(_log, "The LED device is not ready... give up."); + stopRetryTimer(); + } + }); + _retryTimer->start(interval); + } +} + void LedDevice::start() { Info(_log, "Start LedDevice '%s'.", QSTRING_CSTR(_activeDeviceType)); - close(); + if (_isDeviceInitialised) + close(); _isDeviceInitialised = false; // General initialisation and configuration of LedDevice @@ -83,6 +207,8 @@ void LedDevice::enable() { if (!_signalTerminate) { + stopRetryTimer(); + if (!_isDeviceInitialised && init(_devConfig)) { Debug(_log, "First enable the device"); @@ -116,32 +242,32 @@ void LedDevice::enableDevice(bool toEmit) { _isDeviceReady = true; _isEnabled = true; + stopRetryTimer(); if (toEmit) - emit enableStateChanged(_isEnabled); + emit SignalEnableStateChanged(_isEnabled); } } if (_isRefreshEnabled) this->startRefreshTimer(); } - - _newFrame2Send = false; - _blinkIndex = -1; } void LedDevice::stop() { Debug(_log, "Stop device"); + + stopRefreshTimer(); + stopRetryTimer(); + disable(); - this->disable(); - this->stopRefreshTimer(); Info(_log, " Stopped LedDevice '%s'", QSTRING_CSTR(_activeDeviceType)); } void LedDevice::disable() { - Debug(_log, "Disable the device"); + Debug(_log, "Disable the device"); disableDevice(true); } @@ -158,11 +284,8 @@ void LedDevice::disableDevice(bool toEmit) close(); if (toEmit) - emit enableStateChanged(_isEnabled); + emit SignalEnableStateChanged(_isEnabled); } - - _newFrame2Send = false; - _blinkIndex = -1; } void LedDevice::setInError(const QString& errorMsg) @@ -174,7 +297,7 @@ void LedDevice::setInError(const QString& errorMsg) this->stopRefreshTimer(); Error(_log, "Device '%s' is disabled due to an error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(errorMsg)); - emit enableStateChanged(_isEnabled); + emit SignalEnableStateChanged(_isEnabled); } void LedDevice::setActiveDeviceType(const QString& deviceType) @@ -186,32 +309,39 @@ bool LedDevice::init(const QJsonObject& deviceConfig) { Debug(_log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData()); + _defaultInterval = deviceConfig["refreshTime"].toInt(0); + _forcedInterval = deviceConfig["forcedRefreshTime"].toInt(0); + _smoothingInterval = deviceConfig["smoothingRefreshTime"].toInt(0); + setLedCount(deviceConfig["currentLedCount"].toInt(1)); // property injected to reflect real led count - setRefreshTime(deviceConfig["refreshTime"].toInt(_refreshTimerInterval_ms)); + setRefreshTime(deviceConfig["refreshTime"].toInt(_currentInterval)); return true; } void LedDevice::startRefreshTimer() { - if (_isDeviceReady && _isEnabled && _refreshTimerInterval_ms > 0) + if (_isDeviceReady && _isEnabled && _currentInterval > 0) { // setup refreshTimer if (_refreshTimer == nullptr) { - _refreshTimer = new QTimer(this); + _refreshTimer = std::unique_ptr(new QTimer()); _refreshTimer->setTimerType(Qt::PreciseTimer); - _refreshTimer->setInterval(_refreshTimerInterval_ms); - connect(_refreshTimer, &QTimer::timeout, this, &LedDevice::rewriteLEDs); + _refreshTimer->setInterval(_currentInterval); + if (_smoothingInterval > 0) + connect(_refreshTimer.get(), &QTimer::timeout, this, &LedDevice::SignalSmoothingClockTick, Qt::UniqueConnection); + else + connect(_refreshTimer.get(), &QTimer::timeout, this, &LedDevice::rewriteLEDs, Qt::UniqueConnection); } else - _refreshTimer->setInterval(_refreshTimerInterval_ms); + _refreshTimer->setInterval(_currentInterval); Debug(_log, "Starting timer with interval = %ims", _refreshTimer->interval()); _refreshTimer->start(); } - else if (_refreshTimerInterval_ms > 0) + else if (_currentInterval > 0) { Debug(_log, "Device is not ready to start a timer"); } @@ -221,24 +351,49 @@ void LedDevice::stopRefreshTimer() { if (_refreshTimer != nullptr) { - Debug(_log, "Stopping timer"); + Debug(_log, "Stopping refresh timer"); _refreshTimer->stop(); - delete _refreshTimer; - _refreshTimer = nullptr; + _refreshTimer.reset(); + } +} + +void LedDevice::stopRetryTimer() +{ + if (_retryTimer != nullptr) + { + Debug(_log, "Stopping retry timer"); + + _retryTimer->stop(); + _retryTimer.reset(); } } -void LedDevice::setRefreshTime(int refreshTime_ms) +void LedDevice::setRefreshTime(int userInterval) { - _refreshTimerInterval_ms = qMax(refreshTime_ms, 0); + int selectedInterval = userInterval; + + if (_forcedInterval > 0) + { + selectedInterval = _forcedInterval; + if (userInterval > 0) + Warning(_log, "Ignoring user LED refresh rate. Forcing device specific refresh rate = %.2f Hz", (1000.0/selectedInterval)); + } + else if (_smoothingInterval > 0) + { + selectedInterval = _smoothingInterval; + if (userInterval > 0) + Warning(_log, "Ignoring user LED refresh rate. Forcing smoothing refresh rate = %.2f Hz", (1000.0/selectedInterval)); + } - if (_refreshTimerInterval_ms > 0) + _currentInterval = qMax(selectedInterval, 0); + + if (_currentInterval > 0) { _isRefreshEnabled = true; - Debug(_log, "Refresh interval = %dms", _refreshTimerInterval_ms); + Debug(_log, "Refresh rate = %.2f Hz", (1000.0/_currentInterval)); startRefreshTimer(); } @@ -248,10 +403,26 @@ void LedDevice::setRefreshTime(int refreshTime_ms) stopRefreshTimer(); } - Debug(_log, "RefreshTime updated to %dms", _refreshTimerInterval_ms); + Debug(_log, "Refresh interval updated to %dms", _currentInterval); +} + +int LedDevice::hasLedClock() +{ + return _forcedInterval; } -int LedDevice::updateLeds(std::vector ledValues) +void LedDevice::smoothingRestarted(int newSmoothingInterval) +{ + if (_smoothingInterval != newSmoothingInterval) + { + _smoothingInterval = newSmoothingInterval; + stopRefreshTimer(); + setRefreshTime(_defaultInterval); + Debug(_log, "LED refresh interval adjustment caused by smoothing configuration change to %dms (proposed: %dms)", _currentInterval, newSmoothingInterval); + } +} + +void LedDevice::updateLeds(std::vector ledValues) { // stats int64_t now = InternalClock::now(); @@ -261,27 +432,21 @@ int LedDevice::updateLeds(std::vector ledValues) if (_computeStats.token <= 0 || diff < 0) { _computeStats.token = PerformanceCounters::currentToken(); - _computeStats.statBegin = now; - _computeStats.frames = 0; - _computeStats.droppedFrames = 0; - _computeStats.incomingframes = 1; + _computeStats.reset(now); } else if (prevToken != (_computeStats.token = PerformanceCounters::currentToken())) { - if (_isRefreshEnabled && _refreshTimerInterval_ms > 0) + if (_isRefreshEnabled && _currentInterval > 0) { - qint64 wanted = (1000.0/_refreshTimerInterval_ms) * 60.0 * diff / 60000.0; + qint64 wanted = (1000.0/_currentInterval) * 60.0 * diff / 60000.0; _computeStats.droppedFrames = std::max(wanted - _computeStats.frames - 1, 0ll); } if (diff >= 59000 && diff <= 65000) - emit this->newCounter( - PerformanceReport(static_cast(PerformanceReportType::LED), _computeStats.token, this->_activeDeviceType, _computeStats.frames / qMax(diff / 1000.0, 1.0), _computeStats.frames, _computeStats.incomingframes, _computeStats.droppedFrames)); + emit GlobalSignals::getInstance()->SignalPerformanceNewReport( + PerformanceReport(hyperhdr::PerformanceReportType::LED, _computeStats.token, this->_activeDeviceType + _customInfo, _computeStats.frames / qMax(diff / 1000.0, 1.0), _computeStats.frames, _computeStats.incomingframes, _computeStats.droppedFrames, _instanceIndex)); - _computeStats.statBegin = now; - _computeStats.frames = 0; - _computeStats.droppedFrames = 0; - _computeStats.incomingframes = 1; + _computeStats.reset(now); } else _computeStats.incomingframes++; @@ -289,140 +454,76 @@ int LedDevice::updateLeds(std::vector ledValues) if (!_isEnabled || !_isOn || !_isDeviceReady || _isDeviceInError) { - return -1; + return; } else { - if (_blinkIndex < 0) - _lastLedValues = ledValues; + _lastLedValues = ledValues; - if (!_isRefreshEnabled && (!_newFrame2Send || now - _newFrame2SendTime > 1000 || now < _newFrame2SendTime)) + if (!_isRefreshEnabled) { + if (_newFrame2Send) + _computeStats.droppedFrames++; _newFrame2Send = true; - _newFrame2SendTime = now; - emit manualUpdate(); + emit SignalManualUpdate(); } - else if (!_isRefreshEnabled) - _computeStats.droppedFrames++; + else if (QThread::currentThread() == this->thread()) + rewriteLEDs(); } - return 0; + return; } int LedDevice::rewriteLEDs() { int retval = -1; - _newFrame2Send = false; - - if (_isEnabled && _isOn && _isDeviceReady && !_isDeviceInError && !_signalTerminate) - { - if (_lastLedValues.size() > 0) - retval = write(_lastLedValues); - - _computeStats.frames++; - } - - return retval; -} - -int LedDevice::writeBlack(int numberOfBlack) -{ - int rc = -1; - - Debug(_log, "Set LED strip to black/power off"); - - _lastLedValues = std::vector(static_cast(_ledCount), ColorRgb::BLACK); - - for (int i = 0; i < numberOfBlack; i++) + if ((_newFrame2Send || _isRefreshEnabled) && _isEnabled && _isOn && _isDeviceReady && !_isDeviceInError) { - rc = write(_lastLedValues); - } + _newFrame2Send = false; - return rc; -} - -bool LedDevice::switchOn() -{ - bool rc = false; - - Debug(_log, "Switch on"); + std::vector copy = _lastLedValues; - if (_isOn) - { - rc = true; - } - else - { - if (_isDeviceInitialised) + if (_signalTerminate) + copy = std::vector(static_cast(copy.size()), ColorRgb::BLACK); + else if (_blinkIndex >= 0) { - storeState(); - - if (powerOn()) + int64_t now = InternalClock::now(); + if (_blinkTime + 4500 < now || _blinkTime > now || _blinkIndex >= static_cast(copy.size())) + _blinkIndex = -1; + else { - _isOn = true; - rc = true; + copy = std::vector(static_cast(copy.size()), ColorRgb::BLACK); + copy[_blinkIndex] = (_blinkTime + 1500 > now) ? ColorRgb::RED : (_blinkTime + 3000 > now) ? ColorRgb::GREEN : ColorRgb::BLUE; } - } - } - return rc; -} + } -bool LedDevice::switchOff() -{ - bool rc = false; + if (copy.size() > 0) + retval = write(copy); - Debug(_log, "Switch off"); + if (_signalTerminate) + disableDevice(false); - if (!_isOn && !_isRestoreOrigState) - { - rc = true; + _computeStats.frames++; } - else - { - if (_isDeviceInitialised) - { - // Disable device to ensure no standard Led updates are written/processed - _isOn = false; - rc = true; - - if (_isDeviceReady) - { - if (_isRestoreOrigState) - { - //Restore devices state - restoreState(); - } - else - { - powerOff(); - } - } - } - } - return rc; + return retval; } -bool LedDevice::powerOff() +int LedDevice::writeBlack(int numberOfBlack) { - bool rc = false; + int rc = -1; + + Debug(_log, "Set LED strip to black/power off"); - Debug(_log, "Power Off"); + std::vector blacks = std::vector(static_cast(_ledCount), ColorRgb::BLACK); - // Simulate power-off by writing a final "Black" to have a defined outcome - if (writeBlack() >= 0) + _lastLedValues = blacks; + + for (int i = 0; i < numberOfBlack; i++) { - rc = true; + rc = write(blacks); } - return rc; -} - -bool LedDevice::powerOn() -{ - bool rc = true; - - Debug(_log, "Power On"); return rc; } @@ -461,36 +562,15 @@ void LedDevice::identifyLed(const QJsonObject& params) { _blinkIndex = params["blinkIndex"].toInt(-1); - if (_blinkIndex < 0 || _blinkIndex >= (int)_lastLedValues.size()) + if (_blinkIndex < 0) { _blinkIndex = -1; } else { - const int blinkOrg = _blinkIndex; - - for (auto iter = _lastLedValues.begin(); iter != _lastLedValues.end(); ++iter) - { - *iter = ColorRgb::BLACK; - } - - for (int i = 0; i < 6; i++) - { - ColorRgb color = (i % 3 == 0) ? ColorRgb::RED : (i % 3 == 1) ? ColorRgb::GREEN : ColorRgb::BLUE; - - QTimer::singleShot(800 * i, this, [this, color, blinkOrg]() { - if (_blinkIndex == blinkOrg && _blinkIndex >= 0 && _blinkIndex < (int)_lastLedValues.size()) - { - _lastLedValues[_blinkIndex] = color; - rewriteLEDs(); - } - }); - } - - // disable blinking after the sequence - QTimer::singleShot(4800, this, [this, blinkOrg]() { - if (_blinkIndex == blinkOrg) _blinkIndex = -1; - }); + _newFrame2Send = true; + _blinkTime = InternalClock::now(); + rewriteLEDs(); } } @@ -592,7 +672,7 @@ bool LedDevice::isInError() const { } int LedDevice::getRefreshTime() const { - return _refreshTimerInterval_ms; + return _currentInterval; } int LedDevice::getLedCount() const { @@ -606,3 +686,11 @@ QString LedDevice::getActiveDeviceType() const { bool LedDevice::componentState() const { return _isEnabled; } + +void LedDevice::LedStats::reset(int64_t now) +{ + statBegin = now; + frames = 0; + droppedFrames = 0; + incomingframes = 1; +} diff --git a/sources/leddevice/LedDeviceTemplate.h b/sources/leddevice/LedDeviceTemplate.h index dfa002312..d12cac55d 100644 --- a/sources/leddevice/LedDeviceTemplate.h +++ b/sources/leddevice/LedDeviceTemplate.h @@ -1,66 +1,19 @@ -#ifndef LEDEVICETEMPLATE_H -#define LEDEVICETEMPLATE_H +#pragma once -// LedDevice includes #include -/// -/// Implementation of a LedDevice ... -/// ... -/// class LedDeviceTemplate : public LedDevice { public: - - /// - /// @brief Constructs a specific LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceTemplate(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); protected: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; private: }; - -#endif // LEDEVICETEMPLATE_H diff --git a/sources/leddevice/LedDeviceWrapper.cpp b/sources/leddevice/LedDeviceWrapper.cpp index feaeaa1db..0ad255e78 100644 --- a/sources/leddevice/LedDeviceWrapper.cpp +++ b/sources/leddevice/LedDeviceWrapper.cpp @@ -9,6 +9,7 @@ // util #include #include +#include // qt #include @@ -21,10 +22,10 @@ LedDeviceRegistry LedDeviceWrapper::_ledDeviceMap{}; QMutex LedDeviceWrapper::_ledDeviceMapLock; -LedDeviceWrapper::LedDeviceWrapper(HyperHdrInstance* hyperhdr) - : QObject(hyperhdr) - , _hyperhdr(hyperhdr) - , _ledDevice(nullptr) +LedDeviceWrapper::LedDeviceWrapper(HyperHdrInstance* ownerInstance) + : QObject(ownerInstance) + , _ownerInstance(ownerInstance) + , _ledDevice(nullptr, nullptr) , _enabled(false) { // prepare the device constructor map @@ -35,38 +36,37 @@ LedDeviceWrapper::LedDeviceWrapper(HyperHdrInstance* hyperhdr) #undef REGISTER - _hyperhdr->setNewComponentState(hyperhdr::COMP_LEDDEVICE, false); + _ownerInstance->setNewComponentState(hyperhdr::COMP_LEDDEVICE, false); } LedDeviceWrapper::~LedDeviceWrapper() { - stopDeviceThread(); + _ledDevice.reset(); } -void LedDeviceWrapper::createLedDevice(const QJsonObject& config) +void LedDeviceWrapper::createLedDevice(QJsonObject config, int smoothingInterval) { - if (_ledDevice != nullptr) - { - stopDeviceThread(); - } + _ledDevice.reset(); + + config["smoothingRefreshTime"] = smoothingInterval; - // create thread and device - QThread* thread = new QThread(this); + QThread* thread = new QThread(); thread->setObjectName("LedDeviceThread"); - _ledDevice = LedDeviceFactory::construct(config); + + _ledDevice = std::unique_ptr( + LedDeviceFactory::construct(config), + [](LedDevice* oldLed) { + hyperhdr::THREAD_REMOVER(QString("LedDevice"), oldLed->thread(), oldLed); + } + ); + _ledDevice->setInstanceIndex(_ownerInstance->getInstanceIndex()); _ledDevice->moveToThread(thread); // setup thread management - connect(thread, &QThread::started, _ledDevice, &LedDevice::start, Qt::QueuedConnection); - - // further signals - connect(this, &LedDeviceWrapper::updateLeds, _ledDevice, &LedDevice::updateLeds); - - connect(this, &LedDeviceWrapper::stopLedDevice, _ledDevice, &LedDevice::stop, Qt::BlockingQueuedConnection); - - connect(_ledDevice, &LedDevice::enableStateChanged, this, &LedDeviceWrapper::handleInternalEnableState, Qt::QueuedConnection); - - connect(_ledDevice, &LedDevice::newCounter, this, [=](PerformanceReport pr) {pr.id = this->_hyperhdr->getInstanceIndex(); emit PerformanceCounters::getInstance()->newCounter(pr); }); + connect(thread, &QThread::started, _ledDevice.get(), &LedDevice::start); + connect(thread, &QThread::finished, _ledDevice.get(), &LedDevice::stop); + connect(_ownerInstance, &HyperHdrInstance::SignalSmoothingRestarted, _ledDevice.get(), &LedDevice::smoothingRestarted, Qt::QueuedConnection); + connect(_ledDevice.get(), &LedDevice::SignalEnableStateChanged, this, &LedDeviceWrapper::handleInternalEnableState, Qt::QueuedConnection); // start the thread thread->start(); @@ -74,60 +74,61 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config) void LedDeviceWrapper::handleComponentState(hyperhdr::Components component, bool state) { + if (_ledDevice == nullptr) + return; + if (component == hyperhdr::COMP_LEDDEVICE) { if (state) { - QUEUE_CALL_0(_ledDevice, enable); + QUEUE_CALL_0(_ledDevice.get(), enable); } else { - QUEUE_CALL_0(_ledDevice, disable); + QUEUE_CALL_0(_ledDevice.get(), disable); } - - SAFE_CALL_0_RET(_ledDevice, componentState, bool, _enabled); } } void LedDeviceWrapper::handleInternalEnableState(bool newState) { - _hyperhdr->setNewComponentState(hyperhdr::COMP_LEDDEVICE, newState); + if (_ledDevice == nullptr) + return; + + if (newState) + { + connect(_ledDevice.get(), &LedDevice::SignalSmoothingClockTick, _ownerInstance, &HyperHdrInstance::SignalSmoothingClockTick, static_cast(Qt::DirectConnection | Qt::UniqueConnection)); + connect(_ownerInstance, &HyperHdrInstance::SignalUpdateLeds, _ledDevice.get(), &LedDevice::updateLeds, Qt::UniqueConnection); + } + else + { + disconnect(_ledDevice.get(), &LedDevice::SignalSmoothingClockTick, _ownerInstance, &HyperHdrInstance::SignalSmoothingClockTick); + disconnect(_ownerInstance, &HyperHdrInstance::SignalUpdateLeds, _ledDevice.get(), &LedDevice::updateLeds); + } + + _ownerInstance->setNewComponentState(hyperhdr::COMP_LEDDEVICE, newState); _enabled = newState; if (_enabled) { - _hyperhdr->update(); + _ownerInstance->update(); } } -void LedDeviceWrapper::stopDeviceThread() -{ - // turns the LEDs off & stop refresh timers - emit stopLedDevice(); - - // get current thread - QThread* oldThread = _ledDevice->thread(); - disconnect(oldThread, nullptr, nullptr, nullptr); - oldThread->quit(); - oldThread->wait(); - delete oldThread; - - disconnect(_ledDevice, nullptr, nullptr, nullptr); - delete _ledDevice; - _ledDevice = nullptr; -} unsigned int LedDeviceWrapper::getLedCount() const { int value = 0; - SAFE_CALL_0_RET(_ledDevice, getLedCount, int, value); + if (_ledDevice != nullptr) + SAFE_CALL_0_RET(_ledDevice.get(), getLedCount, int, value); return value; } QString LedDeviceWrapper::getActiveDeviceType() const { QString value = 0; - SAFE_CALL_0_RET(_ledDevice, getActiveDeviceType, QString, value); + if (_ledDevice != nullptr) + SAFE_CALL_0_RET(_ledDevice.get(), getActiveDeviceType, QString, value); return value; } @@ -138,7 +139,18 @@ bool LedDeviceWrapper::enabled() const void LedDeviceWrapper::identifyLed(const QJsonObject& params) { - QUEUE_CALL_1(_ledDevice, blinking, QJsonObject, params); + if (_ledDevice != nullptr) + QUEUE_CALL_1(_ledDevice.get(), blinking, QJsonObject, params); +} + +int LedDeviceWrapper::hasLedClock() +{ + int hasLedClock = 0; + + if (_ledDevice != nullptr) + SAFE_CALL_0_RET(_ledDevice.get(), hasLedClock, int, hasLedClock); + + return hasLedClock; } int LedDeviceWrapper::addToDeviceMap(QString name, LedDeviceCreateFuncType funcPtr) diff --git a/sources/leddevice/dev_net/LedDeviceAtmoOrb.cpp b/sources/leddevice/dev_net/LedDeviceAtmoOrb.cpp index 3aa7252fb..70e159f35 100644 --- a/sources/leddevice/dev_net/LedDeviceAtmoOrb.cpp +++ b/sources/leddevice/dev_net/LedDeviceAtmoOrb.cpp @@ -55,7 +55,7 @@ bool LedDeviceAtmoOrb::init(const QJsonObject& deviceConfig) Debug(_log, "DeviceType : %s", QSTRING_CSTR(this->getActiveDeviceType())); Debug(_log, "LedCount : %d", this->getLedCount()); - Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms); + Debug(_log, "RefreshTime : %d", this->getRefreshTime()); Debug(_log, "MulticastGroup : %s", QSTRING_CSTR(_multicastGroup)); Debug(_log, "MulticastGroupPort: %d", _multiCastGroupPort); diff --git a/sources/leddevice/dev_net/LedDeviceAtmoOrb.h b/sources/leddevice/dev_net/LedDeviceAtmoOrb.h index 5f7cfd934..2d4b0bd16 100644 --- a/sources/leddevice/dev_net/LedDeviceAtmoOrb.h +++ b/sources/leddevice/dev_net/LedDeviceAtmoOrb.h @@ -1,148 +1,46 @@ -#ifndef LEDEVICEATMOORB_H -#define LEDEVICEATMOORB_H +#pragma once -// Qt includes -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif -// LedDevice includes #include class QUdpSocket; -/// -/// Implementation of the LedDevice interface for sending to -/// AtmoOrb devices via network -/// class LedDeviceAtmoOrb : public LedDevice { Q_OBJECT -public: - /// - /// @brief Constructs an AtmoOrb LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// +public: explicit LedDeviceAtmoOrb(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LedDevice - /// ~LedDeviceAtmoOrb() override; - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); - - /// - /// @brief Discover AtmoOrb devices available (for configuration). - /// - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// QJsonObject discover(const QJsonObject& params) override; - - /// - /// @brief Send an update to the AtmoOrb device to identify it. - /// - /// Following parameters are required - /// @code - /// { - /// "orbId" : "orb identifier in the range of (1-255)", - /// } - ///@endcode - /// - /// @param[in] params Parameters to address device - /// virtual void identify(const QJsonObject& params) override; protected: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; private: - - /// - /// Set Orbcolor - /// - /// @param orbId the orb id - /// @param color which color to set - /// @param commandType which type of command to send (off / smoothing / etc..) - /// void setColor(int orbId, const ColorRgb& color, int commandType); - - /// - /// Send Orb command - /// - /// @param bytes the byte array containing command to send over multicast - /// void sendCommand(const QByteArray& bytes); - /// QUdpSocket object used to send data over QUdpSocket* _udpSocket; - - /// QHostAddress object of multicast group IP address QHostAddress _groupAddress; - - /// String containing multicast group IP address QString _multicastGroup; - - /// Multicast port to send data to quint16 _multiCastGroupPort; - - // Multicast status bool _joinedMulticastgroup; - - /// use Orbs own (external) smoothing algorithm bool _useOrbSmoothing; - - // Maximum allowed color difference, will skip Orb (external) smoothing once reached int _skipSmoothingDiff; - - /// Array of the orb ids. QVector _orbIds; - - // Last send color map QMap lastColorRedMap; QMap lastColorGreenMap; QMap lastColorBlueMap; - QMultiMap _services; }; - -#endif // LEDEVICEATMOORB_H diff --git a/sources/leddevice/dev_net/LedDeviceCololight.cpp b/sources/leddevice/dev_net/LedDeviceCololight.cpp index 0ddef1406..f9fb63102 100644 --- a/sources/leddevice/dev_net/LedDeviceCololight.cpp +++ b/sources/leddevice/dev_net/LedDeviceCololight.cpp @@ -3,12 +3,39 @@ #include #include #include +#include #include #include -// Constants namespace { + + const uint8_t PACKET_HEADER[] = + { + 'S', 'Z', + 0x30, 0x30, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + const uint8_t PACKET_SECU[] = + { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + const uint8_t TL1_CMD_FIXED_PART[] = + { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, + 0x00, + 0x00 + }; + const bool verbose = false; const bool verbose3 = false; diff --git a/sources/leddevice/dev_net/LedDeviceCololight.h b/sources/leddevice/dev_net/LedDeviceCololight.h index bf44f2a70..f268b3c84 100644 --- a/sources/leddevice/dev_net/LedDeviceCololight.h +++ b/sources/leddevice/dev_net/LedDeviceCololight.h @@ -1,310 +1,107 @@ -#ifndef LEDEVICECOLOLIGHT_H -#define LEDEVICECOLOLIGHT_H +#pragma once -// LedDevice includes #include #include "ProviderUdp.h" -enum appID { - TL1_CMD = 0x00, - DIRECT_CONTROL = 0x01, - TRANSMIT_FILE = 0x02, - CLEAR_FILES = 0x03, - WRITE_FILE = 0x04, - READ_FILE = 0x05, - MODIFY_SECU = 0x06 -}; - -enum effect : uint32_t { - SAVANNA = 0x04970400, - SUNRISE = 0x01c10a00, - UNICORNS = 0x049a0e00, - PENSIEVE = 0x04c40600, - THE_CIRCUS = 0x04810130, - INSTASHARE = 0x03bc0190, - EIGTHIES = 0x049a0000, - CHERRY_BLOS = 0x04940800, - RAINBOW = 0x05bd0690, - TEST = 0x03af0af0, - CHRISTMAS = 0x068b0900 -}; - -enum verbs { - GET = 0x03, - SET = 0x04, - SETEEPROM = 0x07, - SETVAR = 0x0b -}; - -enum commandTypes { - STATE_OFF = 0x80, - STATE_ON = 0x81, - BRIGTHNESS = 0xCF, - SETCOLOR = 0xFF -}; - -enum idxTypes { - BRIGTHNESS_CONTROL = 0x01, - COLOR_CONTROL = 0x02, - COLOR_DIRECT_CONTROL = 0x81, - READ_INFO_FROM_STORAGE = 0x86 -}; - -enum bufferMode { - MONOCROME = 0x01, - LIGHTBEAD = 0x02, -}; - -enum ledLayout { - STRIP_LAYOUT, - MODLUE_LAYOUT -}; - -enum modelType { - STRIP, - PLUS -}; - -const uint8_t PACKET_HEADER[] = -{ - 'S', 'Z', // Tag "SZ" - 0x30, 0x30, // Version "00" - 0x00, 0x00, // AppID, 0x0000 = TL1 command mode - 0x00, 0x00, 0x00, 0x00 // Size -}; - -const uint8_t PACKET_SECU[] = -{ - 0x00, 0x00, 0x00, 0x00, // Dict - 0x00, 0x00, 0x00, 0x00, // Sum - 0x00, 0x00, 0x00, 0x00, // Salt - 0x00, 0x00, 0x00, 0x00 // SN -}; - -const uint8_t TL1_CMD_FIXED_PART[] = -{ - 0x00, 0x00, 0x00, 0x00, // DISTID - 0x00, 0x00, 0x00, 0x00, // SRCID - 0x00, // SECU - 0x00, // VERB - 0x00, // CTAG - 0x00 // LENGTH -}; - -/// -/// Implementation of a Cololight LedDevice -/// class LedDeviceCololight : public ProviderUdp { -public: + enum appID { + TL1_CMD = 0x00, + DIRECT_CONTROL = 0x01, + TRANSMIT_FILE = 0x02, + CLEAR_FILES = 0x03, + WRITE_FILE = 0x04, + READ_FILE = 0x05, + MODIFY_SECU = 0x06 + }; + + enum effect : uint32_t { + SAVANNA = 0x04970400, + SUNRISE = 0x01c10a00, + UNICORNS = 0x049a0e00, + PENSIEVE = 0x04c40600, + THE_CIRCUS = 0x04810130, + INSTASHARE = 0x03bc0190, + EIGTHIES = 0x049a0000, + CHERRY_BLOS = 0x04940800, + RAINBOW = 0x05bd0690, + TEST = 0x03af0af0, + CHRISTMAS = 0x068b0900 + }; + + enum verbs { + GET = 0x03, + SET = 0x04, + SETEEPROM = 0x07, + SETVAR = 0x0b + }; + + enum commandTypes { + STATE_OFF = 0x80, + STATE_ON = 0x81, + BRIGTHNESS = 0xCF, + SETCOLOR = 0xFF + }; + + enum idxTypes { + BRIGTHNESS_CONTROL = 0x01, + COLOR_CONTROL = 0x02, + COLOR_DIRECT_CONTROL = 0x81, + READ_INFO_FROM_STORAGE = 0x86 + }; + + enum bufferMode { + MONOCROME = 0x01, + LIGHTBEAD = 0x02, + }; + + enum ledLayout { + STRIP_LAYOUT, + MODLUE_LAYOUT + }; + + enum modelType { + STRIP, + PLUS + }; - /// - /// @brief Constructs a Cololight LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// +public: explicit LedDeviceCololight(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); - - /// - /// @brief Discover Cololight devices available (for configuration). - /// - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// QJsonObject discover(const QJsonObject& params) override; - - /// - /// @brief Get a Cololight device's resource properties - /// - /// Following parameters are required - /// @code - /// { - /// "host" : "hostname or IP", - /// } - ///@endcode - /// - /// @param[in] params Parameters to query device - /// @return A JSON structure holding the device's properties - /// QJsonObject getProperties(const QJsonObject& params) override; - - /// - /// @brief Send an update to the Cololight device to identify it. - /// - /// Following parameters are required - /// @code - /// { - /// "host" : "hostname or IP", - /// } - ///@endcode - /// - /// @param[in] params Parameters to address device - /// void identify(const QJsonObject& params) override; protected: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - - /// - /// @brief Power-/turn on the Cololight device. - /// - /// @return True if success - /// bool powerOn() override; - - /// - /// @brief Power-/turn off the Cololight device. - /// - /// @return True if success - /// bool powerOff() override; private: - bool initLedsConfiguration(); void initDirectColorCmdTemplate(); - - /// - /// @brief Read additional information from Cololight - /// - /// @return True if success - /// bool getInfo(); - - /// - /// @brief Set a Cololight effect - /// - /// @param[in] effect from effect list - /// - /// @return True if success - /// bool setEffect(const effect effect); - - /// - /// @brief Set a color - /// - /// @param[in] color in RGB - /// - /// @return True if success - /// bool setColor(const ColorRgb colorRgb); - - /// - /// @brief Set a color (or effect) - /// - /// @param[in] color in four bytes (red, green, blue, mode) - /// - /// @return True if success - /// bool setColor(const uint32_t color); - - /// - /// @brief Set colors per LED as per given list - /// - /// @param[in] list of color per LED - /// - /// @return True if success - /// bool setColor(const std::vector& ledValues); - - /// - /// @brief Set the Cololight device in TL1 command mode - /// - /// @param[in] isOn, Enable TL1 command mode = true - /// - /// @return True if success - /// bool setTL1CommandMode(bool isOn); - - /// - /// @brief Set the Cololight device's state (on/off) in TL1 mode - /// - /// @param[in] isOn, on=true - /// - /// @return True if success - /// bool setState(bool isOn); - - /// - /// @brief Set the Cololight device's state (on/off) in Direct Mode - /// - /// @param[in] isOn, on=true - /// - /// @return True if success - /// bool setStateDirect(bool isOn); - - /// - /// @brief Send a request to the Cololight device for execution - /// - /// @param[in] appID - /// @param[in] command - /// - /// @return True if success - /// bool sendRequest(const appID appID, const QByteArray& command); - - /// - /// @brief Read response for a send request - /// - /// @return True if success - /// bool readResponse(); - - /// - /// @brief Read response for a send request - /// - /// @param[out] response - /// - /// @return True if success - /// bool readResponse(QByteArray& response); - // Cololight model, e.g. CololightPlus, CololightStrip int _modelType; - - // Defines how Cololight LED are organised (multiple light beads in a module or individual lights on a strip int _ledLayoutType; - - // Count of overall LEDs across all modules int _ledBeadCount; - - // Distance (in #modules) of the module farest away from the main controller int _distance; QByteArray _packetFixPart; QByteArray _DataPart; - QByteArray _directColorCommandTemplate; - quint32 _sequenceNumber; - //Cololights discovered and their response message details QMultiMap> _services; }; - -#endif // LEDEVICECOLOLIGHT_H diff --git a/sources/leddevice/dev_net/LedDeviceFadeCandy.h b/sources/leddevice/dev_net/LedDeviceFadeCandy.h index 82f61e9a0..7395a23e3 100644 --- a/sources/leddevice/dev_net/LedDeviceFadeCandy.h +++ b/sources/leddevice/dev_net/LedDeviceFadeCandy.h @@ -1,132 +1,38 @@ -#ifndef LEDEVICEFADECANDY_H -#define LEDEVICEFADECANDY_H +#pragma once + +#ifndef PCH_ENABLED + #include + #include +#endif + +class QTcpSocket; -// STL/Qt includes -#include -#include -// LedDevice includes #include -/// -/// Implementation of the LedDevice interface for sending to -/// fadecandy/opc-server via network by using the 'open pixel control' protocol. -/// class LedDeviceFadeCandy : public LedDevice { Q_OBJECT public: - /// - /// @brief Constructs a LED-device for fadecandy/opc server - /// - /// Following code shows all configuration options - /// @code - /// "device" : - /// { - /// "name" : "MyPi", - /// "type" : "fadecandy", - /// "output" : "localhost", - /// "colorOrder" : "rgb", - /// "setFcConfig" : false, - /// "gamma" : 1.0, - /// "whitepoint" : [1.0, 1.0, 1.0], - /// "dither" : false, - /// "interpolation" : false, - /// "manualLed" : false, - /// "ledOn" : false - /// }, - ///@endcode - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceFadeCandy(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LedDevice - /// ~LedDeviceFadeCandy() override; - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); protected: - /// - /// @brief Initialise the Nanoleaf device's configuration and network address details - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; private: - - /// - /// @brief Initialise device's network details - /// - /// @return True if success bool initNetwork(); - - /// - /// @brief try to establish connection to opc server, if not connected yet - /// - /// @return True, if connection is established - /// bool tryConnect(); - - /// - /// @brief Return the connection state - /// - /// @return True, if connection established - /// bool isConnected() const; - - /// - /// @brief Transfer current opc_data buffer to opc server - /// - /// @return amount of transferred bytes. -1 error while transferring, -2 error while connecting - /// qint64 transferData(); - - /// - /// @brief Send system exclusive commands - /// - /// @param[in] systemId fadecandy device identifier (for standard fadecandy always: 1) - /// @param[in] commandId id of command - /// @param[in] msg the sysEx message - /// @return amount bytes written, -1 if failed qint64 sendSysEx(uint8_t systemId, uint8_t commandId, const QByteArray& msg); - - /// - /// @brief Sends the configuration to fadecandy cserver - /// void sendFadeCandyConfiguration(); QTcpSocket* _client; @@ -135,7 +41,6 @@ class LedDeviceFadeCandy : public LedDevice int _channel; QByteArray _opc_data; - // fadecandy sysEx bool _setFcConfig; double _gamma; double _whitePoint_r; @@ -146,5 +51,3 @@ class LedDeviceFadeCandy : public LedDevice bool _manualLED; bool _ledOnOff; }; - -#endif // LEDEVICEFADECANDY_H diff --git a/sources/leddevice/dev_net/LedDeviceNanoleaf.cpp b/sources/leddevice/dev_net/LedDeviceNanoleaf.cpp index 986750356..6473800a3 100644 --- a/sources/leddevice/dev_net/LedDeviceNanoleaf.cpp +++ b/sources/leddevice/dev_net/LedDeviceNanoleaf.cpp @@ -106,8 +106,6 @@ LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject& deviceConfig) LedDeviceNanoleaf::~LedDeviceNanoleaf() { - delete _restApi; - _restApi = nullptr; } LedDevice* LedDeviceNanoleaf::construct(const QJsonObject& deviceConfig) @@ -341,7 +339,7 @@ bool LedDeviceNanoleaf::initRestAPI(const QString& hostname, int port, const QSt if (_restApi == nullptr) { - _restApi = new ProviderRestApi(hostname, port); + _restApi = std::unique_ptr(new ProviderRestApi(hostname, port)); //Base-path is api-path + authentication token _restApi->setBasePath(QString(API_BASE_PATH).arg(token)); diff --git a/sources/leddevice/dev_net/LedDeviceNanoleaf.h b/sources/leddevice/dev_net/LedDeviceNanoleaf.h index a20932d1c..c626eab78 100644 --- a/sources/leddevice/dev_net/LedDeviceNanoleaf.h +++ b/sources/leddevice/dev_net/LedDeviceNanoleaf.h @@ -1,167 +1,40 @@ -#ifndef LEDEVICENANOLEAF_H -#define LEDEVICENANOLEAF_H +#pragma once + +#ifndef PCH_ENABLED + #include +#endif -// LedDevice includes #include #include "ProviderRestApi.h" #include "ProviderUdp.h" -// Qt includes -#include -/// -/// Implementation of the LedDevice interface for sending to -/// Nanoleaf devices via network by using the 'external control' protocol. -/// + class LedDeviceNanoleaf : public ProviderUdp { public: - /// - /// @brief Constructs LED-device for Nanoleaf LightPanels (aka Aurora) or Canvas - /// - /// following code shows all configuration options - /// @code - /// "device" : - /// { - /// "type" : "nanoleaf" - /// "host" : "hostname or IP", - /// "token": "Authentication Token", - /// }, - ///@endcode - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceNanoleaf(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LED-device - /// ~LedDeviceNanoleaf() override; - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); - - /// - /// @brief Discover Nanoleaf devices available (for configuration). - /// - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// QJsonObject discover(const QJsonObject& params) override; - - /// - /// @brief Get the Nanoleaf device's resource properties - /// - /// Following parameters are required - /// @code - /// { - /// "host" : "hostname or IP [:port]", - /// "token" : "authentication token", - /// "filter": "resource to query", root "/" is used, if empty - /// } - ///@endcode - /// - /// @param[in] params Parameters to query device - /// @return A JSON structure holding the device's properties - /// QJsonObject getProperties(const QJsonObject& params) override; - - /// - /// @brief Send an update to the Nanoleaf device to identify it. - /// - /// Following parameters are required - /// @code - /// { - /// "host" : "hostname or IP [:port]", - /// "token" : "authentication token", - /// } - ///@endcode - /// - /// @param[in] params Parameters to address device - /// void identify(const QJsonObject& params) override; protected: - - /// - /// @brief Initialise the Nanoleaf device's configuration and network address details - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - ////// int write(const std::vector& ledValues) override; - - /// - /// @brief Power-/turn on the Nanoleaf device. - /// - /// @brief Store the device's original state. - /// bool powerOn() override; - - /// - /// @brief Power-/turn off the Nanoleaf device. - /// - /// @return True if success - /// bool powerOff() override; private: - - /// - /// @brief Initialise the access to the REST-API wrapper - /// - /// @param[in] host - /// @param[in] port - /// @param[in] authentication token - /// - /// @return True, if success - /// bool initRestAPI(const QString& hostname, int port, const QString& token); - - /// - /// @brief Get Nanoleaf device details and configuration - /// - /// @return True, if Nanoleaf device capabilities fit configuration - /// bool initLedsConfiguration(); - - /// - /// @brief Change Nanoleaf device to External Control (UDP) mode - /// - /// @return Response from device - ///@brief QJsonDocument changeToExternalControlMode(); - - /// - /// @brief Get command to power 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; - ///REST-API wrapper - ProviderRestApi* _restApi; + std::unique_ptr _restApi; QString _hostname; int _apiPort; @@ -171,17 +44,13 @@ class LedDeviceNanoleaf : public ProviderUdp bool _leftRight; int _startPos; int _endPos; - - //Nanoleaf device details + QString _deviceModel; QString _deviceFirmwareVersion; ushort _extControlVersion; - /// The number of panels with LEDs int _panelLedCount; - /// Array of the panel ids. QVector _panelIds; }; -#endif // LEDEVICENANOLEAF_H diff --git a/sources/leddevice/dev_net/LedDevicePhilipsHue.cpp b/sources/leddevice/dev_net/LedDevicePhilipsHue.cpp index 35555d165..4bba7f238 100644 --- a/sources/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/sources/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -19,6 +19,16 @@ namespace { bool verbose = false; + const uint8_t HEADER[] = + { + 'H', 'u', 'e', 'S', 't', 'r', 'e', 'a', 'm', + 0x01, 0x00, + 0x01, + 0x00, 0x00, + 0x01, + 0x00 + }; + // Configuration settings const char CONFIG_ADDRESS[] = "output"; //const char CONFIG_PORT[] = "port"; @@ -94,15 +104,14 @@ namespace { const char API_SSL_SEED_CUSTOM[] = "dtls_client"; const int API_SSL_SERVER_PORT = 2100; const int STREAM_SSL_HANDSHAKE_ATTEMPTS = 5; - constexpr std::chrono::milliseconds STREAM_REFRESH_TIME{ 20 }; - const int SSL_CIPHERSUITES[2] = { MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, 0 }; + constexpr std::chrono::milliseconds STREAM_REFRESH_TIME{ 20 }; // V2 const char CONFIG_ENTERTAINMENT_CONFIGURATION_ID[] = "entertainmentConfigurationId"; const int API_DEFAULT_PORT_V2 = 443; const char API_CHANNELS_V2[] = "channels"; const char API_GROUPS_V2[] = "entertainment_configuration"; - const char API_BASE_PATH_V2[] = "/clip/v2/resource"; + const char API_RESOURCE_PATH_V2[] = "/clip/v2/resource"; const char API_HEADER_KEY_V2[] = "hue-application-key"; const char API_HEADER_ID_V2[] = "hue-application-id"; const char API_LIGHT_V2[] = "light"; @@ -293,7 +302,7 @@ bool LedDevicePhilipsHueBridge::init(const QJsonObject& deviceConfig) log("DeviceType", "%s", QSTRING_CSTR(this->getActiveDeviceType())); log("LedCount", "%d", this->getLedCount()); - log("RefreshTime", "%d", _refreshTimerInterval_ms); + log("RefreshTime", "%d", this->getRefreshTime()); //Set hostname as per configuration and_defaultHost default port QString address = deviceConfig[CONFIG_ADDRESS].toString(); @@ -326,12 +335,7 @@ bool LedDevicePhilipsHueBridge::init(const QJsonObject& deviceConfig) } else { - _currentRetry = _maxRetry; - if (!_retryMode && !_signalTerminate) - { - _retryMode = true; - QTimer::singleShot(1000, [this]() { _retryMode = false; if (_currentRetry > 0 && !_signalTerminate) enableDevice(true); }); - } + setupRetry(2000); } } } @@ -352,7 +356,7 @@ bool LedDevicePhilipsHueBridge::initRestAPI(const QString& hostname, int port, c if (_apiV2) { - _restApi->setBasePath(QString(API_BASE_PATH_V2) + "/"); + _restApi->setBasePath(""); _restApi->addHeader(API_HEADER_KEY_V2, token); } else @@ -404,6 +408,7 @@ bool LedDevicePhilipsHueBridge::checkApiError(const QJsonDocument& response, boo const int* LedDevicePhilipsHueBridge::getCiphersuites() const { + static const int SSL_CIPHERSUITES[2] = { get_MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256(), 0 }; return SSL_CIPHERSUITES; } @@ -425,7 +430,7 @@ void LedDevicePhilipsHueBridge::log(const char* msg, const char* type, ...) cons QJsonDocument LedDevicePhilipsHueBridge::getAllBridgeInfos() { if (_apiV2) - return get(API_BASE_PATH_V2); + return get(API_RESOURCE_PATH_V2); else return get(API_ROOT); } @@ -763,24 +768,25 @@ QJsonArray LedDevicePhilipsHueBridge::getGroupChannelsV2(QString groupId) const QJsonDocument LedDevicePhilipsHueBridge::getLightStateV2(QString lightId) { DebugIf(verbose, _log, "GetLightState [%s]", QSTRING_CSTR(lightId)); - return get(QString("%1/%2").arg(API_LIGHT_V2).arg(lightId)); + return get(QString("%1/%2/%3").arg(API_RESOURCE_PATH_V2).arg(API_LIGHT_V2).arg(lightId)); } void LedDevicePhilipsHueBridge::setLightStateV2(QString lightId, const QString& state) { DebugIf(verbose, _log, "SetLightState [%s]: %s", QSTRING_CSTR(lightId), QSTRING_CSTR(state)); - post(QString("%1/%2").arg(API_LIGHT_V2).arg(lightId), state); + post(QString("%1/%2/%3").arg(API_RESOURCE_PATH_V2).arg(API_LIGHT_V2).arg(lightId), state); } QJsonDocument LedDevicePhilipsHueBridge::getGroupStateV2(QString groupId) { DebugIf(verbose, _log, "GetGroupState [%s]", QSTRING_CSTR(groupId)); - return get(QString("%1/%2").arg(API_GROUPS_V2).arg(groupId)); + return get(QString("%1/%2/%3").arg(API_RESOURCE_PATH_V2).arg(API_GROUPS_V2).arg(groupId)); } QJsonDocument LedDevicePhilipsHueBridge::setGroupStateV2(QString groupId, bool state) { - return post(QString("%1/%2").arg(API_GROUPS_V2).arg(groupId), + DebugIf(verbose, _log, "SetGroupState [%s]: %s", QSTRING_CSTR(groupId), (state ? "on" : "off")); + return post(QString("%1/%2/%3").arg(API_RESOURCE_PATH_V2).arg(API_GROUPS_V2).arg(groupId), QString(R"({"action":"%1"})").arg(state ? "start" : "stop"), true); } @@ -815,13 +821,13 @@ QStringList LedDevicePhilipsHueBridge::getLightIdsInChannelV2(QJsonObject channe QJsonObject service = item.toObject()["service"].toObject(); if (service["rtype"].toString() == "entertainment") { - QJsonDocument jsonDocument = get(QString("%1/%2").arg("entertainment", service["rid"].toString())); + QJsonDocument jsonDocument = get(QString("%1/%2/%3").arg(API_RESOURCE_PATH_V2).arg("entertainment", service["rid"].toString())); QJsonObject entertainment = jsonDocument.object()["data"].toArray().first().toObject(); QString ownerType = entertainment["owner"].toObject()["rtype"].toString(); if (ownerType == "device") { QJsonDocument deviceDocument = get( - QString("%1/%2").arg("device", entertainment["owner"].toObject()["rid"].toString())); + QString("%1/%2/%3").arg(API_RESOURCE_PATH_V2).arg("device", entertainment["owner"].toObject()["rid"].toString())); QJsonObject device = deviceDocument.object()["data"].toArray().first().toObject(); for (const auto& item : device["services"].toArray()) { @@ -1283,7 +1289,7 @@ bool LedDevicePhilipsHue::initLeds(QString groupName) _devConfig["host"] = _hostname; _devConfig["sslport"] = API_SSL_SERVER_PORT; _devConfig["servername"] = API_SSL_SERVER_NAME; - _devConfig["refreshTime"] = static_cast(STREAM_REFRESH_TIME.count()); + _devConfig["forcedRefreshTime"] = static_cast(STREAM_REFRESH_TIME.count()); _devConfig["psk"] = _devConfig[CONFIG_CLIENTKEY].toString(); _devConfig["psk_identity"] = _devConfig[CONFIG_USERNAME].toString(); _devConfig["seed_custom"] = API_SSL_SEED_CUSTOM; @@ -1669,11 +1675,14 @@ bool LedDevicePhilipsHue::getStreamGroupState() return false; } -QByteArray LedDevicePhilipsHue::prepareStreamData() const +std::vector LedDevicePhilipsHue::prepareStreamData() const { - QByteArray msg; - msg.reserve(static_cast(sizeof(HEADER) + sizeof(PAYLOAD_PER_LIGHT) * _lights.size())); - msg.append(reinterpret_cast(HEADER), sizeof(HEADER)); + std::vector payload; + + payload.reserve(sizeof(HEADER) + 9 * _lights.size()); + + for (size_t i = 0; i < sizeof(HEADER); i++) + payload.push_back(HEADER[i]); for (const PhilipsHueLight& light : _lights) { @@ -1682,16 +1691,19 @@ QByteArray LedDevicePhilipsHue::prepareStreamData() const quint64 G = lightC.y * 0xffff; quint64 B = lightC.bri * 0xffff; unsigned int id = light.getId(); - const uint8_t payload[] = { - 0x00, 0x00, static_cast(id), - static_cast((R >> 8) & 0xff), static_cast(R & 0xff), - static_cast((G >> 8) & 0xff), static_cast(G & 0xff), - static_cast((B >> 8) & 0xff), static_cast(B & 0xff) - }; - msg.append(reinterpret_cast(payload), sizeof(payload)); + + payload.push_back(0x00); + payload.push_back(0x00); + payload.push_back(static_cast(id)); + payload.push_back(static_cast((R >> 8) & 0xff)); + payload.push_back(static_cast(R & 0xff)); + payload.push_back(static_cast((G >> 8) & 0xff)); + payload.push_back(static_cast(G & 0xff)); + payload.push_back(static_cast((B >> 8) & 0xff)); + payload.push_back(static_cast(B & 0xff)); } - return msg; + return payload; } int LedDevicePhilipsHue::close() @@ -1705,8 +1717,6 @@ int LedDevicePhilipsHue::close() void LedDevicePhilipsHue::stop() { - _currentRetry = 0; - LedDevicePhilipsHueBridge::stop(); } @@ -1724,17 +1734,12 @@ bool LedDevicePhilipsHue::switchOn() { bool rc = false; - if (_retryMode) - return false; - Info(_log, "Switching ON Philips Hue device"); try { if (_isOn) { - _currentRetry = 0; - Debug(_log, "Philips is already enabled. Skipping."); rc = true; } @@ -1743,7 +1748,6 @@ bool LedDevicePhilipsHue::switchOn() if (!_isDeviceInitialised && initMaps()) { init(_configBackup); - _currentRetry = 0; _isDeviceInitialised = true; } @@ -1768,27 +1772,10 @@ bool LedDevicePhilipsHue::switchOn() } } - if (!_isOn && _maxRetry > 0) + if (!_isOn) { - if (_currentRetry <= 0) - _currentRetry = _maxRetry + 1; - - _currentRetry--; - - if (_currentRetry > 0) - Warning(_log, "The PhilipsHue device is not ready... will try to reconnect (try %i/%i).", (_maxRetry - _currentRetry + 1), _maxRetry); - else - Error(_log, "The PhilipsHue device is not ready... give up."); - - - if (_currentRetry > 0 && !_signalTerminate) - { - _retryMode = true; - QTimer::singleShot(1000, [this]() { _retryMode = false; if (_currentRetry > 0 && !_signalTerminate) enableDevice(true); }); - } + setupRetry(2000); } - else - _currentRetry = 0; } } catch (...) @@ -1811,8 +1798,6 @@ bool LedDevicePhilipsHue::switchOff() Info(_log, "Switching OFF Philips Hue device"); - _currentRetry = 0; - try { if (_useHueEntertainmentAPI && _groupStreamState) @@ -1974,7 +1959,7 @@ void LedDevicePhilipsHue::writeStream(bool flush) return; } - QByteArray streamData = prepareStreamData(); + std::vector streamData = prepareStreamData(); writeBytes(static_cast(streamData.size()), reinterpret_cast(streamData.data()), flush); } @@ -1983,6 +1968,8 @@ std::vector LedDevicePhilipsHue::prepareStreamDataV2() QString entertainmentConfigId = _entertainmentConfigurationId; std::vector payload; + payload.reserve(16 + entertainmentConfigId.size() + 7 * _lights.size()); + payload.push_back(static_cast('H')); payload.push_back(static_cast('u')); payload.push_back(static_cast('e')); @@ -1999,28 +1986,28 @@ std::vector LedDevicePhilipsHue::prepareStreamDataV2() payload.push_back(static_cast(RESERVED)); payload.push_back(static_cast(COLORSPACE_XYBRI)); payload.push_back(static_cast(RESERVED)); + for (int i = 0; i < entertainmentConfigId.size(); ++i) { payload.push_back(static_cast(entertainmentConfigId.at(i).toLatin1())); } + for (auto light : _lights) { auto id = static_cast(light.getId() & 0x00ff); - CiColor lightXY = light.getColor(); - payload.push_back(id); + CiColor lightXY = light.getColor(); quint64 R = lightXY.x * 0xffff; quint64 G = lightXY.y * 0xffff; quint64 B = lightXY.bri * 0xffff; + payload.push_back(id); payload.push_back(static_cast((R >> 8) & 0xff)); payload.push_back(static_cast(R & 0xff)); payload.push_back(static_cast((G >> 8) & 0xff)); payload.push_back(static_cast(G & 0xff)); payload.push_back(static_cast((B >> 8) & 0xff)); payload.push_back(static_cast(B & 0xff)); - - } return payload; } @@ -2213,12 +2200,12 @@ QJsonObject LedDevicePhilipsHue::discover(const QJsonObject& /*params*/) devicesDiscovered.insert("ledDeviceType", _activeDeviceType); #ifdef ENABLE_BONJOUR - auto bonInstance = DiscoveryWrapper::getInstance(); + std::shared_ptr bonInstance = _discoveryWrapper.lock(); if (bonInstance != nullptr) { QList recs; - SAFE_CALL_0_RET(bonInstance, getPhilipsHUE, QList, recs); + SAFE_CALL_0_RET(bonInstance.get(), getPhilipsHUE, QList, recs); for (DiscoveryRecord& r : recs) { diff --git a/sources/leddevice/dev_net/LedDevicePhilipsHue.h b/sources/leddevice/dev_net/LedDevicePhilipsHue.h index 078cfecc8..91dc1dd06 100644 --- a/sources/leddevice/dev_net/LedDevicePhilipsHue.h +++ b/sources/leddevice/dev_net/LedDevicePhilipsHue.h @@ -1,150 +1,58 @@ #pragma once -// STL includes -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include -// Qt includes -#include + #include + #include + #include +#endif -// LedDevice includes #include #include "ProviderRestApi.h" #include "ProviderUdpSSL.h" -//Streaming message header and payload definition -const uint8_t HEADER[] = -{ - 'H', 'u', 'e', 'S', 't', 'r', 'e', 'a', 'm', //protocol - 0x01, 0x00, //version 1.0 - 0x01, //sequence number 1 - 0x00, 0x00, //Reserved write 0โ€™s - 0x01, //xy Brightness - 0x00, // Reserved, write 0โ€™s -}; - -const uint8_t PAYLOAD_PER_LIGHT[] = -{ - 0x01, 0x00, 0x06, //light ID - //color: 16 bpc - 0xff, 0xff, - 0xff, 0xff, - 0xff, 0xff, - /* -(message.R >> 8) & 0xff, message.R & 0xff, -(message.G >> 8) & 0xff, message.G & 0xff, -(message.B >> 8) & 0xff, message.B & 0xff -*/ -}; - -/** - * A XY color point in the color space of the hue system without brightness. - */ struct XYColor { - /// X component. double x; - /// Y component. double y; }; -/** - * Color triangle to define an available color space for the hue lamps. - */ struct CiColorTriangle { XYColor red, green, blue; }; -/** - * A color point in the color space of the hue system. - */ struct CiColor { - /// X component. double x; - /// Y component. double y; - /// The brightness. double bri; - /// /// Converts an RGB color to the Hue xy color space and brightness. /// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md - /// - /// @param red the red component in [0, 1] - /// - /// @param green the green component in [0, 1] - /// - /// @param blue the blue component in [0, 1] - /// - /// @return color point - /// static CiColor rgbToCiColor(double red, double green, double blue, const CiColorTriangle& colorSpace, bool candyGamma); - /// - /// @param p the color point to check - /// - /// @return true if the color point is covered by the lamp color space - /// static bool isPointInLampsReach(CiColor p, const CiColorTriangle& colorSpace); - - /// - /// @param p1 point one - /// - /// @param p2 point tow - /// - /// @return the cross product between p1 and p2 - /// static double crossProduct(XYColor p1, XYColor p2); - - /// - /// @param a reference point one - /// - /// @param b reference point two - /// - /// @param p the point to which the closest point is to be found - /// - /// @return the closest color point of p to a and b - /// static XYColor getClosestPointToPoint(XYColor a, XYColor b, CiColor p); - - /// - /// @param p1 point one - /// - /// @param p2 point tow - /// - /// @return the distance between the two points - /// static double getDistanceBetweenTwoPoints(CiColor p1, XYColor p2); }; bool operator==(const CiColor& p1, const CiColor& p2); bool operator!=(const CiColor& p1, const CiColor& p2); -/** - * Simple class to hold the id, the latest color, the color space and the original state. - */ class PhilipsHueLight { public: // Hue system model ids (http://www.developers.meethue.com/documentation/supported-lights). - // Light strips, color iris, ... static const std::set GAMUT_A_MODEL_IDS; - // Hue bulbs, spots, ... static const std::set GAMUT_B_MODEL_IDS; - // Hue Lightstrip plus, go ... static const std::set GAMUT_C_MODEL_IDS; - /// - /// Constructs the light. - /// - /// @param log the logger - /// @param bridge the bridge - /// @param id the light id - /// PhilipsHueLight(Logger* log, unsigned int id, QJsonObject values, unsigned int ledidx, int onBlackTimeToPowerOff, int onBlackTimeToPowerOn); @@ -152,19 +60,8 @@ class PhilipsHueLight unsigned int ledidx); ~PhilipsHueLight(); - /// - /// @param on - /// void setOnOffState(bool on); - - /// - /// @param transitionTime the transition time between colors in multiples of 100 ms - /// void setTransitionTime(int transitionTime); - - /// - /// @param color the color to set - /// void setColor(const CiColor& color); unsigned int getId() const; @@ -174,8 +71,6 @@ class PhilipsHueLight CiColor getColor() const; bool hasColor(); - /// - /// @return the color space of the light determined by the model id reported by the bridge. CiColorTriangle getColorSpace() const; void saveOriginalState(const QJsonObject& values); @@ -192,7 +87,6 @@ class PhilipsHueLight private: Logger* _log; - /// light id unsigned int _id; unsigned int _ledidx; bool _on; @@ -200,14 +94,11 @@ class PhilipsHueLight CiColor _color; ColorRgb _colorRgb; bool _hasColor; - /// darkes blue color in hue lamp GAMUT = black CiColor _colorBlack; - /// The model id of the hue lamp which is used to determine the color space. QString _modelId; QString _lightname; CiColorTriangle _colorSpace; - /// The json string of the original state. QJsonObject _originalStateJSON; QString _originalState; @@ -230,32 +121,8 @@ class LedDevicePhilipsHueBridge : public ProviderUdpSSL explicit LedDevicePhilipsHueBridge(const QJsonObject& deviceConfig); ~LedDevicePhilipsHueBridge() override; - /// - /// @brief Initialise the access to the REST-API wrapper - /// - /// @param[in] host - /// @param[in] port - /// @param[in] authentication token - /// - /// @return True, if success - /// bool initRestAPI(const QString& hostname, int port, const QString& token); - - /// - /// @brief Perform a REST-API GET - /// - /// @param route the route of the GET request. - /// - /// @return the content of the GET request. - /// QJsonDocument get(const QString& route); - - /// - /// @brief Perform a REST-API POST - /// - /// @param route the route of the POST request. - /// @param content the content of the POST request. - /// QJsonDocument post(const QString& route, const QString& content, bool supressError = false); QJsonDocument getLightState(unsigned int lightId); @@ -280,31 +147,13 @@ class LedDevicePhilipsHueBridge : public ProviderUdpSSL protected: - /// - /// @brief Initialise the Hue-Bridge configuration and network address details - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Check, if Hue API response indicate error - /// - /// @param[in] response from Hue-Bridge in JSON-format - /// return True, Hue Bridge reports error - /// bool checkApiError(const QJsonDocument& response, bool supressError = false); - ///REST-API wrapper ProviderRestApi* _restApi; - - /// Ip address of the bridge QString _hostname; - int _apiPort; - /// User name for the API ("newdeveloper") + int _apiPort; QString _username; - bool _useHueEntertainmentAPI; QJsonDocument getGroupState(unsigned int groupId); @@ -332,7 +181,6 @@ class LedDevicePhilipsHueBridge : public ProviderUdpSSL void setLightsMap(const QJsonDocument& doc); void setGroupMap(const QJsonDocument& doc); - //Philips Hue Bridge details QString _deviceModel; QString _deviceFirmwareVersion; QString _deviceAPIVersion; @@ -354,79 +202,17 @@ class LedDevicePhilipsHueBridge : public ProviderUdpSSL bool _apiV2; }; -/** - * Implementation for the Philips Hue system. - * - * To use set the device to "philipshue". - * Uses the official Philips Hue API (http://developers.meethue.com). - * - * @author ntim (github), bimsarck (github) - */ class LedDevicePhilipsHue : public LedDevicePhilipsHueBridge { Q_OBJECT public: - /// - /// @brief Constructs LED-device for Philips Hue Lights system - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDevicePhilipsHue(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LED-device - /// ~LedDevicePhilipsHue() override; - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); - - /// - /// @brief Discover devices of this type available (for configuration). - /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways. - /// - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// QJsonObject discover(const QJsonObject& params) override; - - /// - /// @brief Get the Hue Bridge device's resource properties - /// - /// Following parameters are required - /// @code - /// { - /// "host" : "hostname or IP [:port]", - /// "user" : "username", - /// "filter": "resource to query", root "/" is used, if empty - /// } - ///@endcode - /// - /// @param[in] params Parameters to query device - /// @return A JSON structure holding the device's properties - /// QJsonObject getProperties(const QJsonObject& params) override; - - /// - /// @brief Send an update to the device to identify it. - /// - /// Used in context of a set of devices of the same type. - /// - /// @param[in] params Parameters to address device - /// void identify(const QJsonObject& params) override; - - /// - /// @brief Get the number of LEDs supported by the device. - /// - /// @return Number of device's LEDs - /// unsigned int getLightsCount() const { return _lightsCount; } void setOnOffState(PhilipsHueLight& light, bool on, bool force = false); @@ -435,136 +221,26 @@ class LedDevicePhilipsHue : public LedDevicePhilipsHueBridge void setState(PhilipsHueLight& light, bool on, const CiColor& color); public slots: - - /// - /// @brief Stops the device. - /// - /// Includes switching-off the device and stopping refreshes. - /// void stop() override; protected: - - /// - /// Initialise the device's configuration - /// - /// @param deviceConfig Device's configuration in JSON - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - - /// - /// @brief Switch the LEDs on. - /// - /// Takes care that the device is opened and powered-on. - /// Depending on the configuration, the device may store its current state for later restore. - /// @see powerOn, storeState - /// - /// @return True if success - /// bool switchOn() override; - - /// - /// @brief Switch the LEDs off. - /// - /// Takes care that the LEDs and device are switched-off and device is closed. - /// Depending on the configuration, the device may be powered-off or restored to its previous state. - /// @see powerOff, restoreState - /// - /// @return True, if success - /// bool switchOff() override; - - bool switchOff(bool restoreState); - - /// - /// @brief Power-/turn on the LED-device. - /// - /// Powers-/Turns on the LED hardware, if supported. - /// - /// @return True, if success - /// bool powerOn() override; - - /// - /// @brief Power-/turn off the LED-device. - /// - /// Depending on the device's capability, the device is powered-/turned off or - /// an off state is simulated by writing "Black to LED" (default). - /// - /// @return True, if success - /// bool powerOff() override; - - /// - /// @brief Store the device's original state. - /// - /// Save the device's state before hyperhdr color streaming starts allowing to restore state during switchOff(). - /// - /// @return True if success - /// bool storeState() override; - - /// - /// @brief Restore the device's original state. - /// - /// Restore the device's state as before hyperhdr color streaming started. - /// This includes the on/off state of the device. - /// - /// @return True, if success - /// bool restoreState() override; - void colorChannel(const ColorRgb& colorRgb, unsigned int i); private: - bool initLeds(QString groupName); - bool powerOn(bool wait); - - /// - /// @brief Creates new PhilipsHueLight(s) based on user lightid with bridge feedback - /// - /// @param map Map of lightid/value pairs of bridge - /// - - bool setLights(); - - /// creates new PhilipsHueLight(s) based on user lightid with bridge feedback - /// - /// @param map Map of lightid/value pairs of bridge - /// bool updateLights(const QMap& map); - - /// - /// @brief Set the number of LEDs supported by the device. - /// - /// @rparam[in] Number of device's LEDs - // void setLightsCount(unsigned int lightsCount); bool openStream(); @@ -576,22 +252,15 @@ public slots: void writeStream(bool flush = false); int writeSingleLights(const std::vector& ledValues); - QByteArray prepareStreamData() const; + std::vector prepareStreamData() const; std::vector prepareStreamDataV2(); - /// bool _switchOffOnBlack; - /// The brightness factor to multiply on color change. double _brightnessFactor; - /// Transition time in multiples of 100 ms. - /// The default of the Hue lights is 400 ms, but we may want it snappier. int _transitionTime; - bool _isInitLeds; - /// Array of the light ids. std::vector _lightIds; - /// Array to save the lamps. std::vector _lights; unsigned int _lightsCount; @@ -607,7 +276,6 @@ public slots: uint32_t _handshake_timeout_min; uint32_t _handshake_timeout_max; - bool _stopConnection; QString _groupName; diff --git a/sources/leddevice/dev_net/LedDeviceTpm2net.h b/sources/leddevice/dev_net/LedDeviceTpm2net.h index afdd722dc..e120c59c8 100644 --- a/sources/leddevice/dev_net/LedDeviceTpm2net.h +++ b/sources/leddevice/dev_net/LedDeviceTpm2net.h @@ -1,52 +1,18 @@ -#ifndef LEDEVICETPM2NET_H -#define LEDEVICETPM2NET_H +#pragma once -// hyperhdr includes #include "ProviderUdp.h" -/// -/// Implementation of the LedDevice interface for sending LED colors via udp tpm2.net packets -/// class LedDeviceTpm2net : public ProviderUdp { public: - - /// - /// @brief Constructs a TPM2 LED-device fed via UDP - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceTpm2net(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; int _tpm2Max; int _tpm2ByteCount; int _tpm2TotalPackets; }; - -#endif // LEDEVICETPM2NET_H diff --git a/sources/leddevice/dev_net/LedDeviceUdpArtNet.cpp b/sources/leddevice/dev_net/LedDeviceUdpArtNet.cpp index b56565617..7ddaace10 100644 --- a/sources/leddevice/dev_net/LedDeviceUdpArtNet.cpp +++ b/sources/leddevice/dev_net/LedDeviceUdpArtNet.cpp @@ -1,4 +1,3 @@ -// hyperhdr local includes #include "LedDeviceUdpArtNet.h" #ifdef _WIN32 @@ -9,11 +8,43 @@ #include -const ushort ARTNET_DEFAULT_PORT = 6454; +/** + * + * This program is provided free for you to use in any way that you wish, + * subject to the laws and regulations where you are using it. Due diligence + * is strongly suggested before using this code. Please give credit where due. + * + **/ + +namespace { + const int DMX_MAX = 512; + const ushort ARTNET_DEFAULT_PORT = 6454; +} + +// http://stackoverflow.com/questions/16396013/artnet-packet-structure +union artnet_packet_t +{ +#pragma pack(push, 1) + struct { + char ID[8]; // "Art-Net" + uint16_t OpCode; // See Doc. Table 1 - OpCodes e.g. 0x5000 OpOutput / OpDmx + uint16_t ProtVer; // 0x0e00 (aka 14) + uint8_t Sequence; // monotonic counter + uint8_t Physical; // 0x00 + uint8_t SubUni; // low universe (0-255) + uint8_t Net; // high universe (not used) + uint16_t Length; // data length (2 - 512) + uint8_t Data[DMX_MAX]; // universe data + }; +#pragma pack(pop) + + uint8_t raw[18 + DMX_MAX]; +}; LedDeviceUdpArtNet::LedDeviceUdpArtNet(const QJsonObject& deviceConfig) : ProviderUdp(deviceConfig) { + artnet_packet = std::unique_ptr(new artnet_packet_t); } LedDevice* LedDeviceUdpArtNet::construct(const QJsonObject& deviceConfig) @@ -32,6 +63,7 @@ bool LedDeviceUdpArtNet::init(const QJsonObject& deviceConfig) { _artnet_universe = deviceConfig["universe"].toInt(1); _artnet_channelsPerFixture = deviceConfig["channelsPerFixture"].toInt(3); + _disableSplitting = deviceConfig["disableSplitting"].toBool(false); isInitOK = true; } @@ -48,15 +80,15 @@ void LedDeviceUdpArtNet::prepare(unsigned this_universe, unsigned this_sequence, this_dmxChannelCount++; } - memcpy(artnet_packet.ID, "Art-Net\0", 8); + memcpy(artnet_packet->ID, "Art-Net\0", 8); - artnet_packet.OpCode = htons(0x0050); // OpOutput / OpDmx - artnet_packet.ProtVer = htons(0x000e); - artnet_packet.Sequence = this_sequence; - artnet_packet.Physical = 0; - artnet_packet.SubUni = this_universe & 0xff; - artnet_packet.Net = (this_universe >> 8) & 0x7f; - artnet_packet.Length = htons(this_dmxChannelCount); + artnet_packet->OpCode = htons(0x0050); // OpOutput / OpDmx + artnet_packet->ProtVer = htons(0x000e); + artnet_packet->Sequence = this_sequence; + artnet_packet->Physical = 0; + artnet_packet->SubUni = this_universe & 0xff; + artnet_packet->Net = (this_universe >> 8) & 0x7f; + artnet_packet->Length = htons(this_dmxChannelCount); } int LedDeviceUdpArtNet::write(const std::vector& ledValues) @@ -76,23 +108,23 @@ int LedDeviceUdpArtNet::write(const std::vector& ledValues) int dmxIdx = 0; // offset into the current dmx packet - memset(artnet_packet.raw, 0, sizeof(artnet_packet.raw)); + memset(artnet_packet->raw, 0, sizeof(artnet_packet->raw)); for (unsigned int ledIdx = 0; ledIdx < _ledRGBCount; ledIdx++) { - artnet_packet.Data[dmxIdx++] = rawdata[ledIdx]; + artnet_packet->Data[dmxIdx++] = rawdata[ledIdx]; if ((ledIdx % 3 == 2) && (ledIdx > 0)) { dmxIdx += (_artnet_channelsPerFixture - 3); } // is this the last byte of last packet || last byte of other packets - if ((ledIdx == _ledRGBCount - 1) || (dmxIdx >= DMX_MAX)) + if ((ledIdx == _ledRGBCount - 1) || (dmxIdx >= DMX_MAX) || (_disableSplitting && dmxIdx + _artnet_channelsPerFixture > DMX_MAX)) { prepare(thisUniverse, _artnet_seq, dmxIdx); - retVal &= writeBytes(18 + qMin(dmxIdx, DMX_MAX), artnet_packet.raw); + retVal &= writeBytes(18 + qMin(dmxIdx, DMX_MAX), artnet_packet->raw); - memset(artnet_packet.raw, 0, sizeof(artnet_packet.raw)); + memset(artnet_packet->raw, 0, sizeof(artnet_packet->raw)); thisUniverse++; dmxIdx = 0; } diff --git a/sources/leddevice/dev_net/LedDeviceUdpArtNet.h b/sources/leddevice/dev_net/LedDeviceUdpArtNet.h index 14f5c9014..8b16669aa 100644 --- a/sources/leddevice/dev_net/LedDeviceUdpArtNet.h +++ b/sources/leddevice/dev_net/LedDeviceUdpArtNet.h @@ -1,91 +1,23 @@ -#ifndef LEDEVICEUDPARTNET_H -#define LEDEVICEUDPARTNET_H +#pragma once -// hyperhdr includes #include "ProviderUdp.h" -#include +union artnet_packet_t; -/** - * - * This program is provided free for you to use in any way that you wish, - * subject to the laws and regulations where you are using it. Due diligence - * is strongly suggested before using this code. Please give credit where due. - * - **/ - -const int DMX_MAX = 512; // 512 usable slots - -// http://stackoverflow.com/questions/16396013/artnet-packet-structure -typedef union -{ -#pragma pack(push, 1) - struct { - char ID[8]; // "Art-Net" - uint16_t OpCode; // See Doc. Table 1 - OpCodes e.g. 0x5000 OpOutput / OpDmx - uint16_t ProtVer; // 0x0e00 (aka 14) - uint8_t Sequence; // monotonic counter - uint8_t Physical; // 0x00 - uint8_t SubUni; // low universe (0-255) - uint8_t Net; // high universe (not used) - uint16_t Length; // data length (2 - 512) - uint8_t Data[DMX_MAX]; // universe data - }; -#pragma pack(pop) - - uint8_t raw[18 + DMX_MAX]; - -} artnet_packet_t; - -/// -/// Implementation of the LedDevice interface for sending LED colors to an Art-Net LED-device via UDP -/// class LedDeviceUdpArtNet : public ProviderUdp { public: - - /// - /// @brief Constructs an Art-Net LED-device fed via UDP - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceUdpArtNet(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - - /// - /// @brief Generate Art-Net communication header - /// void prepare(unsigned this_universe, unsigned this_sequence, unsigned this_dmxChannelCount); - artnet_packet_t artnet_packet; + std::unique_ptr artnet_packet; uint8_t _artnet_seq = 1; int _artnet_channelsPerFixture = 3; int _artnet_universe = 1; + bool _disableSplitting = false; }; - -#endif // LEDEVICEUDPARTNET_H diff --git a/sources/leddevice/dev_net/LedDeviceUdpE131.cpp b/sources/leddevice/dev_net/LedDeviceUdpE131.cpp index 3e95f9f48..5170cd76c 100644 --- a/sources/leddevice/dev_net/LedDeviceUdpE131.cpp +++ b/sources/leddevice/dev_net/LedDeviceUdpE131.cpp @@ -9,24 +9,54 @@ // hyperhdr local includes #include "LedDeviceUdpE131.h" -const ushort E131_DEFAULT_PORT = 5568; - -/* defined parameters from http://tsp.esta.org/tsp/documents/docs/BSR_E1-31-20xx_CP-2014-1009r2.pdf */ -const uint32_t VECTOR_ROOT_E131_DATA = 0x00000004; -//#define VECTOR_ROOT_E131_EXTENDED 0x00000008 -const uint8_t VECTOR_DMP_SET_PROPERTY = 0x02; -const uint32_t VECTOR_E131_DATA_PACKET = 0x00000002; -//#define VECTOR_E131_EXTENDED_SYNCHRONIZATION 0x00000001 -//#define VECTOR_E131_EXTENDED_DISCOVERY 0x00000002 -//#define VECTOR_UNIVERSE_DISCOVERY_UNIVERSE_LIST 0x00000001 -//#define E131_E131_UNIVERSE_DISCOVERY_INTERVAL 10 // seconds -//#define E131_NETWORK_DATA_LOSS_TIMEOUT 2500 // milli econds -//#define E131_DISCOVERY_UNIVERSE 64214 -const int DMX_MAX = 512; // 512 usable slots +union e131_packet_t +{ + #pragma pack(push, 1) + struct + { + uint16_t preamble_size; + uint16_t postamble_size; + uint8_t acn_id[12]; + uint16_t root_flength; + uint32_t root_vector; + char cid[16]; + + uint16_t frame_flength; + uint32_t frame_vector; + char source_name[64]; + uint8_t priority; + uint16_t reserved; + uint8_t sequence_number; + uint8_t options; + uint16_t universe; + + uint16_t dmp_flength; + uint8_t dmp_vector; + uint8_t type; + uint16_t first_address; + uint16_t address_increment; + uint16_t property_value_count; + uint8_t property_values[513]; + }; + #pragma pack(pop) + + uint8_t raw[638]; +}; + +namespace +{ + const unsigned int E131_DMP_DATA = 125; + const ushort E131_DEFAULT_PORT = 5568; + const uint32_t VECTOR_ROOT_E131_DATA = 0x00000004; + const uint8_t VECTOR_DMP_SET_PROPERTY = 0x02; + const uint32_t VECTOR_E131_DATA_PACKET = 0x00000002; + const int DMX_MAX = 512; +} LedDeviceUdpE131::LedDeviceUdpE131(const QJsonObject& deviceConfig) : ProviderUdp(deviceConfig) { + e131_packet = std::unique_ptr(new e131_packet_t); } LedDevice* LedDeviceUdpE131::construct(const QJsonObject& deviceConfig) @@ -73,36 +103,36 @@ bool LedDeviceUdpE131::init(const QJsonObject& deviceConfig) // populates the headers void LedDeviceUdpE131::prepare(unsigned this_universe, unsigned this_dmxChannelCount) { - memset(e131_packet.raw, 0, sizeof(e131_packet.raw)); + memset(e131_packet->raw, 0, sizeof(e131_packet->raw)); /* Root Layer */ - e131_packet.preamble_size = htons(16); - e131_packet.postamble_size = 0; - memcpy(e131_packet.acn_id, _acn_id, 12); - e131_packet.root_flength = htons(0x7000 | (110 + this_dmxChannelCount)); - e131_packet.root_vector = htonl(VECTOR_ROOT_E131_DATA); - memcpy(e131_packet.cid, _e131_cid.toRfc4122().constData(), sizeof(e131_packet.cid)); + e131_packet->preamble_size = htons(16); + e131_packet->postamble_size = 0; + memcpy(e131_packet->acn_id, _acn_id, 12); + e131_packet->root_flength = htons(0x7000 | (110 + this_dmxChannelCount)); + e131_packet->root_vector = htonl(VECTOR_ROOT_E131_DATA); + memcpy(e131_packet->cid, _e131_cid.toRfc4122().constData(), sizeof(e131_packet->cid)); /* Frame Layer */ - e131_packet.frame_flength = htons(0x7000 | (88 + this_dmxChannelCount)); - e131_packet.frame_vector = htonl(VECTOR_E131_DATA_PACKET); - snprintf(e131_packet.source_name, sizeof(e131_packet.source_name), "%s", QSTRING_CSTR(_e131_source_name)); - e131_packet.priority = 100; - e131_packet.reserved = htons(0); - e131_packet.options = 0; // Bit 7 = Preview_Data + e131_packet->frame_flength = htons(0x7000 | (88 + this_dmxChannelCount)); + e131_packet->frame_vector = htonl(VECTOR_E131_DATA_PACKET); + snprintf(e131_packet->source_name, sizeof(e131_packet->source_name), "%s", QSTRING_CSTR(_e131_source_name)); + e131_packet->priority = 100; + e131_packet->reserved = htons(0); + e131_packet->options = 0; // Bit 7 = Preview_Data // Bit 6 = Stream_Terminated // Bit 5 = Force_Synchronization - e131_packet.universe = htons(this_universe); + e131_packet->universe = htons(this_universe); /* DMX Layer */ - e131_packet.dmp_flength = htons(0x7000 | (11 + this_dmxChannelCount)); - e131_packet.dmp_vector = VECTOR_DMP_SET_PROPERTY; - e131_packet.type = 0xa1; - e131_packet.first_address = htons(0); - e131_packet.address_increment = htons(1); - e131_packet.property_value_count = htons(1 + this_dmxChannelCount); - - e131_packet.property_values[0] = 0; // start code + e131_packet->dmp_flength = htons(0x7000 | (11 + this_dmxChannelCount)); + e131_packet->dmp_vector = VECTOR_DMP_SET_PROPERTY; + e131_packet->type = 0xa1; + e131_packet->first_address = htons(0); + e131_packet->address_increment = htons(1); + e131_packet->property_value_count = htons(1 + this_dmxChannelCount); + + e131_packet->property_values[0] = 0; // start code } int LedDeviceUdpE131::write(const std::vector& ledValues) @@ -122,10 +152,10 @@ int LedDeviceUdpE131::write(const std::vector& ledValues) // is this the last packet? ? ^^ last packet : ^^ earlier packets prepare(_e131_universe + rawIdx / DMX_MAX, thisChannelCount); - e131_packet.sequence_number = _e131_seq; + e131_packet->sequence_number = _e131_seq; } - e131_packet.property_values[1 + rawIdx % DMX_MAX] = rawdata[rawIdx]; + e131_packet->property_values[1 + rawIdx % DMX_MAX] = rawdata[rawIdx]; // is this the last byte of last packet || last byte of other packets if ((rawIdx == dmxChannelCount - 1) || (rawIdx % DMX_MAX == DMX_MAX - 1)) @@ -139,7 +169,7 @@ int LedDeviceUdpE131::write(const std::vector& ledValues) , E131_DMP_DATA + 1 + thisChannelCount ); #endif - retVal &= writeBytes(E131_DMP_DATA + 1 + thisChannelCount, e131_packet.raw); + retVal &= writeBytes(E131_DMP_DATA + 1 + thisChannelCount, e131_packet->raw); } } diff --git a/sources/leddevice/dev_net/LedDeviceUdpE131.h b/sources/leddevice/dev_net/LedDeviceUdpE131.h index 07e1337d2..9583eeb2a 100644 --- a/sources/leddevice/dev_net/LedDeviceUdpE131.h +++ b/sources/leddevice/dev_net/LedDeviceUdpE131.h @@ -1,10 +1,10 @@ -#ifndef LEDEVICEUDPE131_H -#define LEDEVICEUDPE131_H +#pragma once -// hyperhdr includes -#include "ProviderUdp.h" +#ifndef PCH_ENABLED + #include +#endif -#include +#include "ProviderUdp.h" /** * @@ -19,120 +19,23 @@ * **/ - /* E1.31 Packet Offsets */ - //#define E131_ROOT_PREAMBLE_SIZE 0 - //#define E131_ROOT_POSTAMBLE_SIZE 2 - //#define E131_ROOT_ID 4 - //#define E131_ROOT_FLENGTH 16 - //#define E131_ROOT_VECTOR 18 - //#define E131_ROOT_CID 22 - - //#define E131_FRAME_FLENGTH 38 - //#define E131_FRAME_VECTOR 40 - //#define E131_FRAME_SOURCE 44 - //#define E131_FRAME_PRIORITY 108 - //#define E131_FRAME_RESERVED 109 - //#define E131_FRAME_SEQ 111 - //#define E131_FRAME_OPT 112 - //#define E131_FRAME_UNIVERSE 113 - - //#define E131_DMP_FLENGTH 115 - //#define E131_DMP_VECTOR 117 - //#define E131_DMP_TYPE 118 - //#define E131_DMP_ADDR_FIRST 119 - //#define E131_DMP_ADDR_INC 121 - //#define E131_DMP_COUNT 123 -const unsigned int E131_DMP_DATA = 125; - -/* E1.31 Packet Structure */ -typedef union -{ -#pragma pack(push, 1) - struct - { - /* Root Layer */ - uint16_t preamble_size; - uint16_t postamble_size; - uint8_t acn_id[12]; - uint16_t root_flength; - uint32_t root_vector; - char cid[16]; - - /* Frame Layer */ - uint16_t frame_flength; - uint32_t frame_vector; - char source_name[64]; - uint8_t priority; - uint16_t reserved; - uint8_t sequence_number; - uint8_t options; - uint16_t universe; - - /* DMP Layer */ - uint16_t dmp_flength; - uint8_t dmp_vector; - uint8_t type; - uint16_t first_address; - uint16_t address_increment; - uint16_t property_value_count; - uint8_t property_values[513]; - }; -#pragma pack(pop) +union e131_packet_t; - uint8_t raw[638]; -} e131_packet_t; - -/// -/// Implementation of the LedDevice interface for sending led colors via udp/E1.31 packets -/// class LedDeviceUdpE131 : public ProviderUdp { public: - - /// - /// @brief Constructs an E1.31 LED-device fed via UDP - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceUdpE131(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - - /// - /// @brief Generate E1.31 communication header - /// void prepare(unsigned this_universe, unsigned this_dmxChannelCount); - e131_packet_t e131_packet; + std::unique_ptr e131_packet; uint8_t _e131_seq = 0; uint8_t _e131_universe = 1; uint8_t _acn_id[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 }; QString _e131_source_name; QUuid _e131_cid; }; - -#endif // LEDEVICEUDPE131_H diff --git a/sources/leddevice/dev_net/LedDeviceUdpH801.h b/sources/leddevice/dev_net/LedDeviceUdpH801.h index afa911194..6eab95609 100644 --- a/sources/leddevice/dev_net/LedDeviceUdpH801.h +++ b/sources/leddevice/dev_net/LedDeviceUdpH801.h @@ -1,48 +1,16 @@ -#ifndef LEDEVICEUDPH801_H -#define LEDEVICEUDPH801_H +#pragma once -// hyperhdr includes #include "ProviderUdp.h" -/// -/// Implementation of the LedDevice interface for sending LED colors to a H801 LED-device via UDP -/// -/// class LedDeviceUdpH801 : public ProviderUdp { public: - - /// - /// @brief Constructs a H801 LED-device fed via UDP - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceUdpH801(const QJsonObject& deviceConfig); - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; QList _ids; @@ -51,7 +19,4 @@ class LedDeviceUdpH801 : public ProviderUdp const int _colors = 5; const int _id_size = 3; const int _suffix_size = 1; - }; - -#endif // LEDEVICEUDPH801_H diff --git a/sources/leddevice/dev_net/LedDeviceUdpRaw.h b/sources/leddevice/dev_net/LedDeviceUdpRaw.h index 2db00fa64..90f1b204b 100644 --- a/sources/leddevice/dev_net/LedDeviceUdpRaw.h +++ b/sources/leddevice/dev_net/LedDeviceUdpRaw.h @@ -1,48 +1,14 @@ -#ifndef LEDEVICEUDPRAW_H -#define LEDEVICEUDPRAW_H +#pragma once -// hyperhdr includes #include "ProviderUdp.h" -/// -/// Implementation of the LedDevice interface for sending LED colors via UDP -/// class LedDeviceUdpRaw : public ProviderUdp { public: - - /// - /// @brief Constructs a LED-device fed via UDP - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceUdpRaw(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); protected: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICEUDPRAW_H diff --git a/sources/leddevice/dev_net/LedDeviceWled.cpp b/sources/leddevice/dev_net/LedDeviceWled.cpp index 0364d8a2a..1031059a1 100644 --- a/sources/leddevice/dev_net/LedDeviceWled.cpp +++ b/sources/leddevice/dev_net/LedDeviceWled.cpp @@ -7,31 +7,11 @@ #include #endif -// Constants -namespace { - - // Configuration settings - const char CONFIG_ADDRESS[] = "host"; - - // UDP elements - const quint16 STREAM_DEFAULT_PORT = 19446; - - // WLED JSON-API elements - const int API_DEFAULT_PORT = -1; //Use default port per communication scheme - - const char API_BASE_PATH[] = "/json/"; - //const char API_PATH_INFO[] = "info"; - const char API_PATH_STATE[] = "state"; - - // List of State Information - const char STATE_VALUE_TRUE[] = "true"; - const char STATE_VALUE_FALSE[] = "false"; -} //End of constants - LedDeviceWled::LedDeviceWled(const QJsonObject& deviceConfig) : ProviderUdp(deviceConfig) , _restApi(nullptr) - , _apiPort(API_DEFAULT_PORT) + , _apiPort(80) + , _warlsStreamPort(21324) , _overrideBrightness(true) , _brightnessLevel(255) , _restoreConfig(false) @@ -78,7 +58,7 @@ bool LedDeviceWled::init(const QJsonObject& deviceConfig) Debug(_log, "Max retry : %d", _maxRetry); //Set hostname as per configuration - QString address = deviceConfig[CONFIG_ADDRESS].toString(); + QString address = deviceConfig["host"].toString(); //If host not configured the init fails if (address.isEmpty()) @@ -94,16 +74,12 @@ bool LedDeviceWled::init(const QJsonObject& deviceConfig) { _apiPort = addressparts[1].toInt(); } - else - { - _apiPort = API_DEFAULT_PORT; - } if (initRestAPI(_hostname, _apiPort)) { // Update configuration with hostname without port _devConfig["host"] = _hostname; - _devConfig["port"] = STREAM_DEFAULT_PORT; + _devConfig["port"] = _warlsStreamPort; isInitOK = ProviderUdp::init(_devConfig); Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostname)); @@ -123,7 +99,7 @@ bool LedDeviceWled::initRestAPI(const QString& hostname, int port) if (_restApi == nullptr) { _restApi = new ProviderRestApi(hostname, port); - _restApi->setBasePath(API_BASE_PATH); + _restApi->setBasePath("/json"); isInitOK = true; } @@ -140,7 +116,7 @@ QString LedDeviceWled::getOnOffRequest(bool isOn) const } else { - QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE; + QString state = isOn ? "true" : "false"; QString bri = (_overrideBrightness && isOn) ? QString(",\"bri\":%1").arg(_brightnessLevel) : ""; return QString("{\"on\":%1,\"live\":%1%2}").arg(state).arg(bri); } @@ -149,58 +125,74 @@ QString LedDeviceWled::getOnOffRequest(bool isOn) const bool LedDeviceWled::powerOn() { Debug(_log, ""); - bool on = false; - if (!_retryMode) - { - //Power-on WLED device - _restApi->setPath(API_PATH_STATE); + _restApi->setPath(""); + httpResponse response = _restApi->get(); - bool readConfig = _restoreConfig && _configBackup.isEmpty(); - httpResponse response = (readConfig) ? _restApi->get() : _restApi->put(getOnOffRequest(true)); + auto wledConfig = response.getBody(); + if (wledConfig.isEmpty()) + { + response.setError(true); + response.setErrorReason("Empty WLED config"); + } - if (readConfig && !response.error()) + if (!response.error()) + { + if (wledConfig.isObject()) { - _configBackup = response.getBody(); + QJsonObject mainConfig = wledConfig.object(); + QJsonObject infoConfig = mainConfig["info"].toObject(); + QJsonObject ledsConfig = infoConfig["leds"].toObject(); + QJsonObject stateConfig = mainConfig["state"].toObject(); + QJsonObject wifiConfig = infoConfig["wifi"].toObject(); - if (_configBackup.isObject()) + if (_restoreConfig) { - auto copy = _configBackup.object(); - copy["live"] = false; - _configBackup.setObject(copy); + stateConfig["live"] = false; + _configBackup.setObject(stateConfig); } - response = _restApi->put(getOnOffRequest(true)); - } + uint ledsNumber = ledsConfig["count"].toInt(0); + int powerLimiter = ledsConfig["maxpwr"].toInt(0); + int quality = wifiConfig["signal"].toInt(0); - if (response.error()) - { - this->setInError(response.getErrorReason()); + _warlsStreamPort = infoConfig["udpport"].toInt(_warlsStreamPort); - // power on simultaneously with Rpi causes timeout - if (_maxRetry > 0 && response.error()) - { - if (_currentRetry <= 0) - _currentRetry = _maxRetry + 1; + QString infoMessage = QString("WLED info => wifi quality: %1, wifi channel: %2, leds: %3, arch: %4, ver: %5, uptime: %6s, port: %7, power limit: %8mA") + .arg(QString::number(quality) + ((quality < 80) ? + "% (LOW)" : "%") ).arg(wifiConfig["channel"].toInt()).arg(ledsNumber) + .arg(infoConfig["arch"].toString()).arg(infoConfig["ver"].toString()) + .arg(infoConfig["uptime"].toInt()).arg(_warlsStreamPort).arg(powerLimiter); - _currentRetry--; + if (quality < 80 || powerLimiter > 0 || _ledCount != ledsNumber) + Warning(_log, "%s", QSTRING_CSTR(infoMessage)); + else + Info(_log, "%s", QSTRING_CSTR(infoMessage)); + + if (powerLimiter > 0) + Error(_log, "Serious warning: the power limiter in WLED is set which may lead to unexpected side effects. Use the right cabling & power supply with the appropriate power, not this half-measure."); - if (_currentRetry > 0) - Warning(_log, "The WLED device is not ready... will try to reconnect (try %i/%i).", (_maxRetry - _currentRetry + 1), _maxRetry); - else - Error(_log, "The WLED device is not ready... give up."); + if (_ledCount != ledsNumber) + Warning(_log, "The number of LEDs defined in HyperHDR (%i) is different from that defined in WLED (%i)", _ledCount, ledsNumber); + + _customInfo = QString(" %1%").arg(quality); - if (_currentRetry > 0 && !_signalTerminate) - { - _retryMode = true; - QTimer::singleShot(1000, [this]() { _retryMode = false; if (_currentRetry > 0 && !_signalTerminate) enableDevice(true); }); - } - } + ProviderUdp::setPort(_warlsStreamPort); } else - on = true; + Warning(_log, "Could not read WLED config"); + + _restApi->setPath("/state"); + response = _restApi->put(getOnOffRequest(true)); } - return on; + + if (response.error()) + { + this->setInError(response.error() ? response.getErrorReason() : "Empty WLED config"); + setupRetry(1500); + return false; + } + + return true; } bool LedDeviceWled::powerOff() @@ -208,8 +200,7 @@ bool LedDeviceWled::powerOff() Debug(_log, ""); bool off = true; - _currentRetry = 0; - _retryMode = false; + _customInfo = ""; if (_isDeviceReady) { @@ -217,7 +208,7 @@ bool LedDeviceWled::powerOff() writeBlack(); //Power-off the WLED device physically - _restApi->setPath(API_PATH_STATE); + _restApi->setPath("/state"); httpResponse response = _restApi->put(getOnOffRequest(false)); if (response.error()) { @@ -235,12 +226,12 @@ QJsonObject LedDeviceWled::discover(const QJsonObject& /*params*/) devicesDiscovered.insert("ledDeviceType", _activeDeviceType); #ifdef ENABLE_BONJOUR - auto bonInstance = DiscoveryWrapper::getInstance(); + std::shared_ptr bonInstance = _discoveryWrapper.lock(); if (bonInstance != nullptr) { QList recs; - SAFE_CALL_0_RET(bonInstance, getWLED, QList, recs); + SAFE_CALL_0_RET(bonInstance.get(), getWLED, QList, recs); for (DiscoveryRecord& r : recs) { @@ -260,54 +251,43 @@ QJsonObject LedDeviceWled::discover(const QJsonObject& /*params*/) return devicesDiscovered; } -QJsonObject LedDeviceWled::getProperties(const QJsonObject& params) +int LedDeviceWled::write(const std::vector& ledValues) { - Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); - QJsonObject properties; - - // Get Nanoleaf device properties - QString host = params["host"].toString(""); - if (!host.isEmpty()) + if (ledValues.size() != _ledCount) { - QString filter = params["filter"].toString(""); - - // Resolve hostname and port (or use default API port) - QStringList addressparts = QStringUtils::SPLITTER(host, ':'); - QString apiHost = addressparts[0]; - int apiPort; - - if (addressparts.size() > 1) - { - apiPort = addressparts[1].toInt(); - } - else - { - apiPort = API_DEFAULT_PORT; - } + setLedCount(static_cast(ledValues.size())); + return 0; + } + else if (ledValues.size() <= 490) + { + int wledSize = _ledRGBCount + 2; + std::vector wledData(wledSize, 0); + wledData[0] = 2; + wledData[1] = 255; + memcpy(wledData.data() + 2, ledValues.data(), _ledRGBCount); - initRestAPI(apiHost, apiPort); - _restApi->setPath(filter); + return writeBytes(wledSize, wledData.data()); + } + else + { + long long offset = 0; + const uint8_t* start = reinterpret_cast(ledValues.data()); + const uint8_t* end = reinterpret_cast(ledValues.data()) + _ledRGBCount; - httpResponse response = _restApi->get(); - if (response.error()) + while (start < end) { - Warning(_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason())); + auto realSize = std::min(static_cast(end - start), static_cast(489 * sizeof(ColorRgb))); + std::vector wledData(realSize + 4, 0); + wledData[0] = 4; + wledData[1] = 255; + wledData[2] = ((offset >> 8) & 0xff); + wledData[3] = (offset & 0xff); + memcpy(wledData.data() + 4, start, realSize); + start += realSize; + offset += realSize / sizeof(ColorRgb); + writeBytes(static_cast(wledData.size()), wledData.data()); } - properties.insert("properties", response.getBody().object()); - - Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData()); + return _ledRGBCount; } - return properties; -} - - -int LedDeviceWled::write(const std::vector& ledValues) -{ - const uint8_t* dataPtr = reinterpret_cast(ledValues.data()); - - if (ledValues.size() != _ledCount) - setLedCount(static_cast(ledValues.size())); - - return writeBytes(_ledRGBCount, dataPtr); } diff --git a/sources/leddevice/dev_net/LedDeviceWled.h b/sources/leddevice/dev_net/LedDeviceWled.h index 1d20a34cb..318e485d3 100644 --- a/sources/leddevice/dev_net/LedDeviceWled.h +++ b/sources/leddevice/dev_net/LedDeviceWled.h @@ -1,125 +1,36 @@ -#ifndef LEDDEVICEWLED_H -#define LEDDEVICEWLED_H +#pragma once -// LedDevice includes #include #include "ProviderRestApi.h" #include "ProviderUdp.h" -/// -/// Implementation of a WLED-device -/// ... -/// -/// class LedDeviceWled : public ProviderUdp { Q_OBJECT public: - /// - /// @brief Constructs a WLED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceWled(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the WLED-device - /// ~LedDeviceWled() override; - /// - /// @brief Constructs the WLED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); - - /// - /// @brief Discover WLED devices available (for configuration). - /// - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// QJsonObject discover(const QJsonObject& params) override; - /// - /// @brief Get the WLED device's resource properties - /// - /// Following parameters are required - /// @code - /// { - /// "host" : "hostname or IP [:port]", - /// "filter": "resource to query", root "/" is used, if empty - /// } - ///@endcode - /// - /// @param[in] params Parameters to query device - /// @return A JSON structure holding the device's properties - /// - QJsonObject getProperties(const QJsonObject& params) override; - protected: - - /// - /// @brief Initialise the WLED device's configuration and network address details - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - - /// - /// @brief Power-/turn on the WLED device. - /// - /// @return True if success - /// bool powerOn() override; - - /// - /// @brief Power-/turn off the WLED device. - /// - /// @return True if success - /// bool powerOff() override; private: - - /// - /// @brief Initialise the access to the REST-API wrapper - /// - /// @param[in] host - /// @param[in] port - /// @return True, if success - /// bool initRestAPI(const QString& hostname, int port); - - /// - /// @brief Get command to power WLED-device on or off - /// - /// @param isOn True, if to switch on device - /// @return Command to switch device on/off - /// QString getOnOffRequest(bool isOn) const; - - ///REST-API wrapper ProviderRestApi* _restApi; QString _hostname; int _apiPort; + int _warlsStreamPort; bool _overrideBrightness; int _brightnessLevel; bool _restoreConfig; QJsonDocument _configBackup; }; - -#endif // LEDDEVICEWLED_H diff --git a/sources/leddevice/dev_net/LedDeviceYeelight.cpp b/sources/leddevice/dev_net/LedDeviceYeelight.cpp index 284c38e43..127a65270 100644 --- a/sources/leddevice/dev_net/LedDeviceYeelight.cpp +++ b/sources/leddevice/dev_net/LedDeviceYeelight.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include diff --git a/sources/leddevice/dev_net/LedDeviceYeelight.h b/sources/leddevice/dev_net/LedDeviceYeelight.h index 9c29e2df1..533a2749e 100644 --- a/sources/leddevice/dev_net/LedDeviceYeelight.h +++ b/sources/leddevice/dev_net/LedDeviceYeelight.h @@ -1,21 +1,18 @@ -#ifndef LEDEVICEYEELIGHT_H -#define LEDEVICEYEELIGHT_H +#pragma once -// LedDevice includes -#include +#ifndef PCH_ENABLED + #include + #include + + #include +#endif -// Qt includes -#include -#include -#include -#include +#include -#include +class QTcpSocket; +class QTcpServer; -// Constants namespace { - - // List of State Information const char API_METHOD_POWER[] = "set_power"; const char API_METHOD_POWER_ON[] = "on"; const char API_METHOD_POWER_OFF[] = "off"; @@ -35,10 +32,8 @@ namespace { constexpr std::chrono::milliseconds API_PARAM_DURATION_POWERONOFF{ 1000 }; constexpr std::chrono::milliseconds API_PARAM_EXTRA_TIME_DARKNESS{ 200 }; -} //End of constants -/// -/// Response object for Yeelight-API calls and JSON-responses -/// +} + class YeelightResponse { public: @@ -70,9 +65,6 @@ class YeelightResponse QString _errorReason; }; -/// -/// Implementation of one Yeelight light. -/// class YeelightLight { @@ -91,293 +83,57 @@ class YeelightLight API_NIGHT_LIGHT_MODE }; - /// @brief Constructs one Yeelight light - /// - /// @param[in] log Logger instance - /// @param[in] hostname or IP-address - /// @param[in] port, default port 55443 is used when not provided - /// YeelightLight(Logger* log, const QString& hostname, quint16 port); - - /// - /// @brief Destructor of the Yeelight light - /// virtual ~YeelightLight(); - /// - /// @brief Set the Yeelight light connectivity parameters - /// - /// @param[in] hostname or IP-address - /// @param[in] port, default port 55443 is used when not provided - /// void setHostname(const QString& hostname, quint16 port); - - /// - /// @brief Set the Yeelight light name - /// - /// @param[in] name - /// void setName(const QString& name) { _name = name; } - - /// - /// @brief Get the Yeelight light name - /// - /// @return The Yeelight light name - /// QString getName() const { return _name; } - - /// - /// @brief Opens the Yeelight light connectivity - /// - /// @return True, on success (i.e. device is open) - /// bool open(); - - /// - /// @brief Closes the Yeelight light connectivity - /// - /// @return True, on success (i.e. device is closed) - /// bool close(); - - /// - /// @brief Send a command to light up Yeelight light to allow identification - /// - /// @return True, if success - /// bool identify(); - - /// - /// @brief Execute a Yeelight-API command - /// - /// @param[in] command The API command request in JSON - /// @return 0: success, -1: error, -2: command quota exceeded - /// int writeCommand(const QJsonDocument& command); - - /// - /// @brief Execute a Yeelight-API command - /// - /// @param[in] command The API command request in JSON - /// @param[out] result The response to the command in JSON - /// @return 0: success, -1: error, -2: command quota exceeded - /// int writeCommand(const QJsonDocument& command, QJsonArray& result); - - /// - /// @brief Stream a Yeelight-API command - /// - /// Yeelight must be in music mode, i.e. Streaming socket is established - /// - /// @param[in] command The API command request in JSON - /// @return True, on success - /// bool streamCommand(const QJsonDocument& command); - - /// - /// @brief Set the Yeelight light streaming socket - /// - /// @param[in] socket - /// void setStreamSocket(QTcpSocket* socket); - - /// - /// @brief Power on/off on the Yeelight light - /// - /// @param[in] on True: power on, False: power off - /// - /// @return True, if success - /// bool setPower(bool on); - - /// - /// @brief Power on/off on the Yeelight light - /// - /// @param[in] on True: power on, False: power off - /// @param[in] effect Transition effect, sudden or smooth - /// @param[in] duration Duration of the transition, if smooth - /// @param[in] mode Color mode after powering on - /// - /// @return True, if success - /// bool setPower(bool on, API_EFFECT effect, int duration, API_MODE mode = API_RGB_MODE); - - /// - /// @brief Set the Yeelight light to the given color (using RGB mode) - /// - /// @param[in] color as RGB value - /// - /// @return True, if success - /// bool setColorRGB(const ColorRgb& color); - - /// - /// @brief Set the Yeelight light to the given color (using HSV mode) - /// - /// @param[in] color as RGB value - /// - /// @return True, if success - /// bool setColorHSV(const ColorRgb& color); - - /// - /// @brief Set the Yeelight light effect and duration while transiting between color updates - /// - /// @param[in] effect Transition effect, sudden or smooth - /// @param[in] duration Duration of the transition, if smooth - /// void setTransitionEffect(API_EFFECT effect, int duration = API_PARAM_DURATION.count()); - - /// - /// @brief Set the Yeelight light brightness configuration behaviour - /// - /// @param[in] min Minimum Brightness (in %). Every value lower than minimum will be set to minimum. - /// @param[in] max Maximum Brightness (in %). Every value greater than maximum will be set to maximum. - /// @param[in] switchoff True, power-off light, if brightness is lower then minimum - /// @param[in] extraTime Additional time (in ms), which added to transition duration while powering-off - /// @param[in] factor Brightness factor to multiply on color change. - /// void setBrightnessConfig(int min = 1, int max = 100, bool switchoff = false, int extraTime = 0, double factor = 1); - - /// - /// @brief Set the Yeelight light into music-mode - /// - /// @param[in] on True: music-mode on, False: music-mode off - /// @param[in] hostAddress of the music-mode server - /// @param[in] port of the music-mode server - /// bool setMusicMode(bool on, const QHostAddress& hostAddress = {}, int port = -1); - - /// - /// @brief Set the wait-time between two Yeelight light commands - /// - /// The write of a command is delayed by the given wait-time, if the last write happen in the wait-time time frame. - /// Used to avoid that the Yeelight light runs into the quota exceed error scenario. - /// A Yeelight light can do 60 commands/min ( -> wait-time = 1000ms). - /// - /// @param[in] waitTime in milliseconds - /// void setQuotaWaitTime(int waitTime) { _waitTimeQuota = waitTime; } - - /// - /// @brief Get the Yeelight light properties - /// - /// @return properties as JSON-object - /// QJsonObject getProperties(); - - /// - /// @brief Get the Yeelight light properties and store them along the Yeelight light for later access - /// void storeState(); - - /// - /// @brief Restore the Yeelight light's original state. - /// - /// Restore the device's state as before hyperhdr color streaming started. - /// - /// @return True, if success - /// bool restoreState(); - - /// - /// @brief Check, if light was originally powered on before hyperhdr color streaming started.. - /// - /// @return True, if light was on at start - /// bool wasOriginallyOn() const { return _power == API_METHOD_POWER_ON ? true : false; } - - /// - /// @brief Check, if the Yeelight light is ready for updates - /// - /// @return True, if ready - /// bool isReady() const { return !_isInError; } - - /// - /// @brief Check, if the Yeelight light is powered on - /// - /// @return True, if powered on - /// bool isOn() const { return _isOn; } - - /// - /// @brief Check, if the Yeelight light is in music-mode - /// - /// @return True, if in music mode - /// bool isInMusicMode(bool deviceCheck = false); - - /// - /// @brief Set the Yeelight light in error state - /// - /// @param[in] errorMsg The error message to be logged - /// void setInError(const QString& errorMsg); - - /// - /// @brief Set the Yeelight light debug-level - /// - /// @param[in] level Debug level (0: no debug output, 1-3: verbosity level) - /// void setDebuglevel(int level) { _debugLevel = level; } private: YeelightResponse handleResponse(int correlationID, QByteArray const& response); - /// - /// @brief Build Yeelight-API command - /// - /// @param[in] method Control method to be invoked - /// @param[in] params Parameters for control method - /// @return Yeelight-API command in JSON format - /// QJsonDocument getCommand(const QString& method, const QJsonArray& params); - - /// - /// @brief Map Yeelight light properties into the Yeelight light members for direct access - /// - /// @param[in] properties Yeelight light's properties as JSON-Object - /// void mapProperties(const QJsonObject& properties); - - /// - /// @brief Write a Yeelight light specific log-line for debugging purposed - /// - /// @param[in] logLevel Debug level (0: no debug output, 1-3: verbosity level) - /// @param[in] msg Log message prefix (max 20 characters) - /// @param[in] type log message text - /// @param[in] ... variable input to log message text - /// /// void log(int logLevel, const char* msg, const char* type, ...); Logger* _log; int _debugLevel; - - /// Error status of Yeelight light bool _isInError; - - /// IP address/port of the Yeelight light QString _host; quint16 _port; - - /// Yeelight light communication socket QTcpSocket* _tcpSocket; - /// Music mode server communication socket QTcpSocket* _tcpStreamSocket; - - /// ID of last command written or streamed int _correlationID; - /// Timestamp of last write qint64 _lastWriteTime; - - /// Last color written to Yeelight light (RGB represented as QColor) QColor _color; - /// Last color written to Yeelight light (RGB represented as int) int _lastColorRgbValue; - /// Yeelight light behavioural parameters API_EFFECT _transitionEffect; int _transitionDuration; int _extraTimeDarkness; @@ -388,11 +144,7 @@ class YeelightLight double _brightnessFactor; QString _transitionEffectParam; - - /// Wait time to avoid quota exceed scenario int _waitTimeQuota; - - /// Yeelight light properties QJsonObject _originalStateProperties; QString _name; QString _model; @@ -401,140 +153,29 @@ class YeelightLight int _colorRgbValue; int _bright; int _ct; - - /// Yeelight light status bool _isOn; bool _isInMusicMode; }; -/// -/// Implementation of the LedDevice interface for sending to -/// Yeelight devices via network -/// class LedDeviceYeelight : public LedDevice { public: - - /// - /// @brief Constructs a Yeelight LED-device serving multiple lights - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceYeelight(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LedDevice - /// ~LedDeviceYeelight() override; - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); - - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// QJsonObject discover(const QJsonObject& params) override; - - /// - /// @brief Get a Yeelight device's resource properties - /// - /// Following parameters are required - /// @code - /// { - /// "hostname" : "hostname or IP", - /// "port" : port, default port 55443 is used when not provided - /// } - ///@endcode - /// - /// @param[in] params Parameters to query device - /// @return A JSON structure holding the device's properties - /// QJsonObject getProperties(const QJsonObject& params) override; - - /// - /// @brief Send an update to the Yeelight device to identify it. - /// - /// Following parameters are required - /// @code - /// { - /// "hostname" : "hostname or IP", - /// "port" : port, default port 55443 is used when not provided - /// } - ///@endcode - /// - /// @param[in] params Parameters to address device - /// void identify(const QJsonObject& params) override; protected: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - - /// - /// @brief Power-/turn on the Nanoleaf device. - /// - /// @brief Store the device's original state. - /// bool powerOn() override; - - /// - /// @brief Power-/turn off the Nanoleaf device. - /// - /// @return True if success - /// bool powerOff() override; - - /// - /// @brief Store the device's original state. - /// - /// Save the device's state before hyperhdr color streaming starts allowing to restore state during switchOff(). - /// - /// @return True if success - /// bool storeState() override; - - /// - /// @brief Restore the device's original state. - /// - /// Restore the device's state as before hyperhdr color streaming started. - /// This includes the on/off state of the device. - /// - /// @return True, if success - /// bool restoreState() override; private: @@ -554,51 +195,14 @@ class LedDeviceYeelight : public LedDevice MODEL_RGB }; - /// - /// @brief Start music-mode server - /// - /// @return True, if music mode server is running - /// bool startMusicModeServer(); - - /// - /// @brief Stop music-mode server - /// - /// @return True, if music mode server has been stopped - /// bool stopMusicModeServer(); - - /// - /// @brief Update list of Yeelight lights handled by the LED-device - /// - /// @param[in] list List of Yeelight lights - /// - /// @return False, if no lights were provided - /// bool updateLights(const QVector& list); - - /// - /// @brief Set the number of Yeelight lights handled by the LED-device - /// - /// @param[in] lightsCount Number of Yeelight lights - /// void setLightsCount(unsigned int lightsCount) { _lightsCount = lightsCount; } - - /// - /// @brief Get the number of Yeelight lights handled by the LED-device - /// - /// @return Number of Yeelight lights - /// uint getLightsCount() const { return _lightsCount; } - - /// Array of the Yeelight addresses handled by the LED-device QVector _lightsAddressList; - - /// Array to save the lights std::vector _lights; unsigned int _lightsCount; - - /// Yeelight configuration/behavioural parameters int _outputColorModel; YeelightLight::API_EFFECT _transitionEffect; int _transitionDuration; @@ -613,11 +217,8 @@ class LedDeviceYeelight : public LedDevice int _debuglevel; - ///Music mode Server details QHostAddress _musicModeServerAddress; int _musicModeServerPort; QTcpServer* _tcpMusicModeServer = nullptr; }; - -#endif // LEDEVICEYEELIGHT_H diff --git a/sources/leddevice/dev_net/ProviderRestApi.cpp b/sources/leddevice/dev_net/ProviderRestApi.cpp index 430a4dd82..09e6d0ee5 100644 --- a/sources/leddevice/dev_net/ProviderRestApi.cpp +++ b/sources/leddevice/dev_net/ProviderRestApi.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -27,6 +27,7 @@ // Local-HyperHDR includes #include "ProviderRestApi.h" +#include // Qt includes #include @@ -41,26 +42,22 @@ const int TIMEOUT = (500); -std::unique_ptr ProviderRestApi::_networkWorker(nullptr); - ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath) :_log(Logger::getInstance("LEDDEVICE")) , _scheme((port == 443) ? "https" : "http") , _hostname(host) , _port(port) { - if (_networkWorker == nullptr) - { - _networkWorker = std::unique_ptr(new networkHelper()); - } - qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); _apiUrl.setScheme(_scheme); _apiUrl.setHost(host); _apiUrl.setPort(port); _basePath = basePath; + + _workerThread = NetworkHelper::threadFactory(); } ProviderRestApi::ProviderRestApi(const QString& host, int port) @@ -71,6 +68,7 @@ ProviderRestApi::ProviderRestApi() ProviderRestApi::~ProviderRestApi() { + } void ProviderRestApi::updateHost(const QString& host, int port) @@ -81,24 +79,19 @@ void ProviderRestApi::updateHost(const QString& host, int port) void ProviderRestApi::setBasePath(const QString& basePath) { - _basePath.clear(); - appendPath(_basePath, basePath); + _basePath = basePath; } void ProviderRestApi::setPath(const QString& path) { - _path.clear(); - appendPath(_path, path); -} - -void ProviderRestApi::appendPath(const QString& path) -{ - appendPath(_path, path); + _path = path; } void ProviderRestApi::appendPath(QString& path, const QString& appendPath) const -{ - path = QUrl(path).resolved(appendPath).toString(); +{ + auto list = path.split('/', Qt::SkipEmptyParts); + list.append(appendPath.split('/', Qt::SkipEmptyParts)); + path = (list.size()) ? "/" + list.join('/') : ""; } void ProviderRestApi::addHeader(const QString &key, const QString &value) @@ -160,32 +153,14 @@ httpResponse ProviderRestApi::get() return get(getUrl()); } -bool ProviderRestApi::waitForResult(QNetworkReply* networkReply) -{ - bool networkTimeout = false; - - if (!networkReply->isFinished() && networkReply->error() == QNetworkReply::NoError) - { - if (!_resultLocker.try_lock_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(TIMEOUT))) - { - if (!networkReply->isFinished()) - { - networkTimeout = true; - disconnect(networkReply, &QNetworkReply::finished, nullptr, nullptr); - QTimer::singleShot(0, networkReply, &QNetworkReply::abort); - } - } - else - _resultLocker.unlock(); - } - - return networkTimeout; +const QMap& ProviderRestApi::getHeaders() { + return _headers; } httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation op, const QUrl& url, const QString& body) { + httpResponse response; qint64 begin = InternalClock::nowPrecise(); - QString opCode = (op == QNetworkAccessManager::PutOperation) ? "PUT" : (op == QNetworkAccessManager::PostOperation) ? "POST" : (op == QNetworkAccessManager::GetOperation) ? "GET" : ""; @@ -193,184 +168,193 @@ httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation if (opCode.length() == 0) { Error(_log, "Unsupported opertion code"); - return httpResponse(); + return response; } Debug(_log, "%s begin: [%s] [%s]", QSTRING_CSTR(opCode), QSTRING_CSTR(url.toString()), QSTRING_CSTR(body)); - // Perform request - QNetworkRequest request(url); - QNetworkReply* networkReply = nullptr; + NetworkHelper* networkHelper(new NetworkHelper()); - request.setOriginatingObject(this); - QMapIterator i(_headers); - while (i.hasNext()) - { - i.next(); - request.setRawHeader(i.key().toUtf8(), i.value().toUtf8()); - } + networkHelper->moveToThread(_workerThread.get()); - QSslConfiguration conf = request.sslConfiguration(); - conf.setPeerVerifyMode(QSslSocket::VerifyNone); - request.setSslConfiguration(conf); + _resultLocker.lock(); - SAFE_CALL_3_RET(_networkWorker.get(), executeOperation, - QNetworkReply*, networkReply, QNetworkAccessManager::Operation, op, QNetworkRequest, request, QByteArray, body.toUtf8()); + BLOCK_CALL_5(networkHelper, executeOperation, ProviderRestApi*, this, QNetworkAccessManager::Operation, op, QUrl, url, QString, body, httpResponse*, &response); + if (_resultLocker.try_lock_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(TIMEOUT))) + { + _resultLocker.unlock(); + } + else + { + QUEUE_CALL_0(networkHelper, abortOperation); + _resultLocker.lock(); + _resultLocker.unlock(); + } - bool networkTimeout = waitForResult(networkReply); - long long timeTotal = InternalClock::nowPrecise() - begin; + qint64 timeTotal = InternalClock::nowPrecise() - begin; - Debug(_log, "%s end (%lld ms): [%s] [%s]", QSTRING_CSTR(opCode), timeTotal, QSTRING_CSTR(url.toString()), QSTRING_CSTR(body)); + Debug(_log, "%s end (%lld ms): [%s] [%s]", QSTRING_CSTR(opCode), timeTotal, QSTRING_CSTR(url.toString()), QSTRING_CSTR(body)); - httpResponse response = (networkReply->operation() == op) ? getResponse(networkReply, networkTimeout) : httpResponse(); + if (response.error()) + Error(_log, "Reply error. Reason: %s", QSTRING_CSTR(response.getErrorReason())); + else + Debug(_log, "Reply OK [%d]", response.getHttpStatusCode()); - networkReply->deleteLater(); + networkHelper->deleteLater(); return response; } - -void ProviderRestApi::aquireResultLock() -{ - _resultLocker.tryLock(); -} - void ProviderRestApi::releaseResultLock() { _resultLocker.unlock(); } -httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply, bool timeout) +std::shared_ptr NetworkHelper::threadFactory() { - httpResponse response; - - int httpStatusCode = (timeout) ? 408 : reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - response.setHttpStatusCode(httpStatusCode); - - QMap headers; - // We sometimes need headers in the response to get the hue application id for instance - for (const auto &item: reply->rawHeaderPairs()) - { - headers[item.first] = item.second; - }; - response.setHeaders(headers); - - if (timeout) - response.setNetworkReplyError(QNetworkReply::TimeoutError); - else - response.setNetworkReplyError(reply->error()); + static QMutex locker; + static std::weak_ptr persist; + QMutexLocker lockThis(&locker); - if (reply->error() == QNetworkReply::NoError) + auto result = persist.lock(); + if (!result) { - if (httpStatusCode != 204) { - QByteArray replyData = reply->readAll(); - - if (!replyData.isEmpty()) - { - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(replyData, &error); - - if (error.error != QJsonParseError::NoError) - { - //Received not valid JSON response - //std::cout << "Response: [" << replyData.toStdString() << "]" << std::endl; - response.setError(true); - response.setErrorReason(error.errorString()); - } - else - { - //std::cout << "Response: [" << QString (jsonDoc.toJson(QJsonDocument::Compact)).toStdString() << "]" << std::endl; - response.setBody(jsonDoc); - } - } - else - { // Create valid body which is empty - response.setBody(QJsonDocument()); - } - } + result = std::shared_ptr(new QThread(), [](QThread* thread) { + hyperhdr::THREAD_REMOVER(QString("NetworkHelperThread"), thread, nullptr); + }); + result->start(); + persist = result; } - 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; - case 408: - advise = "Check if target IP is valid and your network status"; - httpReason = "Timeout"; - break; - default: - break; - } - errorReason = QString("[%3 %4] - %5").arg(QString::number(httpStatusCode), httpReason, advise); - } - else { - errorReason = reply->errorString(); - } - response.setError(true); - response.setErrorReason(errorReason); + return result; +} - // Create valid body which is empty - response.setBody(QJsonDocument()); +void NetworkHelper::abortOperation() +{ + if (_networkReply != nullptr) + { + _timeout = true; + _networkReply->abort(); } +} - if (response.error()) - Error(_log, "Reply.httpStatusCode %s", QSTRING_CSTR(response.getErrorReason())); - else - Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode); +NetworkHelper::NetworkHelper() : + _networkManager(nullptr), + _networkReply(nullptr), + _timeout(false) +{ +} - return response; +NetworkHelper::~NetworkHelper() +{ + delete _networkReply; + delete _networkManager; } -networkHelper::networkHelper() +void NetworkHelper::executeOperation(ProviderRestApi* parent, QNetworkAccessManager::Operation op, QUrl url, QString body, httpResponse* response) { - QThread* parent = new QThread(); - parent->setObjectName("RestApiThread"); + QNetworkRequest request(url); + + QMapIterator i = parent->getHeaders(); + while (i.hasNext()) + { + i.next(); + request.setRawHeader(i.key().toUtf8(), i.value().toUtf8()); + } _networkManager = new QNetworkAccessManager(); - _networkManager->moveToThread(parent); - this->moveToThread(parent); - parent->start(); -} + connect(_networkManager, &QNetworkAccessManager::sslErrors, this, [](QNetworkReply* reply, const QList& errors) { + reply->ignoreSslErrors(errors); + }); -networkHelper::~networkHelper() -{ - // get current thread - QThread* oldThread = _networkManager->thread(); - disconnect(oldThread, nullptr, nullptr, nullptr); - oldThread->quit(); - oldThread->wait(); - delete oldThread; + _networkReply = (op == QNetworkAccessManager::PutOperation) ? _networkManager->put(request, body.toUtf8()) : + (op == QNetworkAccessManager::PostOperation) ? _networkManager->post(request, body.toUtf8()) : _networkManager->get(request); - disconnect(_networkManager, nullptr, nullptr, nullptr); - delete _networkManager; - _networkManager = nullptr; + connect(_networkReply, &QNetworkReply::finished, this, [=]() {getResponse(response); parent->releaseResultLock(); }, Qt::DirectConnection); } -QNetworkReply* networkHelper::executeOperation(QNetworkAccessManager::Operation op, QNetworkRequest request, QByteArray body) +void NetworkHelper::getResponse(httpResponse* response) { - ProviderRestApi* parent = static_cast(request.originatingObject()); - parent->aquireResultLock(); + if (_networkReply != nullptr) + { + int httpStatusCode = (_timeout) ? 408 : _networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + response->setHttpStatusCode(httpStatusCode); - auto ret = (op == QNetworkAccessManager::PutOperation) ? _networkManager->put(request, body): - (op == QNetworkAccessManager::PostOperation) ? _networkManager->post(request, body) : _networkManager->get(request); + QMap headers; + for (const auto& item: _networkReply->rawHeaderPairs()) + { + headers[item.first] = item.second; + } + response->setHeaders(headers); - connect(ret, &QNetworkReply::finished, this, [=](){parent->releaseResultLock();}); - return ret; + if (_timeout) + response->setNetworkReplyError(QNetworkReply::TimeoutError); + else + response->setNetworkReplyError(_networkReply->error()); + + if (_networkReply->error() == QNetworkReply::NoError) + { + if (httpStatusCode != 204) { + QByteArray replyData = _networkReply->readAll(); + + if (!replyData.isEmpty()) + { + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(replyData, &error); + + if (error.error != QJsonParseError::NoError) + { + response->setError(true); + response->setErrorReason(error.errorString()); + } + else + { + response->setBody(jsonDoc); + } + } + } + } + else + { + if (httpStatusCode > 0) + { + QString httpReason = _networkReply->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; + case 408: + advise = "Check the status of your network and whether the destination IP address is correct"; + httpReason = "Timeout"; + break; + default: + break; + } + response->setErrorReason(QString("[%3 %4] - %5").arg(QString::number(httpStatusCode), httpReason, advise)); + } + else + { + response->setErrorReason(_networkReply->errorString()); + } + response->setError(true); + } + + // clean up + _networkReply->deleteLater(); + _networkReply = nullptr; + _networkManager->deleteLater(); + _networkManager = nullptr; + } } bool httpResponse::error() const diff --git a/sources/leddevice/dev_net/ProviderRestApi.h b/sources/leddevice/dev_net/ProviderRestApi.h index 5a3a3e775..6f9ae0f38 100644 --- a/sources/leddevice/dev_net/ProviderRestApi.h +++ b/sources/leddevice/dev_net/ProviderRestApi.h @@ -1,138 +1,55 @@ -#ifndef PROVIDERRESTKAPI_H -#define PROVIDERRESTKAPI_H +#pragma once -// Local-HyperHDR includes -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include + #include + + #include +#endif -// Qt includes -#include #include -#include -#include -#include -#include class httpResponse; -class networkHelper; +class NetworkHelper; +class QNetworkReply; class ProviderRestApi : public QObject { Q_OBJECT public: - - /// - /// @brief Constructor of the REST-API wrapper - /// ProviderRestApi(); - - /// - /// @brief Constructor of the REST-API wrapper - /// - /// @param[in] host - /// @param[in] port - /// ProviderRestApi(const QString& host, int port); - - /// - /// @brief Constructor of the REST-API wrapper - /// - /// @param[in] host - /// @param[in] port - /// @param[in] API base-path - /// ProviderRestApi(const QString& host, int port, const QString& basePath); - ~ProviderRestApi(); void updateHost(const QString& host, int port); - - /// - /// @brief Get the URL as defined using scheme, host, port, API-basepath, path, query, fragment - /// - /// @return url - /// QUrl getUrl() const; - - /// - /// @brief Set an API's base path (the stable path element before addressing resources) - /// - /// @param[in] basePath, e.g. "/api/v1/" or "/json" - /// void setBasePath(const QString& basePath); - - /// - /// @brief Set an API's path to address resources - /// - /// @param[in] path, e.g. "/lights/1/state/" - /// void setPath(const QString& path); - - /// - /// @brief Append an API's path element to path set before - /// - /// @param[in] path - /// - void appendPath(const QString& appendPath); void addHeader(const QString &key, const QString &value); - - /// - /// @brief Set an API's fragment - /// - /// @param[in] fragment, e.g. "question3" - /// + const QMap& getHeaders(); void setFragment(const QString& fragment); - - /// - /// @brief Set an API's query string - /// - /// @param[in] query, e.g. "&A=128&FX=0" - /// void setQuery(const QUrlQuery& query); - /// - /// @brief Execute GET request - /// - /// @return Response The body of the response in JSON - /// httpResponse get(); - httpResponse put(const QString& body = ""); - httpResponse post(const QString& body); - httpResponse get(const QUrl& url); - httpResponse put(const QUrl& url, const QString& body = ""); - httpResponse post(const QUrl& url, const QString& body); - - /// - /// @brief Handle responses for REST requests - /// - /// @param[in] reply Network reply - /// @return Response The body of the response in JSON - /// - httpResponse getResponse(QNetworkReply* const& reply, bool timeout); - - void aquireResultLock(); void releaseResultLock(); private: - - /// - /// @brief Append an API's path element to path given as param - /// - /// @param[in/out] path to be updated - /// @param[in] path, element to be appended - /// void appendPath(QString& path, const QString& appendPath) const; - httpResponse executeOperation(QNetworkAccessManager::Operation op, const QUrl& url, const QString& body = ""); - bool waitForResult(QNetworkReply* networkReply); - - Logger* _log; + Logger* _log; QUrl _apiUrl; @@ -148,28 +65,31 @@ class ProviderRestApi : public QObject QMutex _resultLocker; - static std::unique_ptr _networkWorker; + std::shared_ptr _workerThread; }; -class networkHelper : public QObject +class NetworkHelper : public QObject { Q_OBJECT -public: QNetworkAccessManager* _networkManager; + QNetworkReply* _networkReply; + bool _timeout; + + void getResponse(httpResponse* response); - networkHelper(); +public: + NetworkHelper(); + ~NetworkHelper(); - ~networkHelper(); + static std::shared_ptr threadFactory(); public slots: - QNetworkReply* executeOperation(QNetworkAccessManager::Operation op, QNetworkRequest request, QByteArray body); + void executeOperation(ProviderRestApi* parent, QNetworkAccessManager::Operation op, QUrl url, QString body, httpResponse* response); + void abortOperation(); }; -/// -/// Response object for REST-API calls and JSON-responses -/// class httpResponse { public: @@ -202,6 +122,3 @@ class httpResponse int _httpStatusCode = 0; QNetworkReply::NetworkError _networkReplyError = QNetworkReply::NoError; }; - - -#endif // PROVIDERRESTKAPI_H diff --git a/sources/leddevice/dev_net/ProviderUdp.cpp b/sources/leddevice/dev_net/ProviderUdp.cpp index 2bf22e0b0..cd76da796 100644 --- a/sources/leddevice/dev_net/ProviderUdp.cpp +++ b/sources/leddevice/dev_net/ProviderUdp.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -40,8 +40,6 @@ // Local HyperHDR includes #include "ProviderUdp.h" -const ushort MAX_PORT = 65535; - ProviderUdp::ProviderUdp(const QJsonObject& deviceConfig) : LedDevice(deviceConfig) , _udpSocket(nullptr) @@ -50,10 +48,7 @@ ProviderUdp::ProviderUdp(const QJsonObject& deviceConfig) { } -ProviderUdp::~ProviderUdp() -{ - delete _udpSocket; -} +ProviderUdp::~ProviderUdp() = default; bool ProviderUdp::init(const QJsonObject& deviceConfig) { @@ -87,7 +82,7 @@ bool ProviderUdp::init(const QJsonObject& deviceConfig) if (!_isDeviceInError) { int config_port = deviceConfig["port"].toInt(_port); - if (config_port <= 0 || config_port > MAX_PORT) + if (config_port <= 0 || config_port > 0xffff) { QString errortext = QString("Invalid target port [%1]!").arg(config_port); this->setInError(errortext); @@ -98,7 +93,7 @@ bool ProviderUdp::init(const QJsonObject& deviceConfig) _port = static_cast(config_port); Debug(_log, "UDP socket will write to %s:%u", QSTRING_CSTR(_address.toString()), _port); - _udpSocket = new QUdpSocket(this); + _udpSocket = std::unique_ptr(new QUdpSocket()); isInitOK = true; } @@ -179,3 +174,12 @@ int ProviderUdp::writeBytes(const QByteArray& bytes) } return rc; } + +void ProviderUdp::setPort(int port) +{ + if (port > 0 && port <= 0xffff && _port != port) + { + _port = port; + Debug(_log, "Updated port to: %i", _port); + } +} diff --git a/sources/leddevice/dev_net/ProviderUdp.h b/sources/leddevice/dev_net/ProviderUdp.h index 39d7e68cc..c54f63081 100644 --- a/sources/leddevice/dev_net/ProviderUdp.h +++ b/sources/leddevice/dev_net/ProviderUdp.h @@ -1,83 +1,33 @@ -#ifndef PROVIDERUDP_H -#define PROVIDERUDP_H +#pragma once -// LedDevice includes -#include +#ifndef PCH_ENABLED + #include +#endif -// HyperHDR includes +#include #include -// Qt includes -#include -#include +class QUdpSocket; -/// -/// The ProviderUdp implements an abstract base-class for LedDevices using UDP packets. -/// class ProviderUdp : public LedDevice { public: - - /// - /// @brief Constructs an UDP LED-device - /// ProviderUdp(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the UDP LED-device - /// ~ProviderUdp() override; QHostAddress getAddress() const { return _address; } protected: - /// - /// @brief Initialise the UDP device's configuration and network address details - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the UDP device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the given bytes to the UDP-device - /// - /// @param[in] size The length of the data - /// @param[in] data The data - /// - /// @return Zero on success, else negative - /// int writeBytes(const unsigned size, const uint8_t* data); - - /// - /// @brief Writes the given bytes to the UDP-device - /// - /// @param[in] data The data - /// - /// @return Zero on success, else negative - /// int writeBytes(const QByteArray& bytes); + void setPort(int port); - /// - QUdpSocket* _udpSocket; + std::unique_ptr _udpSocket; QHostAddress _address; quint16 _port; QString _defaultHost; }; - -#endif // PROVIDERUDP_H diff --git a/sources/leddevice/dev_net/ProviderUdpSSL.cpp b/sources/leddevice/dev_net/ProviderUdpSSL.cpp index 1ccf95518..fd8cca7b1 100644 --- a/sources/leddevice/dev_net/ProviderUdpSSL.cpp +++ b/sources/leddevice/dev_net/ProviderUdpSSL.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,6 +25,7 @@ * SOFTWARE. */ + // STL includes #include #include @@ -33,27 +34,45 @@ // Linux includes #include #ifndef _WIN32 -#include + #include #endif +#include #include #include -// Local HyperHDR includes #include "ProviderUdpSSL.h" +#if !defined(MBEDTLS_OLD_CONFIG_FILE) + #include +#else + #include MBEDTLS_OLD_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PLATFORM_C) + #include +#endif + +#include +#include +#include +#include +#include +#include +#include + const int MAX_RETRY = 20; const ushort MAX_PORT_SSL = 65535; ProviderUdpSSL::ProviderUdpSSL(const QJsonObject& deviceConfig) : LedDevice(deviceConfig) - , client_fd() - , entropy() - , ssl() - , conf() - , cacert() - , ctr_drbg() - , timer() + , client_fd(new mbedtls_net_context()) + , entropy(nullptr) + , ssl(new mbedtls_ssl_context()) + , conf(new mbedtls_ssl_config()) + , cacert(new mbedtls_x509_crt()) + , ctr_drbg(new mbedtls_ctr_drbg_context()) + , timer(new mbedtls_timing_delay_context()) , _transport_type("DTLS") , _custom("dtls_client") , _address("127.0.0.1") @@ -70,29 +89,25 @@ ProviderUdpSSL::ProviderUdpSSL(const QJsonObject& deviceConfig) , _handshake_timeout_min(300) , _handshake_timeout_max(1000) { - - bool error = false; - - try - { - mbedtls_ctr_drbg_init(&ctr_drbg); - error = !seedingRNG(); - } - catch (...) - { - error = true; - } - - if (error) - Error(_log, "Failed to initialize mbedtls seed"); } ProviderUdpSSL::~ProviderUdpSSL() { closeConnection(); - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); + if (entropy != nullptr) + { + mbedtls_ctr_drbg_free(ctr_drbg); + mbedtls_entropy_free(entropy); + delete entropy; + } + + delete client_fd; + delete ssl; + delete conf; + delete cacert; + delete ctr_drbg; + delete timer; } bool ProviderUdpSSL::init(const QJsonObject& deviceConfig) @@ -187,10 +202,16 @@ bool ProviderUdpSSL::initConnection() if (_streamReady) return true; - mbedtls_net_init(&client_fd); - mbedtls_ssl_init(&ssl); - mbedtls_ssl_config_init(&conf); - mbedtls_x509_crt_init(&cacert); + if (!createEntropy()) + { + Error(_log, "Failed to generate initial entropy"); + return false; + } + + mbedtls_net_init(client_fd); + mbedtls_ssl_init(ssl); + mbedtls_ssl_config_init(conf); + mbedtls_x509_crt_init(cacert); if (setupStructure()) { @@ -200,7 +221,14 @@ bool ProviderUdpSSL::initConnection() return true; } else + { + mbedtls_net_free(client_fd); + mbedtls_ssl_free(ssl); + mbedtls_ssl_config_free(conf); + mbedtls_x509_crt_free(cacert); + return false; + } } void ProviderUdpSSL::closeConnection() @@ -213,19 +241,29 @@ void ProviderUdpSSL::closeConnection() } } -bool ProviderUdpSSL::seedingRNG() +bool ProviderUdpSSL::createEntropy() { - mbedtls_entropy_init(&entropy); + if (entropy != nullptr) + return true; + + mbedtls_ctr_drbg_init(ctr_drbg); + entropy = new mbedtls_entropy_context(); + mbedtls_entropy_init(entropy); - QByteArray customDataArray = _custom.toLocal8Bit(); + const QByteArray& customDataArray = _custom.toLocal8Bit(); const char* customData = customDataArray.constData(); - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, - &entropy, reinterpret_cast(customData), + int ret = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, + entropy, reinterpret_cast(customData), std::min(strlen(customData), (size_t)MBEDTLS_CTR_DRBG_MAX_SEED_INPUT)); if (ret != 0) - { + { + mbedtls_ctr_drbg_free(ctr_drbg); + mbedtls_entropy_free(entropy); + delete entropy; + entropy = nullptr; + Error(_log, "%s", QSTRING_CSTR(QString("mbedtls_ctr_drbg_seed FAILED %1").arg(errorMsg(ret)))); return false; } @@ -237,7 +275,7 @@ bool ProviderUdpSSL::setupStructure() { int transport = (_transport_type == "DTLS") ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM; - int ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, transport, MBEDTLS_SSL_PRESET_DEFAULT); + int ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, transport, MBEDTLS_SSL_PRESET_DEFAULT); if (ret != 0) { @@ -247,15 +285,15 @@ bool ProviderUdpSSL::setupStructure() const int* ciphersuites = getCiphersuites(); - mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED); - mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); + mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_ca_chain(conf, cacert, NULL); - mbedtls_ssl_conf_handshake_timeout(&conf, _handshake_timeout_min, _handshake_timeout_max); + mbedtls_ssl_conf_handshake_timeout(conf, _handshake_timeout_min, _handshake_timeout_max); - mbedtls_ssl_conf_ciphersuites(&conf, ciphersuites); - mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_ciphersuites(conf, ciphersuites); + mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, ctr_drbg); - if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) + if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0) { Error(_log, "%s", QSTRING_CSTR(QString("mbedtls_ssl_setup FAILED %1").arg(errorMsg(ret)))); return false; @@ -266,12 +304,12 @@ bool ProviderUdpSSL::setupStructure() bool ProviderUdpSSL::startUPDConnection() { - mbedtls_ssl_session_reset(&ssl); + mbedtls_ssl_session_reset(ssl); if (!setupPSK()) return false; - int ret = mbedtls_net_connect(&client_fd, _address.toString().toUtf8(), std::to_string(_ssl_port).c_str(), MBEDTLS_NET_PROTO_UDP); + int ret = mbedtls_net_connect(client_fd, _address.toString().toUtf8(), std::to_string(_ssl_port).c_str(), MBEDTLS_NET_PROTO_UDP); if (ret != 0) { @@ -279,8 +317,8 @@ bool ProviderUdpSSL::startUPDConnection() return false; } - mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout); - mbedtls_ssl_set_timer_cb(&ssl, &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); + mbedtls_ssl_set_bio(ssl, client_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout); + mbedtls_ssl_set_timer_cb(ssl, timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); return startSSLHandshake(); } @@ -290,7 +328,7 @@ bool ProviderUdpSSL::setupPSK() QByteArray pskRawArray = QByteArray::fromHex(_psk.toUtf8()); QByteArray pskIdRawArray = _psk_identity.toUtf8(); - int ret = mbedtls_ssl_conf_psk(&conf, + int ret = mbedtls_ssl_conf_psk(conf, reinterpret_cast (pskRawArray.constData()), pskRawArray.length(), reinterpret_cast (pskIdRawArray.constData()), @@ -316,7 +354,7 @@ bool ProviderUdpSSL::startSSLHandshake() do { - ret = mbedtls_ssl_handshake(&ssl); + ret = mbedtls_ssl_handshake(ssl); } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE); if (ret == 0) @@ -339,7 +377,7 @@ bool ProviderUdpSSL::startSSLHandshake() } else { - if (mbedtls_ssl_get_verify_result(&ssl) != 0) + if (mbedtls_ssl_get_verify_result(ssl) != 0) { Error(_log, "SSL certificate verification failed!"); return false; @@ -354,11 +392,11 @@ void ProviderUdpSSL::freeSSLConnection() try { Warning(_log, "Release mbedtls"); - mbedtls_ssl_session_reset(&ssl); - mbedtls_net_free(&client_fd); - mbedtls_ssl_free(&ssl); - mbedtls_ssl_config_free(&conf); - mbedtls_x509_crt_free(&cacert); + mbedtls_ssl_session_reset(ssl); + mbedtls_net_free(client_fd); + mbedtls_ssl_free(ssl); + mbedtls_ssl_config_free(conf); + mbedtls_x509_crt_free(cacert); } catch (std::exception& e) { @@ -384,7 +422,7 @@ void ProviderUdpSSL::writeBytes(unsigned int size, const uint8_t* data, bool flu do { - ret = mbedtls_ssl_write(&ssl, data, size); + ret = mbedtls_ssl_write(ssl, data, size); } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE); if (ret <= 0) @@ -427,7 +465,7 @@ void ProviderUdpSSL::writeBytes(unsigned int size, const uint8_t* data, bool flu this->enableDevice(false); if (!_isOn) - emit enableStateChanged(false); + emit SignalEnableStateChanged(false); } } } @@ -443,7 +481,10 @@ QString ProviderUdpSSL::errorMsg(int ret) void ProviderUdpSSL::closeSSLNotify() { /* No error checking, the connection might be closed already */ - while (mbedtls_ssl_close_notify(&ssl) == MBEDTLS_ERR_SSL_WANT_WRITE); + while (mbedtls_ssl_close_notify(ssl) == MBEDTLS_ERR_SSL_WANT_WRITE); } - +int ProviderUdpSSL::get_MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256() const +{ + return MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256; +} diff --git a/sources/leddevice/dev_net/ProviderUdpSSL.h b/sources/leddevice/dev_net/ProviderUdpSSL.h index 21d3e209e..43b928925 100644 --- a/sources/leddevice/dev_net/ProviderUdpSSL.h +++ b/sources/leddevice/dev_net/ProviderUdpSSL.h @@ -1,97 +1,48 @@ -#ifndef PROVIDERUDPSSL_H -#define PROVIDERUDPSSL_H +#pragma once -#include -#include +#ifndef PCH_ENABLED + #include + #include -// Qt includes -#include -#include - -//----------- mbedtls -#if !defined(MBEDTLS_OLD_CONFIG_FILE) - #include -#else - #include MBEDTLS_OLD_CONFIG_FILE + #include + #include + #include #endif -#if defined(MBEDTLS_PLATFORM_C) -#include -#endif +#include +#include -#include -#include -#include +struct mbedtls_net_context; +struct mbedtls_entropy_context; +struct mbedtls_ssl_context; +struct mbedtls_ssl_config; +struct mbedtls_x509_crt; +struct mbedtls_ctr_drbg_context; +struct mbedtls_timing_delay_context; -#include -#include -#include -#include -#include -#include -#include class ProviderUdpSSL : public LedDevice { Q_OBJECT public: - /// - /// @brief Constructs an UDP SSL LED-device - /// ProviderUdpSSL(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LED-device - /// virtual ~ProviderUdpSSL(); protected: - - /// - /// @brief Initialise the UDP-SSL device's configuration and network address details - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success#endif // PROVIDERUDP_H - /// + int get_MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256() const; bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int closeNetwork(); - - /// - /// @brief Initialise device's network details - /// - /// @return True, if success - /// bool initNetwork(); - - /// - /// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the - /// values are latched. - /// - /// @param[in] size The length of the data - /// @param[in] data The data - /// void writeBytes(unsigned int size, const uint8_t* data, bool flush = false); - - /// - /// get ciphersuites list from mbedtls_ssl_list_ciphersuites - /// - /// @return const int * array - /// virtual const int* getCiphersuites() const; private: bool initConnection(); void closeConnection(); - bool seedingRNG(); + bool createEntropy(); bool setupStructure(); bool startUPDConnection(); bool setupPSK(); @@ -100,13 +51,13 @@ class ProviderUdpSSL : public LedDevice void closeSSLNotify(); void freeSSLConnection(); - mbedtls_net_context client_fd; - mbedtls_entropy_context entropy; - mbedtls_ssl_context ssl; - mbedtls_ssl_config conf; - mbedtls_x509_crt cacert; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_timing_delay_context timer; + mbedtls_net_context* client_fd; + mbedtls_entropy_context* entropy; + mbedtls_ssl_context* ssl; + mbedtls_ssl_config* conf; + mbedtls_x509_crt* cacert; + mbedtls_ctr_drbg_context* ctr_drbg; + mbedtls_timing_delay_context* timer; QString _transport_type; QString _custom; @@ -124,5 +75,3 @@ class ProviderUdpSSL : public LedDevice uint32_t _handshake_timeout_min; uint32_t _handshake_timeout_max; }; - -#endif // PROVIDERUDPSSL_H diff --git a/sources/leddevice/dev_other/LedDeviceFile.cpp b/sources/leddevice/dev_other/LedDeviceFile.cpp index 67b93c7ea..639993b0a 100644 --- a/sources/leddevice/dev_other/LedDeviceFile.cpp +++ b/sources/leddevice/dev_other/LedDeviceFile.cpp @@ -1,12 +1,11 @@ #include "LedDeviceFile.h" -// Qt includes -#include #include +#include LedDeviceFile::LedDeviceFile(const QJsonObject& deviceConfig) : LedDevice(deviceConfig) - , _lastWriteTime(QDateTime::currentDateTime()) + , _lastWriteTimeNano(std::chrono::high_resolution_clock::now()) , _file(nullptr) { _printTimeStamp = false; @@ -26,14 +25,14 @@ bool LedDeviceFile::init(const QJsonObject& deviceConfig) { bool initOK = LedDevice::init(deviceConfig); - _lastWriteTime = QDateTime::currentDateTime(); + _lastWriteTimeNano = std::chrono::high_resolution_clock::now(); _fileName = deviceConfig["output"].toString("/dev/null"); #if _WIN32 - if (_fileName == "/dev/null") + if (_fileName == "/dev/null" || _fileName.trimmed().length() == 0) { - _fileName = "NULL"; + _fileName = "\\\\.\\NUL"; } #endif @@ -100,11 +99,14 @@ int LedDeviceFile::write(const std::vector& ledValues) if (_printTimeStamp) { QDateTime now = QDateTime::currentDateTime(); - qint64 elapsedTimeMs = _lastWriteTime.msecsTo(now); + auto nowNano = std::chrono::high_resolution_clock::now(); - out << now.toString(Qt::ISODateWithMs) << " | +" << QString("%1").arg(elapsedTimeMs, 4); + auto nano = std::chrono::duration_cast(nowNano-_lastWriteTimeNano).count(); + double nanoDouble = (nano/1000000.0); - _lastWriteTime = now; + out << now.toString(Qt::ISODateWithMs) << " | +" << QString("%1").arg(nanoDouble, 0, 'f', 1); + + _lastWriteTimeNano = std::chrono::high_resolution_clock::now(); } out << " ["; diff --git a/sources/leddevice/dev_other/LedDeviceFile.h b/sources/leddevice/dev_other/LedDeviceFile.h index dd5c6f42b..7584bf49d 100644 --- a/sources/leddevice/dev_other/LedDeviceFile.h +++ b/sources/leddevice/dev_other/LedDeviceFile.h @@ -1,82 +1,33 @@ -#ifndef LEDEVICEFILE_H -#define LEDEVICEFILE_H +#pragma once + +#ifndef PCH_ENABLED + #include + #include +#endif -// LedDevice includes #include -// Qt includes -#include -#include +class QFile; -/// -/// Implementation of the LedDevice that write the LED-colors to an -/// ASCII-textfile primarily for testing purposes -/// class LedDeviceFile : public LedDevice { public: - - /// - /// @brief Constructs a file output LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceFile(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LedDevice - /// ~LedDeviceFile() override; - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); protected: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; private: - - QDateTime _lastWriteTime; + std::chrono::time_point _lastWriteTimeNano; QFile* _file; QString _fileName; - /// Timestamp for the output record bool _printTimeStamp; - }; - -#endif // LEDEVICEFILE_H diff --git a/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp b/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp index 6af71bc0f..11afe742b 100644 --- a/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp +++ b/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp @@ -1,8 +1,12 @@ #include "LedDeviceWS281x.h" +#include LedDeviceWS281x::LedDeviceWS281x(const QJsonObject& deviceConfig) : LedDevice(deviceConfig) { + _ledString = nullptr; + _channel = 0; + _whiteAlgorithm = RGBW::stringToWhiteAlgorithm("white_off"); } LedDeviceWS281x::~LedDeviceWS281x() @@ -20,6 +24,9 @@ bool LedDeviceWS281x::init(const QJsonObject& deviceConfig) bool isInitOK = false; + if (_ledString == nullptr) + _ledString = std::unique_ptr(new ws2811_t()); + // Initialise sub-class if (LedDevice::init(deviceConfig)) { @@ -34,31 +41,34 @@ bool LedDeviceWS281x::init(const QJsonObject& deviceConfig) else { _channel = deviceConfig["pwmchannel"].toInt(0); - if (_channel != 0 && _channel != 1) + if (_channel <0 || _channel >= RPI_PWM_CHANNELS) { - errortext = "WS281x: invalid PWM channel; must be 0 or 1."; + errortext = QString("WS281x: invalid PWM channel. Must be greater than 0 and less than %1").arg(RPI_PWM_CHANNELS); isInitOK = false; } else - { - memset(&_led_string, 0, sizeof(_led_string)); - _led_string.freq = deviceConfig["freq"].toInt(800000UL); - _led_string.dmanum = deviceConfig["dma"].toInt(5); - _led_string.channel[_channel].gpionum = deviceConfig["gpio"].toInt(18); - _led_string.channel[_channel].count = deviceConfig["leds"].toInt(256); - _led_string.channel[_channel].invert = deviceConfig["invert"].toInt(0); - _led_string.channel[_channel].strip_type = (deviceConfig["rgbw"].toBool(false) ? SK6812_STRIP_GRBW : WS2811_STRIP_RGB); - _led_string.channel[_channel].brightness = 255; - - _led_string.channel[!_channel].gpionum = 0; - _led_string.channel[!_channel].invert = _led_string.channel[_channel].invert; - _led_string.channel[!_channel].count = 0; - _led_string.channel[!_channel].brightness = 0; - _led_string.channel[!_channel].strip_type = WS2811_STRIP_RGB; - - Debug(_log, "ws281x strip type : %d", _led_string.channel[_channel].strip_type); - - if (_refreshTimerInterval_ms > 0) + { + memset(_ledString.get(), 0, sizeof(ws2811_t)); + _ledString->freq = deviceConfig["freq"].toInt(800000UL); + _ledString->dmanum = deviceConfig["dma"].toInt(5); + _ledString->channel[_channel].gpionum = deviceConfig["gpio"].toInt(18); + _ledString->channel[_channel].count = deviceConfig["leds"].toInt(256); + _ledString->channel[_channel].invert = deviceConfig["invert"].toInt(0); + _ledString->channel[_channel].strip_type = (deviceConfig["rgbw"].toBool(false) ? SK6812_STRIP_GRBW : WS2811_STRIP_RGB); + _ledString->channel[_channel].brightness = 255; + + _ledString->channel[!_channel].gpionum = 0; + _ledString->channel[!_channel].invert = _ledString->channel[_channel].invert; + _ledString->channel[!_channel].count = 0; + _ledString->channel[!_channel].brightness = 0; + _ledString->channel[!_channel].strip_type = _ledString->channel[_channel].strip_type; + + Debug(_log, "ws281x selected dma : %d", _ledString->dmanum); + Debug(_log, "ws281x selected channel : %d", _channel); + Debug(_log, "ws281x total channels : %d", RPI_PWM_CHANNELS); + Debug(_log, "ws281x strip type : %d", _ledString->channel[_channel].strip_type); + + if (_defaultInterval > 0) Error(_log, "The refresh timer is enabled ('Refresh time' > 0) and may limit the performance of the LED driver. Ignore this error if you set it on purpose for some reason (but you almost never need it)."); isInitOK = true; @@ -78,17 +88,22 @@ int LedDeviceWS281x::open() int retval = -1; _isDeviceReady = false; - // Try to open the LedDevice + if (_ledString == nullptr) + { + Error(_log, "Unexpected uninitialized case"); + return retval; + } - ws2811_return_t rc = ws2811_init(&_led_string); + ws2811_return_t rc = ws2811_init(_ledString.get()); if (rc != WS2811_SUCCESS) { QString errortext = QString("Failed to open. Error message: %1").arg(ws2811_get_return_t_str(rc)); + if (errortext.contains("mmap()", Qt::CaseInsensitive)) + errortext += ". YOUR NEED TO RUN HYPERHDR AS A ROOT USER FOR MMAP TO WORK. SUCH CONFIGURATION IS NOT OFFICIALLY SUPPORTED."; this->setInError(errortext); } else { - // Everything is OK, device is ready _isDeviceReady = true; retval = 0; } @@ -103,7 +118,7 @@ int LedDeviceWS281x::close() // LedDevice specific closing activities if (isInitialised()) { - ws2811_fini(&_led_string); + ws2811_fini(_ledString.get()); } return retval; @@ -113,9 +128,10 @@ int LedDeviceWS281x::close() int LedDeviceWS281x::write(const std::vector& ledValues) { int idx = 0; + for (const ColorRgb& color : ledValues) { - if (idx >= _led_string.channel[_channel].count) + if (idx >= _ledString->channel[_channel].count) { break; } @@ -125,19 +141,19 @@ int LedDeviceWS281x::write(const std::vector& ledValues) _temp_rgbw.blue = color.blue; _temp_rgbw.white = 0; - if (_led_string.channel[_channel].strip_type == SK6812_STRIP_GRBW) + if (_ledString->channel[_channel].strip_type == SK6812_STRIP_GRBW) { Rgb_to_Rgbw(color, &_temp_rgbw, _whiteAlgorithm); } - _led_string.channel[_channel].leds[idx++] = + _ledString->channel[_channel].leds[idx++] = ((uint32_t)_temp_rgbw.white << 24) + ((uint32_t)_temp_rgbw.red << 16) + ((uint32_t)_temp_rgbw.green << 8) + _temp_rgbw.blue; - } - while (idx < _led_string.channel[_channel].count) + + while (idx < _ledString->channel[_channel].count) { - _led_string.channel[_channel].leds[idx++] = 0; + _ledString->channel[_channel].leds[idx++] = 0; } - return ws2811_render(&_led_string) ? -1 : 0; + return ws2811_render(_ledString.get()) ? -1 : 0; } diff --git a/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.h b/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.h index a019c01b2..4ee1265a2 100644 --- a/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.h +++ b/sources/leddevice/dev_rpi_pwm/LedDeviceWS281x.h @@ -1,72 +1,26 @@ -#ifndef LEDEVICEWS281X_H -#define LEDEVICEWS281X_H +#pragma once -// LedDevice includes #include -#include -/// -/// Implementation of the LedDevice interface for writing to WS281x LED-device via pwm. -/// +struct ws2811_t; + class LedDeviceWS281x : public LedDevice { public: - - /// - /// @brief Constructs an WS281x LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceWS281x(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LedDevice - /// ~LedDeviceWS281x() override; - - /// - /// @brief Destructor of the LedDevice - /// static LedDevice* construct(const QJsonObject& deviceConfig); protected: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// int open() override; - - /// - /// @brief Closes the output device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// int close() override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; private: - ws2811_t _led_string; - int _channel; + std::unique_ptr _ledString; + int _channel; RGBW::WhiteAlgorithm _whiteAlgorithm; - ColorRgbw _temp_rgbw; + ColorRgbw _temp_rgbw; }; - -#endif // LEDEVICEWS281X_H diff --git a/sources/leddevice/dev_serial/EspTools.h b/sources/leddevice/dev_serial/EspTools.h index 94edd837e..e29e90d2a 100644 --- a/sources/leddevice/dev_serial/EspTools.h +++ b/sources/leddevice/dev_serial/EspTools.h @@ -1,8 +1,10 @@ +#pragma once + /* EspTools.h * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,70 +27,67 @@ * SOFTWARE. */ -#ifndef ESPTOOLS_H -#define ESPTOOLS_H - class EspTools { public: - static void goingSleep(QSerialPort& _rs232Port) + static void goingSleep(QSerialPort* _serialPort) { uint8_t comBuffer[] = { 0x41, 0x77, 0x41, 0x2a, 0xa2, 0x35, 0x68, 0x79, 0x70, 0x65, 0x72, 0x68, 0x64, 0x72 }; - _rs232Port.write((char*)comBuffer, sizeof(comBuffer)); + _serialPort->write((char*)comBuffer, sizeof(comBuffer)); } - static void initializeEsp(QSerialPort& _rs232Port, QSerialPortInfo& serialPortInfo, Logger*& _log) + static void initializeEsp(QSerialPort* _serialPort, QSerialPortInfo& serialPortInfo, Logger*& _log) { uint8_t comBuffer[] = { 0x41, 0x77, 0x41, 0x2a, 0xa2, 0x15, 0x68, 0x79, 0x70, 0x65, 0x72, 0x68, 0x64, 0x72 }; if (serialPortInfo.productIdentifier() == 0xa && serialPortInfo.vendorIdentifier() == 0x2e8a) { Warning(_log, "Detected Rp2040 type board. HyperHDR skips the reset. State: %i, %i", - _rs232Port.isDataTerminalReady(), _rs232Port.isRequestToSend()); + _serialPort->isDataTerminalReady(), _serialPort->isRequestToSend()); - _rs232Port.write((char*)comBuffer, sizeof(comBuffer)); + _serialPort->write((char*)comBuffer, sizeof(comBuffer)); - _rs232Port.setDataTerminalReady(true); - _rs232Port.setRequestToSend(true); - _rs232Port.setRequestToSend(false); + _serialPort->setDataTerminalReady(true); + _serialPort->setRequestToSend(true); + _serialPort->setRequestToSend(false); } else if (serialPortInfo.productIdentifier() == 0x80c2 && serialPortInfo.vendorIdentifier() == 0x303a) { Warning(_log, "Detected ESP32-S2 lolin mini type board. HyperHDR skips the reset. State: %i, %i", - _rs232Port.isDataTerminalReady(), _rs232Port.isRequestToSend()); + _serialPort->isDataTerminalReady(), _serialPort->isRequestToSend()); - _rs232Port.write((char*)comBuffer, sizeof(comBuffer)); + _serialPort->write((char*)comBuffer, sizeof(comBuffer)); - _rs232Port.setDataTerminalReady(true); - _rs232Port.setRequestToSend(true); - _rs232Port.setRequestToSend(false); + _serialPort->setDataTerminalReady(true); + _serialPort->setRequestToSend(true); + _serialPort->setRequestToSend(false); } else if (serialPortInfo.productIdentifier() == 0x3483 && serialPortInfo.vendorIdentifier() == 0x1106) { Warning(_log, "Enabling the Rpi4 udev bug workaround. The serial device is incorrectly identified by the OS and HyperHDR skips the reset. State: %i, %i", - _rs232Port.isDataTerminalReady(), _rs232Port.isRequestToSend()); + _serialPort->isDataTerminalReady(), _serialPort->isRequestToSend()); - _rs232Port.write((char*)comBuffer, sizeof(comBuffer)); + _serialPort->write((char*)comBuffer, sizeof(comBuffer)); - _rs232Port.setDataTerminalReady(true); - _rs232Port.setRequestToSend(true); - _rs232Port.setRequestToSend(false); + _serialPort->setDataTerminalReady(true); + _serialPort->setRequestToSend(true); + _serialPort->setRequestToSend(false); } else { // reset to defaults - _rs232Port.setDataTerminalReady(true); - _rs232Port.setRequestToSend(false); + _serialPort->setDataTerminalReady(true); + _serialPort->setRequestToSend(false); QThread::msleep(50); // reset device - _rs232Port.setDataTerminalReady(false); - _rs232Port.setRequestToSend(true); + _serialPort->setDataTerminalReady(false); + _serialPort->setRequestToSend(true); QThread::msleep(150); // resume device - _rs232Port.setRequestToSend(false); + _serialPort->setRequestToSend(false); QThread::msleep(100); } @@ -97,10 +96,10 @@ class EspTools while (InternalClock::now() - start < 1000) { - _rs232Port.waitForReadyRead(100); - if (_rs232Port.bytesAvailable() > 16) + _serialPort->waitForReadyRead(100); + if (_serialPort->bytesAvailable() > 16) { - auto incoming = _rs232Port.readAll(); + auto incoming = _serialPort->readAll(); for (int i = 0; i < incoming.length(); i++) if (!(incoming[i] == '\n' || (incoming[i] >= ' ' && incoming[i] <= 'Z') || @@ -125,4 +124,3 @@ class EspTools } }; -#endif diff --git a/sources/leddevice/dev_serial/LedDeviceAdalight.cpp b/sources/leddevice/dev_serial/LedDeviceAdalight.cpp index 129404897..f33b2a3e9 100644 --- a/sources/leddevice/dev_serial/LedDeviceAdalight.cpp +++ b/sources/leddevice/dev_serial/LedDeviceAdalight.cpp @@ -5,7 +5,7 @@ #include LedDeviceAdalight::LedDeviceAdalight(const QJsonObject& deviceConfig) - : ProviderRs232(deviceConfig) + : ProviderSerial(deviceConfig) , _headerSize(6) , _ligthBerryAPA102Mode(false) , _awa_mode(false) @@ -27,7 +27,7 @@ bool LedDeviceAdalight::init(const QJsonObject& deviceConfig) bool isInitOK = false; // Initialise sub-class - if (ProviderRs232::init(deviceConfig)) + if (ProviderSerial::init(deviceConfig)) { _ligthBerryAPA102Mode = deviceConfig["lightberry_apa102_mode"].toBool(false); diff --git a/sources/leddevice/dev_serial/LedDeviceAdalight.h b/sources/leddevice/dev_serial/LedDeviceAdalight.h index f2749bc10..5ea018aef 100644 --- a/sources/leddevice/dev_serial/LedDeviceAdalight.h +++ b/sources/leddevice/dev_serial/LedDeviceAdalight.h @@ -1,50 +1,18 @@ -#ifndef LEDEVICETADALIGHT_H -#define LEDEVICETADALIGHT_H +#pragma once -// HyperHDR includes -#include "ProviderRs232.h" +#include "ProviderSerial.h" -/// -/// Implementation of the LedDevice interface for writing to an Adalight LED-device. -/// -class LedDeviceAdalight : public ProviderRs232 +class LedDeviceAdalight : public ProviderSerial { Q_OBJECT public: - - /// - /// @brief Constructs an Adalight LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceAdalight(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - void CreateHeader(); - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; void whiteChannelExtension(uint8_t*& writer); @@ -59,5 +27,3 @@ class LedDeviceAdalight : public ProviderRs232 uint8_t _white_channel_green; uint8_t _white_channel_blue; }; - -#endif // LEDEVICETADALIGHT_H diff --git a/sources/leddevice/dev_serial/LedDeviceAtmo.cpp b/sources/leddevice/dev_serial/LedDeviceAtmo.cpp index 22714e221..142c92e85 100644 --- a/sources/leddevice/dev_serial/LedDeviceAtmo.cpp +++ b/sources/leddevice/dev_serial/LedDeviceAtmo.cpp @@ -2,7 +2,7 @@ #include "LedDeviceAtmo.h" LedDeviceAtmo::LedDeviceAtmo(const QJsonObject& deviceConfig) - : ProviderRs232(deviceConfig) + : ProviderSerial(deviceConfig) { } @@ -16,7 +16,7 @@ bool LedDeviceAtmo::init(const QJsonObject& deviceConfig) bool isInitOK = false; // Initialise sub-class - if (ProviderRs232::init(deviceConfig)) + if (ProviderSerial::init(deviceConfig)) { if (_ledCount != 5) { diff --git a/sources/leddevice/dev_serial/LedDeviceAtmo.h b/sources/leddevice/dev_serial/LedDeviceAtmo.h index 123655711..b5e47425d 100644 --- a/sources/leddevice/dev_serial/LedDeviceAtmo.h +++ b/sources/leddevice/dev_serial/LedDeviceAtmo.h @@ -1,44 +1,14 @@ -#ifndef LEDEVICEATMO_H -#define LEDEVICEATMO_H +#pragma once -// HyperHDR includes -#include "ProviderRs232.h" +#include "ProviderSerial.h" -/// -/// Implementation of the LedDevice interface for writing to serial device using tpm2 protocol. -/// -class LedDeviceAtmo : public ProviderRs232 +class LedDeviceAtmo : public ProviderSerial { public: - - /// - /// @brief Constructs an Atmo LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceAtmo(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the LedDevice - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICEATMO_H diff --git a/sources/leddevice/dev_serial/LedDeviceDMX.cpp b/sources/leddevice/dev_serial/LedDeviceDMX.cpp index 86b972e73..b5f679cd0 100644 --- a/sources/leddevice/dev_serial/LedDeviceDMX.cpp +++ b/sources/leddevice/dev_serial/LedDeviceDMX.cpp @@ -2,11 +2,11 @@ #include #ifndef _WIN32 -#include + #include #endif LedDeviceDMX::LedDeviceDMX(const QJsonObject& deviceConfig) - : ProviderRs232(deviceConfig) + : ProviderSerial(deviceConfig) , _dmxDeviceType(0) , _dmxStart(1) , _dmxSlotsPerLed(3) @@ -25,7 +25,7 @@ bool LedDeviceDMX::init(const QJsonObject& deviceConfig) bool isInitOK = false; // Initialise sub-class - if (ProviderRs232::init(deviceConfig)) + if (ProviderSerial::init(deviceConfig)) { QString dmxTypeString = deviceConfig["dmxtype"].toString("invalid"); if (dmxTypeString == "raw") @@ -49,7 +49,7 @@ bool LedDeviceDMX::init(const QJsonObject& deviceConfig) } Debug(_log, "_dmxTypeString \"%s\", _dmxDeviceType %d", QSTRING_CSTR(dmxTypeString), _dmxDeviceType); - _rs232Port.setStopBits(QSerialPort::TwoStop); + _serialPort->setStopBits(QSerialPort::TwoStop); _dmxLedCount = qMin(static_cast(_ledCount), 512 / _dmxSlotsPerLed); _dmxChannelCount = 1 + _dmxSlotsPerLed * _dmxLedCount; @@ -83,12 +83,12 @@ int LedDeviceDMX::write(const std::vector& ledValues) break; } - _rs232Port.setBreakEnabled(true); + _serialPort->setBreakEnabled(true); // Note Windows: There is no concept of ns sleeptime, the closest possible is 1ms but requested is 0,000176ms #ifndef _WIN32 nanosleep((const struct timespec[]) { {0, 176000L} }, NULL); // 176 uSec break time #endif - _rs232Port.setBreakEnabled(false); + _serialPort->setBreakEnabled(false); #ifndef _WIN32 nanosleep((const struct timespec[]) { {0, 12000L} }, NULL); // 176 uSec make after break time #endif diff --git a/sources/leddevice/dev_serial/LedDeviceDMX.h b/sources/leddevice/dev_serial/LedDeviceDMX.h index 3190cea92..d34ea3189 100644 --- a/sources/leddevice/dev_serial/LedDeviceDMX.h +++ b/sources/leddevice/dev_serial/LedDeviceDMX.h @@ -1,46 +1,15 @@ -#ifndef LEDEVICEDMX_H -#define LEDEVICEDMX_H +#pragma once -// HyperHDR includes -#include "ProviderRs232.h" +#include "ProviderSerial.h" -/// -/// Implementation of the LedDevice interface for writing to DMX512 rs232 LED-device. -/// -class LedDeviceDMX : public ProviderRs232 +class LedDeviceDMX : public ProviderSerial { public: - - /// - /// @brief Constructs a DMX LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceDMX(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; int _dmxDeviceType = 0; @@ -49,5 +18,3 @@ class LedDeviceDMX : public ProviderRs232 int _dmxLedCount = 0; unsigned int _dmxChannelCount = 0; }; - -#endif // LEDEVICEDMX_H diff --git a/sources/leddevice/dev_serial/LedDeviceKarate.cpp b/sources/leddevice/dev_serial/LedDeviceKarate.cpp index ceb8b2b24..06040ae28 100644 --- a/sources/leddevice/dev_serial/LedDeviceKarate.cpp +++ b/sources/leddevice/dev_serial/LedDeviceKarate.cpp @@ -2,7 +2,7 @@ #include "LedDeviceKarate.h" LedDeviceKarate::LedDeviceKarate(const QJsonObject& deviceConfig) - : ProviderRs232(deviceConfig) + : ProviderSerial(deviceConfig) { } @@ -16,7 +16,7 @@ bool LedDeviceKarate::init(const QJsonObject& deviceConfig) bool isInitOK = false; // Initialise sub-class - if (ProviderRs232::init(deviceConfig)) + if (ProviderSerial::init(deviceConfig)) { if (_ledCount != 8 && _ledCount != 16) { diff --git a/sources/leddevice/dev_serial/LedDeviceKarate.h b/sources/leddevice/dev_serial/LedDeviceKarate.h index ec7be2296..72777f81e 100644 --- a/sources/leddevice/dev_serial/LedDeviceKarate.h +++ b/sources/leddevice/dev_serial/LedDeviceKarate.h @@ -1,47 +1,15 @@ -#ifndef LEDEVICEKARATE_H -#define LEDEVICEKARATE_H +#pragma once -// HyperHDR includes -#include "ProviderRs232.h" +#include "ProviderSerial.h" -/// -/// Implementation of the LedDevice interface for writing to serial device -/// -class LedDeviceKarate : public ProviderRs232 +class LedDeviceKarate : public ProviderSerial { Q_OBJECT public: - - /// - /// @brief Constructs a Karate LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceKarate(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICEKARATE_H diff --git a/sources/leddevice/dev_serial/LedDeviceSedu.cpp b/sources/leddevice/dev_serial/LedDeviceSedu.cpp index d1e0fa38e..8c30dddfb 100644 --- a/sources/leddevice/dev_serial/LedDeviceSedu.cpp +++ b/sources/leddevice/dev_serial/LedDeviceSedu.cpp @@ -7,7 +7,7 @@ struct FrameSpec }; LedDeviceSedu::LedDeviceSedu(const QJsonObject& deviceConfig) - : ProviderRs232(deviceConfig) + : ProviderSerial(deviceConfig) { } @@ -21,7 +21,7 @@ bool LedDeviceSedu::init(const QJsonObject& deviceConfig) bool isInitOK = false; // Initialise sub-class - if (ProviderRs232::init(deviceConfig)) + if (ProviderSerial::init(deviceConfig)) { std::vector frameSpecs{ {0xA1, 256}, {0xA2, 512}, {0xB0, 768}, {0xB1, 1536}, {0xB2, 3072} }; diff --git a/sources/leddevice/dev_serial/LedDeviceSedu.h b/sources/leddevice/dev_serial/LedDeviceSedu.h index 1e4eab5da..564863efa 100644 --- a/sources/leddevice/dev_serial/LedDeviceSedu.h +++ b/sources/leddevice/dev_serial/LedDeviceSedu.h @@ -1,47 +1,14 @@ -#ifndef LEDEVICESEDU_H -#define LEDEVICESEDU_H +#pragma once -// HyperHDR includes -#include "ProviderRs232.h" +#include "ProviderSerial.h" -/// -/// Implementation of the LedDevice interface for writing to SEDU LED-device. -/// -class LedDeviceSedu : public ProviderRs232 +class LedDeviceSedu : public ProviderSerial { public: - - /// - /// @brief Constructs a SEDU LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceSedu(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICESEDU_H diff --git a/sources/leddevice/dev_serial/LedDeviceTpm2.cpp b/sources/leddevice/dev_serial/LedDeviceTpm2.cpp index 7fa9a7b14..477803271 100644 --- a/sources/leddevice/dev_serial/LedDeviceTpm2.cpp +++ b/sources/leddevice/dev_serial/LedDeviceTpm2.cpp @@ -2,7 +2,7 @@ LedDeviceTpm2::LedDeviceTpm2(const QJsonObject& deviceConfig) - : ProviderRs232(deviceConfig) + : ProviderSerial(deviceConfig) { } @@ -16,7 +16,7 @@ bool LedDeviceTpm2::init(const QJsonObject& deviceConfig) bool isInitOK = false; // Initialise sub-class - if (ProviderRs232::init(deviceConfig)) + if (ProviderSerial::init(deviceConfig)) { _ledBuffer.resize(5 + _ledRGBCount); diff --git a/sources/leddevice/dev_serial/LedDeviceTpm2.h b/sources/leddevice/dev_serial/LedDeviceTpm2.h index f2f240758..5a9dc8cb7 100644 --- a/sources/leddevice/dev_serial/LedDeviceTpm2.h +++ b/sources/leddevice/dev_serial/LedDeviceTpm2.h @@ -1,47 +1,16 @@ -#ifndef LEDEVICETPM2_H -#define LEDEVICETPM2_H +#pragma once -// HyperHDR includes -#include "ProviderRs232.h" +#include "ProviderSerial.h" -/// -/// Implementation of the LedDevice interface for writing to serial device using tpm2 protocol. -/// -class LedDeviceTpm2 : public ProviderRs232 +class LedDeviceTpm2 : public ProviderSerial { public: - /// - /// @brief Constructs a TPM 2 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceTpm2(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; -#endif // LEDEVICETPM2_H diff --git a/sources/leddevice/dev_serial/ProviderRs232.h b/sources/leddevice/dev_serial/ProviderRs232.h deleted file mode 100644 index 3dd00143e..000000000 --- a/sources/leddevice/dev_serial/ProviderRs232.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef PROVIDERRS232_H -#define PROVIDERRS232_H - -// LedDevice includes -#include - -// qt includes -#include - -/// -/// The ProviderRs232 implements an abstract base-class for LedDevices using a RS232-device. -/// -class ProviderRs232 : public LedDevice -{ - Q_OBJECT - -public: - - /// - /// @brief Constructs a RS232 LED-device - /// - ProviderRs232(const QJsonObject& deviceConfig); - - /// - /// @brief Destructor of the UDP LED-device - /// - ~ProviderRs232() override; - -protected: - - /// - /// @brief Initialise the RS232 device's configuration and network address details - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// - bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Opens the output device. - /// - /// @return Zero on success (i.e. device is ready), else negative - /// - int open() override; - - /// - /// @brief Closes the UDP device. - /// - /// @return Zero on success (i.e. device is closed), else negative - /// - int close() override; - - /// - /// @brief Power-/turn off a RS232-device - /// - /// The off-state is simulated by writing "Black to LED" - /// - /// @return True, if success - /// - bool powerOff() override; - - /// - /// @brief Discover first devices of a serial device available (for configuration) - /// - /// @return A string of the device found - /// - QString discoverFirst() override; - - /// @param[in] params Parameters used to overwrite discovery default behaviour - /// - /// @return A JSON structure holding a list of devices found - /// - QJsonObject discover(const QJsonObject& params) override; - - /// - /// @brief Write the given bytes to the RS232-device - /// - /// @param[in[ size The length of the data - /// @param[in] data The data - /// @return Zero on success, else negative - /// - int writeBytes(const qint64 size, const uint8_t* data); - - /// The name of the output device - QString _deviceName; - /// The RS232 serial-device - QSerialPort _rs232Port; - /// The used baud-rate of the output device - qint32 _baudRate_Hz; - -protected slots: - - /// - /// @brief Set device in error state - /// - /// @param errorMsg The error message to be logged - /// - void setInError(const QString& errorMsg) override; - -public slots: - void waitForExitStats(bool force); - -private: - - /// - /// @brief Try to open device if not opened - /// - /// @return True,if on success - /// - bool tryOpen(int delayAfterConnect_ms); - - /// Try to auto-discover device name? - bool _isAutoDeviceName; - - /// Sleep after the connect before continuing - int _delayAfterConnect_ms; - - /// Frames dropped, as write failed - int _frameDropCounter; - - bool _espHandshake; -}; - -#endif // PROVIDERRS232_H diff --git a/sources/leddevice/dev_serial/ProviderRs232.cpp b/sources/leddevice/dev_serial/ProviderSerial.cpp similarity index 68% rename from sources/leddevice/dev_serial/ProviderRs232.cpp rename to sources/leddevice/dev_serial/ProviderSerial.cpp index 72da40a3e..552e663ca 100644 --- a/sources/leddevice/dev_serial/ProviderRs232.cpp +++ b/sources/leddevice/dev_serial/ProviderSerial.cpp @@ -6,15 +6,16 @@ // LedDevice includes #include -#include "ProviderRs232.h" +#include "ProviderSerial.h" // qt includes #include #include #include +#include -#include #include +#include #include "EspTools.h" @@ -24,9 +25,9 @@ constexpr std::chrono::milliseconds OPEN_TIMEOUT{ 5000 }; // device open timeou const int MAX_WRITE_TIMEOUTS = 5; // Maximum number of allowed timeouts const int NUM_POWEROFF_WRITE_BLACK = 3; // Number of write "BLACK" during powering off -ProviderRs232::ProviderRs232(const QJsonObject& deviceConfig) +ProviderSerial::ProviderSerial(const QJsonObject& deviceConfig) : LedDevice(deviceConfig) - , _rs232Port(this) + , _serialPort(nullptr) , _baudRate_Hz(1000000) , _isAutoDeviceName(false) , _delayAfterConnect_ms(0) @@ -35,17 +36,19 @@ ProviderRs232::ProviderRs232(const QJsonObject& deviceConfig) { } -bool ProviderRs232::init(const QJsonObject& deviceConfig) +bool ProviderSerial::init(const QJsonObject& deviceConfig) { bool isInitOK = false; - // Initialise sub-class + if (_serialPort == nullptr) + _serialPort = new QSerialPort(this); + if (LedDevice::init(deviceConfig)) { Debug(_log, "DeviceType : %s", QSTRING_CSTR(this->getActiveDeviceType())); Debug(_log, "LedCount : %d", this->getLedCount()); - Debug(_log, "RefreshTime : %d", _refreshTimerInterval_ms); + Debug(_log, "RefreshTime : %d", this->getRefreshTime()); _deviceName = deviceConfig["output"].toString("auto"); @@ -66,7 +69,7 @@ bool ProviderRs232::init(const QJsonObject& deviceConfig) Debug(_log, "Delayed open : %d", _delayAfterConnect_ms); Debug(_log, "Retry limit : %d", _maxRetry); - if (_refreshTimerInterval_ms > 0) + if (_defaultInterval > 0) Error(_log, "The refresh timer is enabled ('Refresh time' > 0) and may limit the performance of the LED driver. Ignore this error if you set it on purpose for some reason (but you almost never need it)."); isInitOK = true; @@ -74,15 +77,21 @@ bool ProviderRs232::init(const QJsonObject& deviceConfig) return isInitOK; } -ProviderRs232::~ProviderRs232() +ProviderSerial::~ProviderSerial() { + if (_serialPort != nullptr && _serialPort->isOpen()) + _serialPort->close(); + + delete _serialPort; + + _serialPort = nullptr; } -int ProviderRs232::open() +int ProviderSerial::open() { int retval = -1; - if (_retryMode) + if (_serialPort == nullptr) return retval; _isDeviceReady = false; @@ -93,66 +102,43 @@ int ProviderRs232::open() // Everything is OK, device is ready _isDeviceReady = true; retval = 0; - - _currentRetry = 0; - _retryMode = false; } - else if (_maxRetry > 0) + else { - if (_currentRetry <= 0) - _currentRetry = _maxRetry + 1; - - _currentRetry--; - - if (_currentRetry > 0) - Warning(_log, "The serial device is not ready... will try to reconnect (try %i/%i).", (_maxRetry - _currentRetry + 1), _maxRetry); - else - Error(_log, "The serial device is not ready... give up."); - - if (_currentRetry > 0 && !_signalTerminate) - { - _retryMode = true; - QTimer::singleShot(2000, [this]() { _retryMode = false; if (_currentRetry > 0 && !_signalTerminate) enableDevice(true); }); - } + setupRetry(2500); } return retval; } -void ProviderRs232::waitForExitStats(bool force) +bool ProviderSerial::waitForExitStats() { - if (_rs232Port.isOpen()) + if (_serialPort->isOpen()) { - if (!force && _rs232Port.bytesAvailable() > 32) + if (_serialPort->bytesAvailable() > 32) { - auto check = _rs232Port.peek(256); + auto check = _serialPort->peek(256); if (check.lastIndexOf('\n') > 1) { - auto incoming = QString(_rs232Port.readAll()); - force = true; + auto incoming = QString(_serialPort->readAll()); - Info(_log, "Received: '%s' (%i)", QSTRING_CSTR(incoming), incoming.length()); + Info(_log, "Received goodbye: '%s' (%i)", QSTRING_CSTR(incoming), incoming.length()); + return true; } - } - - if (!_isDeviceReady && force) - { - Debug(_log, "Close UART: %s", QSTRING_CSTR(_deviceName)); - disconnect(&_rs232Port, &QSerialPort::readyRead, nullptr, nullptr); - _rs232Port.close(); - } + } } + + return false; } -int ProviderRs232::close() +int ProviderSerial::close() { int retval = 0; _isDeviceReady = false; - // Test, if device requires closing - if (_rs232Port.isOpen()) + if (_serialPort != nullptr && _serialPort->isOpen()) { - if (_rs232Port.flush()) + if (_serialPort->flush()) { Debug(_log, "Flush was successful"); } @@ -160,29 +146,25 @@ int ProviderRs232::close() if (_espHandshake) { - QTimer::singleShot(600, this, [this]() { waitForExitStats(true); }); - connect(&_rs232Port, &QSerialPort::readyRead, this, [this]() { waitForExitStats(false); }); - - - QTimer::singleShot(200, this, [this]() { if (_rs232Port.isOpen()) EspTools::goingSleep(_rs232Port); }); - EspTools::goingSleep(_rs232Port); + QTimer::singleShot(200, this, [this]() { if (_serialPort->isOpen()) EspTools::goingSleep(_serialPort); }); + EspTools::goingSleep(_serialPort); - for (int i = 0; i < 6 && _rs232Port.isOpen(); i++) + for (int i = 0; i < 6 && _serialPort->isOpen(); i++) { - _rs232Port.waitForReadyRead(100); + if (_serialPort->waitForReadyRead(100) && waitForExitStats()) + break; } } - else - { - Debug(_log, "Close UART: %s", QSTRING_CSTR(_deviceName)); - _rs232Port.close(); - } + + _serialPort->close(); + + Debug(_log, "Serial port is closed: %s", QSTRING_CSTR(_deviceName)); } return retval; } -bool ProviderRs232::powerOff() +bool ProviderSerial::powerOff() { // Simulate power-off by writing a final "Black" to have a defined outcome bool rc = false; @@ -193,11 +175,11 @@ bool ProviderRs232::powerOff() return rc; } -bool ProviderRs232::tryOpen(int delayAfterConnect_ms) +bool ProviderSerial::tryOpen(int delayAfterConnect_ms) { - if (_deviceName.isEmpty() || _rs232Port.portName().isEmpty() || (_isAutoDeviceName && _espHandshake)) + if (_deviceName.isEmpty() || _serialPort->portName().isEmpty() || (_isAutoDeviceName && _espHandshake)) { - if (!_rs232Port.isOpen()) + if (!_serialPort->isOpen()) { if (_isAutoDeviceName) { @@ -210,18 +192,18 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms) } } - _rs232Port.setPortName(_deviceName); + _serialPort->setPortName(_deviceName); } - if (!_rs232Port.isOpen()) + if (!_serialPort->isOpen()) { Info(_log, "Opening UART: %s", QSTRING_CSTR(_deviceName)); _frameDropCounter = 0; - _rs232Port.setBaudRate(_baudRate_Hz); + _serialPort->setBaudRate(_baudRate_Hz); - Debug(_log, "_rs232Port.open(QIODevice::ReadWrite): %s, Baud rate [%d]bps", QSTRING_CSTR(_deviceName), _baudRate_Hz); + Debug(_log, "_serialPort.open(QIODevice::ReadWrite): %s, Baud rate [%d]bps", QSTRING_CSTR(_deviceName), _baudRate_Hz); QSerialPortInfo serialPortInfo(_deviceName); @@ -236,17 +218,17 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms) if (!serialPortInfo.isNull()) { - if (!_rs232Port.isOpen() && !_rs232Port.open(QIODevice::ReadWrite)) + if (!_serialPort->isOpen() && !_serialPort->open(QIODevice::ReadWrite)) { - this->setInError(_rs232Port.errorString()); + this->setInError(_serialPort->errorString()); return false; } if (_espHandshake) { - disconnect(&_rs232Port, &QSerialPort::readyRead, nullptr, nullptr); + disconnect(_serialPort, &QSerialPort::readyRead, nullptr, nullptr); - EspTools::initializeEsp(_rs232Port, serialPortInfo, _log); + EspTools::initializeEsp(_serialPort, serialPortInfo, _log); } } else @@ -270,40 +252,40 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms) Debug(_log, "delayAfterConnect for %d ms - finished", delayAfterConnect_ms); } - return _rs232Port.isOpen(); + return _serialPort->isOpen(); } -void ProviderRs232::setInError(const QString& errorMsg) +void ProviderSerial::setInError(const QString& errorMsg) { - _rs232Port.clearError(); + _serialPort->clearError(); this->close(); LedDevice::setInError(errorMsg); } -int ProviderRs232::writeBytes(const qint64 size, const uint8_t* data) +int ProviderSerial::writeBytes(const qint64 size, const uint8_t* data) { int rc = 0; - if (!_rs232Port.isOpen()) + if (!_serialPort->isOpen()) { - Debug(_log, "!_rs232Port.isOpen()"); + Debug(_log, "!_serialPort.isOpen()"); if (!tryOpen(OPEN_TIMEOUT.count())) { return -1; } } - qint64 bytesWritten = _rs232Port.write(reinterpret_cast(data), size); + qint64 bytesWritten = _serialPort->write(reinterpret_cast(data), size); if (bytesWritten == -1 || bytesWritten != size) { - this->setInError(QString("Rs232 SerialPortError: %1").arg(_rs232Port.errorString())); + this->setInError(QString("Serial port error: %1").arg(_serialPort->errorString())); rc = -1; } else { - if (!_rs232Port.waitForBytesWritten(WRITE_TIMEOUT.count())) + if (!_serialPort->waitForBytesWritten(WRITE_TIMEOUT.count())) { - if (_rs232Port.error() == QSerialPort::TimeoutError) + if (_serialPort->error() == QSerialPort::TimeoutError) { Debug(_log, "Timeout after %dms: %d frames already dropped", WRITE_TIMEOUT, _frameDropCounter); @@ -319,12 +301,12 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t* data) else { //give it another try - _rs232Port.clearError(); + _serialPort->clearError(); } } else { - this->setInError(QString("Rs232 SerialPortError: %1").arg(_rs232Port.errorString())); + this->setInError(QString("Rs232 SerialPortError: %1").arg(_serialPort->errorString())); rc = -1; } } @@ -332,13 +314,13 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t* data) if (_maxRetry > 0 && rc == -1 && !_signalTerminate) { - QTimer::singleShot(2000, this, [=]() { if (!_signalTerminate) enable(); }); + QTimer::singleShot(2000, this, [this]() { if (!_signalTerminate) enable(); }); } return rc; } -QString ProviderRs232::discoverFirst() +QString ProviderSerial::discoverFirst() { for (int round = 0; round < 4; round++) for (auto const& port : QSerialPortInfo::availablePorts()) @@ -376,7 +358,7 @@ QString ProviderRs232::discoverFirst() return ""; } -QJsonObject ProviderRs232::discover(const QJsonObject& /*params*/) +QJsonObject ProviderSerial::discover(const QJsonObject& /*params*/) { QJsonObject devicesDiscovered; QJsonArray deviceList; @@ -417,7 +399,7 @@ QJsonObject ProviderRs232::discover(const QJsonObject& /*params*/) newRecord.hostName = info.description(); newRecord.address = info.portName(); newRecord.isExists = true; - emit DiscoveryWrapper::getInstance()->discoveryEvent(newRecord); + emit GlobalSignals::getInstance()->SignalDiscoveryEvent(newRecord); } #endif } diff --git a/sources/leddevice/dev_serial/ProviderSerial.h b/sources/leddevice/dev_serial/ProviderSerial.h new file mode 100644 index 000000000..202b2c706 --- /dev/null +++ b/sources/leddevice/dev_serial/ProviderSerial.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +class QSerialPort; + +class ProviderSerial : public LedDevice +{ + Q_OBJECT + +public: + ProviderSerial(const QJsonObject& deviceConfig); + ~ProviderSerial() override; + +protected: + + bool init(const QJsonObject& deviceConfig) override; + int open() override; + int close() override; + bool powerOff() override; + QString discoverFirst() override; + QJsonObject discover(const QJsonObject& params) override; + int writeBytes(const qint64 size, const uint8_t* data); + + QString _deviceName; + QSerialPort* _serialPort; + qint32 _baudRate_Hz; + +protected slots: + void setInError(const QString& errorMsg) override; + +public slots: + bool waitForExitStats(); + +private: + bool tryOpen(int delayAfterConnect_ms); + bool _isAutoDeviceName; + int _delayAfterConnect_ms; + int _frameDropCounter; + bool _espHandshake; +}; diff --git a/sources/leddevice/dev_spi/LedDeviceAPA102.cpp b/sources/leddevice/dev_spi/LedDeviceAPA102.cpp index 9f5dd5512..a6a3aa172 100644 --- a/sources/leddevice/dev_spi/LedDeviceAPA102.cpp +++ b/sources/leddevice/dev_spi/LedDeviceAPA102.cpp @@ -17,13 +17,13 @@ bool LedDeviceAPA102::init(const QJsonObject& deviceConfig) // Initialise sub-class if (ProviderSpi::init(deviceConfig)) { - CreateHeader(); + createHeader(); isInitOK = true; } return isInitOK; } -void LedDeviceAPA102::CreateHeader() +void LedDeviceAPA102::createHeader() { const unsigned int startFrameSize = 4; const unsigned int endFrameSize = qMax(((_ledCount + 15) / 16), 4); @@ -46,7 +46,7 @@ int LedDeviceAPA102::write(const std::vector& ledValues) Warning(_log, "APA102 led's number has changed (old: %d, new: %d). Rebuilding buffer.", _ledCount, ledValues.size()); _ledCount = ledValues.size(); - CreateHeader(); + createHeader(); } for (signed iLed = 0; iLed < static_cast(_ledCount); ++iLed) { diff --git a/sources/leddevice/dev_spi/LedDeviceAPA102.h b/sources/leddevice/dev_spi/LedDeviceAPA102.h index 0c379824b..34a359250 100644 --- a/sources/leddevice/dev_spi/LedDeviceAPA102.h +++ b/sources/leddevice/dev_spi/LedDeviceAPA102.h @@ -1,50 +1,17 @@ -#ifndef LEDEVICEAPA102_H -#define LEDEVICEAPA102_H +#pragma once // HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to APA102 led device. -/// class LedDeviceAPA102 : public ProviderSpi { public: - - /// - /// @brief Constructs an APA102 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceAPA102(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - void CreateHeader(); + void createHeader(); }; - -#endif // LEDEVICEAPA102_H diff --git a/sources/leddevice/dev_spi/LedDeviceAPA104.h b/sources/leddevice/dev_spi/LedDeviceAPA104.h index 5fc2ea572..1d7cf5d84 100644 --- a/sources/leddevice/dev_spi/LedDeviceAPA104.h +++ b/sources/leddevice/dev_spi/LedDeviceAPA104.h @@ -1,46 +1,16 @@ -#ifndef LEDEVICEAPA104_H -#define LEDEVICEAPA104_H +#pragma once // HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to APA104 led device via spi. -/// class LedDeviceAPA104 : public ProviderSpi { public: - - /// - /// @brief Constructs an APA104 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceAPA104(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; const int SPI_BYTES_PER_COLOUR; @@ -48,5 +18,3 @@ class LedDeviceAPA104 : public ProviderSpi uint8_t bitpair_to_byte[4]; }; - -#endif // LEDEVICEAPA104_H diff --git a/sources/leddevice/dev_spi/LedDeviceAWA_spi.cpp b/sources/leddevice/dev_spi/LedDeviceAWA_spi.cpp index a27329e26..fb46395a9 100644 --- a/sources/leddevice/dev_spi/LedDeviceAWA_spi.cpp +++ b/sources/leddevice/dev_spi/LedDeviceAWA_spi.cpp @@ -31,7 +31,7 @@ bool LedDeviceAWA_spi::init(const QJsonObject& deviceConfig) _white_channel_green = qMin(deviceConfig["white_channel_green"].toInt(255), 255); _white_channel_blue = qMin(deviceConfig["white_channel_blue"].toInt(255), 255); - CreateHeader(); + createHeader(); if (_white_channel_calibration) Debug(_log, "White channel limit: %i, red: %i, green: %i, blue: %i", _white_channel_limit, _white_channel_red, _white_channel_green, _white_channel_blue); @@ -42,7 +42,7 @@ bool LedDeviceAWA_spi::init(const QJsonObject& deviceConfig) return isInitOK; } -void LedDeviceAWA_spi::CreateHeader() +void LedDeviceAWA_spi::createHeader() { unsigned int totalLedCount = _ledCount - 1; @@ -69,7 +69,7 @@ int LedDeviceAWA_spi::write(const std::vector& ledValues) _ledCount = ledValues.size(); - CreateHeader(); + createHeader(); } auto bufferLength = _headerSize + ledValues.size() * sizeof(ColorRgb) + 8; @@ -106,6 +106,8 @@ int LedDeviceAWA_spi::write(const std::vector& ledValues) return writeBytesEsp8266(bufferLength, _ledBuffer.data()); else if (_spiType == "esp32") return writeBytesEsp32(bufferLength, _ledBuffer.data()); + else if (_spiType == "rp2040") + return writeBytesRp2040(bufferLength, _ledBuffer.data()); else return writeBytes(bufferLength, _ledBuffer.data()); } diff --git a/sources/leddevice/dev_spi/LedDeviceAWA_spi.h b/sources/leddevice/dev_spi/LedDeviceAWA_spi.h index 30ee34573..a1edc6b74 100644 --- a/sources/leddevice/dev_spi/LedDeviceAWA_spi.h +++ b/sources/leddevice/dev_spi/LedDeviceAWA_spi.h @@ -1,59 +1,24 @@ -#ifndef LEDEVICEAWA_SPI_H -#define LEDEVICEAWA_SPI_H +#pragma once // HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to AWA SPI led device. -/// class LedDeviceAWA_spi : public ProviderSpi { public: - - /// - /// @brief Constructs an AWA SPI LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceAWA_spi(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - void CreateHeader(); - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// + void createHeader(); bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - void whiteChannelExtension(uint8_t*& writer); int _headerSize; - bool _white_channel_calibration; uint8_t _white_channel_limit; uint8_t _white_channel_red; uint8_t _white_channel_green; uint8_t _white_channel_blue; }; - -#endif // LEDEVICEAPA102_H diff --git a/sources/leddevice/dev_spi/LedDeviceHD108.cpp b/sources/leddevice/dev_spi/LedDeviceHD108.cpp index 20ff5c999..9e41abe76 100644 --- a/sources/leddevice/dev_spi/LedDeviceHD108.cpp +++ b/sources/leddevice/dev_spi/LedDeviceHD108.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * diff --git a/sources/leddevice/dev_spi/LedDeviceHD108.h b/sources/leddevice/dev_spi/LedDeviceHD108.h index 5ab0af43b..08488d4e5 100644 --- a/sources/leddevice/dev_spi/LedDeviceHD108.h +++ b/sources/leddevice/dev_spi/LedDeviceHD108.h @@ -1,8 +1,10 @@ +#pragma once + /* LedDeviceHD108.h * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,20 +27,13 @@ * SOFTWARE. */ -#ifndef LEDEVICEHD108_H -#define LEDEVICEHD108_H - // HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to LedDeviceHD108 led device via SPI. -/// class LedDeviceHD108 : public ProviderSpi { public: explicit LedDeviceHD108(const QJsonObject& deviceConfig); - static LedDevice* construct(const QJsonObject& deviceConfig); private: @@ -46,8 +41,5 @@ class LedDeviceHD108 : public ProviderSpi uint16_t _globalBrightnessControlMaxLevel; bool init(const QJsonObject& deviceConfig) override; - int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICEHD108_H diff --git a/sources/leddevice/dev_spi/LedDeviceLpd6803.cpp b/sources/leddevice/dev_spi/LedDeviceLpd6803.cpp index bdc6a53e0..645d0a5c0 100644 --- a/sources/leddevice/dev_spi/LedDeviceLpd6803.cpp +++ b/sources/leddevice/dev_spi/LedDeviceLpd6803.cpp @@ -1,5 +1,16 @@ #include "LedDeviceLpd6803.h" +/// +/// Implementation of the LedDevice interface for writing to LDP6803 LED-device. +/// +/// 00000000 00000000 00000000 00000000 1RRRRRGG GGGBBBBB 1RRRRRGG GGGBBBBB ... +/// |---------------------------------| |---------------| |---------------| +/// 32 zeros to start the frame LED1 LED2 ... +/// +/// For each led, the first bit is always 1, and then you have 5 bits each for red, green and blue +/// (R, G and B in the above illustration) making 16 bits per led. Total bytes = 4 + (2 x number of LEDs) +/// + LedDeviceLpd6803::LedDeviceLpd6803(const QJsonObject& deviceConfig) : ProviderSpi(deviceConfig) { diff --git a/sources/leddevice/dev_spi/LedDeviceLpd6803.h b/sources/leddevice/dev_spi/LedDeviceLpd6803.h index 0cb85cbaa..6a6600865 100644 --- a/sources/leddevice/dev_spi/LedDeviceLpd6803.h +++ b/sources/leddevice/dev_spi/LedDeviceLpd6803.h @@ -1,55 +1,15 @@ -#ifndef LEDEVICELPD6803_H -#define LEDEVICELPD6803_H +#pragma once // Local HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to LDP6803 LED-device. -/// -/// 00000000 00000000 00000000 00000000 1RRRRRGG GGGBBBBB 1RRRRRGG GGGBBBBB ... -/// |---------------------------------| |---------------| |---------------| -/// 32 zeros to start the frame LED1 LED2 ... -/// -/// For each led, the first bit is always 1, and then you have 5 bits each for red, green and blue -/// (R, G and B in the above illustration) making 16 bits per led. Total bytes = 4 + (2 x number of LEDs) -/// class LedDeviceLpd6803 : public ProviderSpi { public: - - /// - /// @brief Constructs a LDP6803 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceLpd6803(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICELPD6803_H diff --git a/sources/leddevice/dev_spi/LedDeviceLpd8806.cpp b/sources/leddevice/dev_spi/LedDeviceLpd8806.cpp index 1aa10fe06..078575ced 100644 --- a/sources/leddevice/dev_spi/LedDeviceLpd8806.cpp +++ b/sources/leddevice/dev_spi/LedDeviceLpd8806.cpp @@ -1,5 +1,78 @@ #include "LedDeviceLpd8806.h" +/// +/// Implementation of the LedDevice interface for writing to LPD8806 led device. +/// +/// The following description is copied from 'adafruit' (github.com/adafruit/LPD8806) +/// +/// Clearing up some misconceptions about how the LPD8806 drivers work: +/// +/// The LPD8806 is not a FIFO shift register. The first data out controls the +/// LED *closest* to the processor (unlike a typical shift register, where the +/// first data out winds up at the *furthest* LED). Each LED driver 'fills up' +/// with data and then passes through all subsequent bytes until a latch +/// condition takes place. This is actually pretty common among LED drivers. +/// +/// All color data bytes have the high bit (128) set, with the remaining +/// seven bits containing a brightness value (0-127). A byte with the high +/// bit clear has special meaning (explained later). +/// +/// The rest gets bizarre... +/// +/// The LPD8806 does not perform an in-unison latch (which would display the +/// newly-transmitted data all at once). Rather, each individual byte (even +/// the separate G, R, B components of each LED) is latched AS IT ARRIVES... +/// or more accurately, as the first bit of the subsequent byte arrives and +/// is passed through. So the strip actually refreshes at the speed the data +/// is issued, not instantaneously (this can be observed by greatly reducing +/// the data rate). This has implications for POV displays and light painting +/// applications. The 'subsequent' rule also means that at least one extra +/// byte must follow the last pixel, in order for the final blue LED to latch. +/// +/// To reset the pass-through behaviour and begin sending new data to the start +/// of the strip, a number of zero bytes must be issued (remember, all color +/// data bytes have the high bit set, thus are in the range 128 to 255, so the +/// zero is 'special'). This should be done before each full payload of color +/// values to the strip. Curiously, zero bytes can only travel one meter (32 +/// LEDs) down the line before needing backup; the next meter requires an +/// extra zero byte, and so forth. Longer strips will require progressively +/// more zeros. *(see note below) +/// +/// In the interest of efficiency, it's possible to combine the former EOD +/// extra latch byte and the latter zero reset...the same data can do double +/// duty, latching the last blue LED while also resetting the strip for the +/// next payload. +/// +/// So: reset byte(s) of suitable length are issued once at startup to 'prime' +/// the strip to a known ready state. After each subsequent LED color payload, +/// these reset byte(s) are then issued at the END of each payload, both to +/// latch the last LED and to prep the strip for the start of the next payload +/// (even if that data does not arrive immediately). This avoids a tiny bit +/// of latency as the new color payload can begin issuing immediately on some +/// signal, such as a timer or GPIO trigger. +/// +/// Technically these zero byte(s) are not a latch, as the color data (save +/// for the last byte) is already latched. It's a start-of-data marker, or +/// an indicator to clear the thing-that's-not-a-shift-register. But for +/// conversational consistency with other LED drivers, we'll refer to it as +/// a 'latch' anyway. +/// +/// This has been validated independently with multiple customers' +/// hardware. Please do not report as a bug or issue pull requests for +/// this. Fewer zeros sometimes gives the *illusion* of working, the first +/// payload will correctly load and latch, but subsequent frames will drop +/// data at the end. The data shortfall won't always be visually apparent +/// depending on the color data loaded on the prior and subsequent frames. +/// Tested. Confirmed. Fact. +/// +/// +/// The summary of the story is that the following needs to be written on the spi-device: +/// 1RRRRRRR 1GGGGGGG 1BBBBBBB 1RRRRRRR 1GGGGGGG ... ... 1GGGGGGG 1BBBBBBB 00000000 00000000 ... +/// |---------led_1----------| |---------led_2-- -led_n----------| |----clear data-- +/// +/// The number of zeroes in the 'clear data' is (#led/32 + 1)bytes (or *8 for bits) +/// + LedDeviceLpd8806::LedDeviceLpd8806(const QJsonObject& deviceConfig) : ProviderSpi(deviceConfig) { diff --git a/sources/leddevice/dev_spi/LedDeviceLpd8806.h b/sources/leddevice/dev_spi/LedDeviceLpd8806.h index dcb601f5b..130da6f9f 100644 --- a/sources/leddevice/dev_spi/LedDeviceLpd8806.h +++ b/sources/leddevice/dev_spi/LedDeviceLpd8806.h @@ -1,117 +1,15 @@ -#ifndef LEDEVICELPD8806_H -#define LEDEVICELPD8806_H +#pragma once // Local HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to LPD8806 led device. -/// -/// The following description is copied from 'adafruit' (github.com/adafruit/LPD8806) -/// -/// Clearing up some misconceptions about how the LPD8806 drivers work: -/// -/// The LPD8806 is not a FIFO shift register. The first data out controls the -/// LED *closest* to the processor (unlike a typical shift register, where the -/// first data out winds up at the *furthest* LED). Each LED driver 'fills up' -/// with data and then passes through all subsequent bytes until a latch -/// condition takes place. This is actually pretty common among LED drivers. -/// -/// All color data bytes have the high bit (128) set, with the remaining -/// seven bits containing a brightness value (0-127). A byte with the high -/// bit clear has special meaning (explained later). -/// -/// The rest gets bizarre... -/// -/// The LPD8806 does not perform an in-unison latch (which would display the -/// newly-transmitted data all at once). Rather, each individual byte (even -/// the separate G, R, B components of each LED) is latched AS IT ARRIVES... -/// or more accurately, as the first bit of the subsequent byte arrives and -/// is passed through. So the strip actually refreshes at the speed the data -/// is issued, not instantaneously (this can be observed by greatly reducing -/// the data rate). This has implications for POV displays and light painting -/// applications. The 'subsequent' rule also means that at least one extra -/// byte must follow the last pixel, in order for the final blue LED to latch. -/// -/// To reset the pass-through behaviour and begin sending new data to the start -/// of the strip, a number of zero bytes must be issued (remember, all color -/// data bytes have the high bit set, thus are in the range 128 to 255, so the -/// zero is 'special'). This should be done before each full payload of color -/// values to the strip. Curiously, zero bytes can only travel one meter (32 -/// LEDs) down the line before needing backup; the next meter requires an -/// extra zero byte, and so forth. Longer strips will require progressively -/// more zeros. *(see note below) -/// -/// In the interest of efficiency, it's possible to combine the former EOD -/// extra latch byte and the latter zero reset...the same data can do double -/// duty, latching the last blue LED while also resetting the strip for the -/// next payload. -/// -/// So: reset byte(s) of suitable length are issued once at startup to 'prime' -/// the strip to a known ready state. After each subsequent LED color payload, -/// these reset byte(s) are then issued at the END of each payload, both to -/// latch the last LED and to prep the strip for the start of the next payload -/// (even if that data does not arrive immediately). This avoids a tiny bit -/// of latency as the new color payload can begin issuing immediately on some -/// signal, such as a timer or GPIO trigger. -/// -/// Technically these zero byte(s) are not a latch, as the color data (save -/// for the last byte) is already latched. It's a start-of-data marker, or -/// an indicator to clear the thing-that's-not-a-shift-register. But for -/// conversational consistency with other LED drivers, we'll refer to it as -/// a 'latch' anyway. -/// -/// This has been validated independently with multiple customers' -/// hardware. Please do not report as a bug or issue pull requests for -/// this. Fewer zeros sometimes gives the *illusion* of working, the first -/// payload will correctly load and latch, but subsequent frames will drop -/// data at the end. The data shortfall won't always be visually apparent -/// depending on the color data loaded on the prior and subsequent frames. -/// Tested. Confirmed. Fact. -/// -/// -/// The summary of the story is that the following needs to be written on the spi-device: -/// 1RRRRRRR 1GGGGGGG 1BBBBBBB 1RRRRRRR 1GGGGGGG ... ... 1GGGGGGG 1BBBBBBB 00000000 00000000 ... -/// |---------led_1----------| |---------led_2-- -led_n----------| |----clear data-- -/// -/// The number of zeroes in the 'clear data' is (#led/32 + 1)bytes (or *8 for bits) -/// class LedDeviceLpd8806 : public ProviderSpi { public: - - /// - /// @brief Constructs a LDP8806 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceLpd8806(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICELPD8806_H diff --git a/sources/leddevice/dev_spi/LedDeviceP9813.h b/sources/leddevice/dev_spi/LedDeviceP9813.h index 9ed3646e4..fb098a5ff 100644 --- a/sources/leddevice/dev_spi/LedDeviceP9813.h +++ b/sources/leddevice/dev_spi/LedDeviceP9813.h @@ -1,54 +1,16 @@ -#ifndef LEDEVICEP9813_H -#define LEDEVICEP9813_H +#pragma once // HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to P9813 LED-device. -/// class LedDeviceP9813 : public ProviderSpi { public: - /// - /// @brief Constructs a P9813 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceP9813(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; - - /// - /// Calculates the required checksum for one LED - /// - /// @param color The color of the led - /// @return The checksum for the led - /// uint8_t calculateChecksum(const ColorRgb& color) const; }; - -#endif // LEDEVICEP9813_H diff --git a/sources/leddevice/dev_spi/LedDeviceSK9822.cpp b/sources/leddevice/dev_spi/LedDeviceSK9822.cpp index 4cdf4a288..30d178991 100644 --- a/sources/leddevice/dev_spi/LedDeviceSK9822.cpp +++ b/sources/leddevice/dev_spi/LedDeviceSK9822.cpp @@ -3,12 +3,14 @@ // Local HyperHDR includes #include +namespace +{ + /// The value that determines the higher bits of the SK9822 global brightness control field + const int SK9822_GBC_UPPER_BITS = 0xE0; -/// The value that determines the higher bits of the SK9822 global brightness control field -const int SK9822_GBC_UPPER_BITS = 0xE0; - -/// The maximal current level supported by the SK9822 global brightness control field, 31 -const int SK9822_GBC_MAX_LEVEL = 0x1F; + /// The maximal current level supported by the SK9822 global brightness control field, 31 + const int SK9822_GBC_MAX_LEVEL = 0x1F; +} LedDeviceSK9822::LedDeviceSK9822(const QJsonObject& deviceConfig) : ProviderSpi(deviceConfig) diff --git a/sources/leddevice/dev_spi/LedDeviceSK9822.h b/sources/leddevice/dev_spi/LedDeviceSK9822.h index 6de7d8ee6..ed05b1430 100644 --- a/sources/leddevice/dev_spi/LedDeviceSK9822.h +++ b/sources/leddevice/dev_spi/LedDeviceSK9822.h @@ -1,83 +1,20 @@ -#ifndef LEDEVICESK9822_H -#define LEDEVICESK9822_H +#pragma once -// HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to SK9822 led device via SPI. -/// class LedDeviceSK9822 : public ProviderSpi { public: - - /// - /// @brief Constructs an SK9822 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceSK9822(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed - /// static LedDevice* construct(const QJsonObject& deviceConfig); private: - /// - /// @brief Writes the RGB-Color values to the SPI Tx buffer setting SK9822 current level to maximal value. - /// - /// @param[in,out] txBuf The packed spi transfer buffer of the LED's color values - /// @param[in] ledValues The RGB-color per LED - /// @param[in] maxLevel The maximal current level 1 .. 31 to use - /// void bufferWithMaxCurrent(std::vector& txBuf, const std::vector& ledValues, const int maxLevel); - - /// - /// @brief Writes the RGB-Color values to the SPI Tx buffer using an adjusted SK9822 current level for LED maximal rgb-grayscale values not exceeding the threshold, uses maximal level otherwise. - /// - /// @param[in,out] txBuf The packed spi transfer buffer of the LED's color values - /// @param[in] ledValues The RGB-color per LED - /// @param[in] threshold The threshold 0 .. 255 that defines whether to use adjusted SK9822 current level per LED - /// @param[in] maxLevel The maximal current level 1 .. 31 to use - /// void bufferWithAdjustedCurrent(std::vector& txBuf, const std::vector& ledValues, const int threshold, const int maxLevel); - /// The threshold that defines use of SK9822 global brightness control for maximal rgb grayscale values below. - /// i.e. global brightness control is used for rgb-values when max(r,g,b) < threshold. int _globalBrightnessControlThreshold; - - /// The maximal current level that is targeted. Possibile values 1 .. 31. int _globalBrightnessControlMaxLevel; - - /// - /// @brief Scales the given value such that a given grayscale stimulus is reached for the targeted brightness and defined max current value. - /// - /// @param[in] value The grayscale value to scale - /// @param[in] maxLevel The maximal current level 1 .. 31 to use - /// @param[in] brightness The target brightness - /// @return The scaled grayscale stimulus - /// inline __attribute__((always_inline)) unsigned scale(const uint8_t value, const int maxLevel, const uint16_t brightness); - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICESK9822_H diff --git a/sources/leddevice/dev_spi/LedDeviceSk6812SPI.h b/sources/leddevice/dev_spi/LedDeviceSk6812SPI.h index 78894e45f..b1a9d0893 100644 --- a/sources/leddevice/dev_spi/LedDeviceSk6812SPI.h +++ b/sources/leddevice/dev_spi/LedDeviceSk6812SPI.h @@ -1,46 +1,16 @@ -#ifndef LEDEVICESK6812SPI_H -#define LEDEVICESK6812SPI_H +#pragma once // HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to Sk6801 LED-device via SPI. -/// class LedDeviceSk6812SPI : public ProviderSpi { public: - - /// - /// @brief Constructs a Sk6801 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceSk6812SPI(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; RGBW::WhiteAlgorithm _whiteAlgorithm; @@ -50,5 +20,3 @@ class LedDeviceSk6812SPI : public ProviderSpi ColorRgbw _temp_rgbw; }; - -#endif // LEDEVICESK6812SPI_H diff --git a/sources/leddevice/dev_spi/LedDeviceSk6822SPI.h b/sources/leddevice/dev_spi/LedDeviceSk6822SPI.h index 4cea63936..89b58f100 100644 --- a/sources/leddevice/dev_spi/LedDeviceSk6822SPI.h +++ b/sources/leddevice/dev_spi/LedDeviceSk6822SPI.h @@ -1,46 +1,16 @@ -#ifndef LEDEVICESK6822SPI_H -#define LEDEVICESK6822SPI_H +#pragma once // HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to Sk6822 LED-device via SPI. -/// class LedDeviceSk6822SPI : public ProviderSpi { public: - - /// - /// @brief Constructs a Sk6822 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceSk6822SPI(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; const int SPI_BYTES_PER_COLOUR; @@ -49,5 +19,3 @@ class LedDeviceSk6822SPI : public ProviderSpi uint8_t bitpair_to_byte[4]; }; - -#endif // LEDEVICESK6822SPI_H diff --git a/sources/leddevice/dev_spi/LedDeviceWs2801.h b/sources/leddevice/dev_spi/LedDeviceWs2801.h index 81bddcf8c..087719ae8 100644 --- a/sources/leddevice/dev_spi/LedDeviceWs2801.h +++ b/sources/leddevice/dev_spi/LedDeviceWs2801.h @@ -1,47 +1,14 @@ -#ifndef LEDEVICEWS2801_H -#define LEDEVICEWS2801_H +#pragma once -// HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to Ws2801 led device. -/// class LedDeviceWs2801 : public ProviderSpi { public: - - /// - /// @brief Constructs a Ws2801 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceWs2801(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; }; - -#endif // LEDEVICEWS2801_H diff --git a/sources/leddevice/dev_spi/LedDeviceWs2812SPI.h b/sources/leddevice/dev_spi/LedDeviceWs2812SPI.h index 494ad5a11..e0c475982 100644 --- a/sources/leddevice/dev_spi/LedDeviceWs2812SPI.h +++ b/sources/leddevice/dev_spi/LedDeviceWs2812SPI.h @@ -1,46 +1,15 @@ -#ifndef LEDEVICEWS2812_H -#define LEDEVICEWS2812_H +#pragma once -// HyperHDR includes #include "ProviderSpi.h" -/// -/// Implementation of the LedDevice interface for writing to Ws2812 led device. -/// class LedDeviceWs2812SPI : public ProviderSpi { public: - - /// - /// @brief Constructs a Ws2812 LED-device - /// - /// @param deviceConfig Device's configuration as JSON-Object - /// explicit LedDeviceWs2812SPI(const QJsonObject& deviceConfig); - - /// - /// @brief Constructs the LED-device - /// - /// @param[in] deviceConfig Device's configuration as JSON-Object - /// @return LedDevice constructed static LedDevice* construct(const QJsonObject& deviceConfig); private: - - /// - /// @brief Initialise the device's configuration - /// - /// @param[in] deviceConfig the JSON device configuration - /// @return True, if success - /// bool init(const QJsonObject& deviceConfig) override; - - /// - /// @brief Writes the RGB-Color values to the LEDs. - /// - /// @param[in] ledValues The RGB-color per LED - /// @return Zero on success, else negative - /// int write(const std::vector& ledValues) override; const int SPI_BYTES_PER_COLOUR; @@ -48,5 +17,3 @@ class LedDeviceWs2812SPI : public ProviderSpi uint8_t bitpair_to_byte[4]; }; - -#endif // LEDEVICEWS2812_H diff --git a/sources/leddevice/dev_spi/ProviderSpi.cpp b/sources/leddevice/dev_spi/ProviderSpi.cpp index e108d4267..df74e9561 100644 --- a/sources/leddevice/dev_spi/ProviderSpi.cpp +++ b/sources/leddevice/dev_spi/ProviderSpi.cpp @@ -25,7 +25,6 @@ ProviderSpi::ProviderSpi(const QJsonObject& deviceConfig) , _spiDataInvert(false) , _spiType("") { - memset(&_spi, 0, sizeof(_spi)); } ProviderSpi::~ProviderSpi() @@ -45,10 +44,15 @@ bool ProviderSpi::init(const QJsonObject& deviceConfig) _spiMode = deviceConfig["spimode"].toInt(_spiMode); _spiDataInvert = deviceConfig["invert"].toBool(_spiDataInvert); - Debug(_log, "_baudRate_Hz [%d], _spiType: %s", _baudRate_Hz, QSTRING_CSTR(_spiType)); - Debug(_log, "_spiDataInvert [%d], _spiMode [%d]", _spiDataInvert, _spiMode); + if (_spiType == "rp2040" && _baudRate_Hz > 20833333) + { + _baudRate_Hz = 20833333; + } + + Debug(_log, "Speed: %d, Type: %s", _baudRate_Hz, QSTRING_CSTR(_spiType)); + Debug(_log, "Inverted: %s, Mode: %d", (_spiDataInvert) ? "yes" : "no", _spiMode); - if (_refreshTimerInterval_ms > 0) + if (_defaultInterval > 0) Error(_log, "The refresh timer is enabled ('Refresh time' > 0) and may limit the performance of the LED driver. Ignore this error if you set it on purpose for some reason (but you almost never need it)."); isInitOK = true; @@ -91,7 +95,17 @@ int ProviderSpi::open() } else { - // Everything OK -> enable device + uint8_t rpBuffer[] = { 0x41, 0x77, 0x41, 0x2a, 0xa2, 0x15, 0x68, 0x79, 0x70, 0x65, 0x72, 0x68, 0x64, 0x72 }; + + if (_spiType == "rp2040") + { + writeBytesRp2040(sizeof(rpBuffer), rpBuffer); + } + else if (_spiType == "esp32") + { + writeBytesEsp32(sizeof(rpBuffer), rpBuffer); + } + _isDeviceReady = true; retval = 0; } @@ -113,10 +127,18 @@ int ProviderSpi::open() int ProviderSpi::close() { - // LedDevice specific closing activities + uint8_t rpBuffer[] = { 0x41, 0x77, 0x41, 0x2a, 0xa2, 0x35, 0x68, 0x79, 0x70, 0x65, 0x72, 0x68, 0x64, 0x72 }; int retval = 0; + _isDeviceReady = false; + Debug(_log, "Closing SPI interface"); + + if (_spiType == "rp2040") + { + writeBytesRp2040(sizeof(rpBuffer), rpBuffer); + } + // Test, if device requires closing if (_fid > -1) { @@ -132,7 +154,10 @@ int ProviderSpi::close() int ProviderSpi::writeBytes(unsigned size, const uint8_t* data) { - uint8_t* newdata = nullptr; + MemoryBuffer buffer; + spi_ioc_transfer _spi; + + memset(&_spi, 0, sizeof(_spi)); if (_fid < 0) { @@ -144,19 +169,16 @@ int ProviderSpi::writeBytes(unsigned size, const uint8_t* data) if (_spiDataInvert) { - newdata = (uint8_t*)malloc(size); + buffer.resize(size); for (unsigned i = 0; i < size; i++) { - newdata[i] = data[i] ^ 0xff; + buffer.data()[i] = data[i] ^ 0xff; } - _spi.tx_buf = __u64(newdata); + _spi.tx_buf = __u64(buffer.data()); } int retVal = ioctl(_fid, SPI_IOC_MESSAGE(1), &_spi); ErrorIf((retVal < 0), _log, "SPI failed to write. errno: %d, %s", errno, strerror(errno)); - if (newdata != nullptr) - free(newdata); - return retVal; } @@ -165,6 +187,9 @@ int ProviderSpi::writeBytesEsp8266(unsigned size, const uint8_t* data) uint8_t* startData = (uint8_t*)data; uint8_t* endData = (uint8_t*)data + size; uint8_t buffer[34]; + spi_ioc_transfer _spi; + + memset(&_spi, 0, sizeof(_spi)); if (_fid < 0) { @@ -195,12 +220,15 @@ int ProviderSpi::writeBytesEsp8266(unsigned size, const uint8_t* data) int ProviderSpi::writeBytesEsp32(unsigned size, const uint8_t* data) { - static const int REAL_BUFFER = 1536; - static const uint32_t BUFFER_SIZE = REAL_BUFFER + 8; + const int REAL_BUFFER = 1536; + const uint32_t BUFFER_SIZE = REAL_BUFFER + 8; uint8_t* startData = (uint8_t*)data; uint8_t* endData = (uint8_t*)data + size; uint8_t buffer[BUFFER_SIZE]; + spi_ioc_transfer _spi; + + memset(&_spi, 0, sizeof(_spi)); if (_fid < 0) { @@ -215,11 +243,13 @@ int ProviderSpi::writeBytesEsp32(unsigned size, const uint8_t* data) while (retVal >= 0 && startData < endData) { + if (startData != data) + usleep(1000); + + int sent = std::min(REAL_BUFFER, static_cast(endData - startData)); memset(buffer, 0, sizeof(buffer)); - for (int i = 0; i < REAL_BUFFER && startData < endData; i++, startData++) - { - buffer[i] = *startData; - } + memcpy(buffer, startData, sent); + startData += sent; buffer[REAL_BUFFER] = 0xAA; retVal = ioctl(_fid, SPI_IOC_MESSAGE(1), &_spi); ErrorIf((retVal < 0), _log, "SPI failed to write. errno: %d, %s", errno, strerror(errno)); @@ -228,13 +258,52 @@ int ProviderSpi::writeBytesEsp32(unsigned size, const uint8_t* data) return retVal; } +int ProviderSpi::writeBytesRp2040(unsigned size, const uint8_t* data) +{ + static const int REAL_BUFFER = 1536; + static const uint32_t BUFFER_SIZE = REAL_BUFFER; + + uint8_t* startData = (uint8_t*)data; + uint8_t* endData = (uint8_t*)data + size; + uint8_t buffer[BUFFER_SIZE]; + spi_ioc_transfer _spi; + + memset(&_spi, 0, sizeof(_spi)); + + if (_fid < 0) + { + return -1; + } + + _spi.tx_buf = __u64(&buffer); + _spi.len = __u32(BUFFER_SIZE); + _spi.delay_usecs = 0; + + int retVal = 0; + + while (retVal >= 0 && startData < endData) + { + if (startData != data) + usleep(1000); + + int sent = std::min(REAL_BUFFER, static_cast(endData - startData)); + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, startData, sent); + startData += sent; + retVal = ioctl(_fid, SPI_IOC_MESSAGE(1), &_spi); + ErrorIf((retVal < 0), _log, "SPI failed to write. errno: %d, %s", errno, strerror(errno)); + } + + return retVal; +} + QJsonObject ProviderSpi::discover(const QJsonObject& /*params*/) { QJsonObject devicesDiscovered; QJsonArray deviceList; - QStringList files; + QStringList files; QDirIterator it("/dev", QStringList() << "spidev*", QDir::System); - + while (it.hasNext()) files << it.next(); files.sort(); diff --git a/sources/leddevice/dev_spi/ProviderSpi.h b/sources/leddevice/dev_spi/ProviderSpi.h index bf952ee7c..2d039fd89 100644 --- a/sources/leddevice/dev_spi/ProviderSpi.h +++ b/sources/leddevice/dev_spi/ProviderSpi.h @@ -1,85 +1,32 @@ #pragma once -// Linux-SPI includes #include - -// HyperHDR includes #include -/// -/// The ProviderSpi implements an abstract base-class for LedDevices using the SPI-device. -/// class ProviderSpi : public LedDevice { public: - /// - /// Constructs specific LedDevice - /// ProviderSpi(const QJsonObject& deviceConfig); - - /// - /// Sets configuration - /// - /// @param deviceConfig the json device config - /// @return true if success bool init(const QJsonObject& deviceConfig) override; - - /// - /// Destructor of the LedDevice; closes the output device if it is open - /// ~ProviderSpi() override; - - /// - /// Opens and configures the output device - /// - /// @return Zero on succes else negative - /// int open() override; QJsonObject discover(const QJsonObject& params) override; public slots: - /// - /// Closes the output device. - /// Includes switching-off the device and stopping refreshes - /// int close() override; protected: - /// - /// Writes the given bytes/bits to the SPI-device and sleeps the latch time to ensure that the - /// values are latched. - /// - /// @param[in[ size The length of the data - /// @param[in] data The data - /// - /// @return Zero on success, else negative - /// int writeBytes(unsigned size, const uint8_t* data); - - // esp spi is pripriotary protocol int writeBytesEsp8266(unsigned size, const uint8_t* data); - - // esp32 spi packet protocol int writeBytesEsp32(unsigned size, const uint8_t* data); + int writeBytesRp2040(unsigned size, const uint8_t* data); - /// The name of the output device QString _deviceName; - - /// The used baudrate of the output device int _baudRate_Hz; - - /// The File Identifier of the opened output device (or -1 if not opened) int _fid; - - /// which spi clock mode do we use? (0..3) int _spiMode; - - /// 1=>invert the data pattern bool _spiDataInvert; - QString _spiType; - - /// The transfer structure for writing to the spi-device - spi_ioc_transfer _spi; + QString _spiType; }; diff --git a/sources/leddevice/schemas/schema-artnet.json b/sources/leddevice/schemas/schema-artnet.json index 147023b31..2239d6bd2 100644 --- a/sources/leddevice/schemas/schema-artnet.json +++ b/sources/leddevice/schemas/schema-artnet.json @@ -26,7 +26,14 @@ "title":"edt_dev_spec_chanperfixture_title", "default": 3, "propertyOrder" : 4 - } + }, + "disableSplitting": { + "type": "boolean", + "format": "checkbox", + "title":"edt_prevent_pixel_data_splitting_across_universes", + "default" : false, + "propertyOrder" : 5 + } }, "additionalProperties": true } diff --git a/sources/leddevice/schemas/schema-awa_spi.json b/sources/leddevice/schemas/schema-awa_spi.json index 385197418..891e89f73 100644 --- a/sources/leddevice/schemas/schema-awa_spi.json +++ b/sources/leddevice/schemas/schema-awa_spi.json @@ -93,9 +93,9 @@ "spitype": { "type": "string", "title":"edt_conf_smooth_type_title", - "enum" : ["esp8266","esp32","standard"], + "enum" : ["esp8266","esp32","rp2040","standard"], "options" : { - "enum_titles" : ["esp8266","esp32","standard"] + "enum_titles" : ["esp8266","esp32","rp2040 (Pico)","standard"] }, "default" : "esp8266", "required" : true, diff --git a/sources/mqtt/mqtt.cpp b/sources/mqtt/mqtt.cpp index a9c21309e..62be639f5 100644 --- a/sources/mqtt/mqtt.cpp +++ b/sources/mqtt/mqtt.cpp @@ -8,51 +8,68 @@ #include #include -QString HYPERHDRAPI = QStringLiteral("HyperHDR/JsonAPI"); -QString HYPERHDRAPI_RESPONSE = QStringLiteral("HyperHDR/JsonAPI/response"); - -mqtt::mqtt(QObject* _parent) - : QObject(_parent) - , _jsonPort(8090) +#include + +// default param %1 is 'HyperHDR', do not edit templates here +const static QString TEMPLATE_HYPERHDRAPI = QStringLiteral("%1/JsonAPI"); +const static QString TEMPLATE_HYPERHDRAPI_RESPONSE = QStringLiteral("%1/JsonAPI/response"); + +mqtt::mqtt(const QJsonDocument& mqttConfig) + : QObject() + , _enabled(false) + , _port(1883) + , _is_ssl(false) + , _ignore_ssl_errors(true) + , _maxRetry(0) + , _currentRetry(0) + , _retryTimer(nullptr) + , _initialized(false) , _log(Logger::getInstance("MQTT")) , _clientInstance(nullptr) { + handleSettingsUpdate(settings::type::MQTT, mqttConfig); } mqtt::~mqtt() { + Debug(_log, "Prepare to shutdown"); stop(); + Debug(_log, "MQTT server is closed"); } -void mqtt::start(QString host, int port, QString username, QString password, bool is_ssl, bool ignore_ssl_errors) +void mqtt::start(QString host, int port, QString username, QString password, bool is_ssl, bool ignore_ssl_errors, QString customTopic) { if (_clientInstance != nullptr) return; - Debug(_log, "Starting the MQTT connection. Address: %s:%i. Protocol: %s. Authentication: %s, Ignore errors: %s", - QSTRING_CSTR(host), port, (is_ssl) ? "SSL": "NO SSL", (!username.isEmpty() || !password.isEmpty()) ? "YES" : "NO", (ignore_ssl_errors) ? "YES" : "NO"); + HYPERHDRAPI = QString(TEMPLATE_HYPERHDRAPI).arg(customTopic); + HYPERHDRAPI_RESPONSE = QString(TEMPLATE_HYPERHDRAPI_RESPONSE).arg(customTopic); - QHostAddress adr(host); + Debug(_log, "Starting the MQTT connection. Address: %s:%i. Protocol: %s. Authentication: %s, Ignore errors: %s", + QSTRING_CSTR(host), port, (is_ssl) ? "SSL" : "NO SSL", (!username.isEmpty() || !password.isEmpty()) ? "YES" : "NO", (ignore_ssl_errors) ? "YES" : "NO"); + Debug(_log, "MQTT topic: %s, MQTT response: %s", QSTRING_CSTR(HYPERHDRAPI), QSTRING_CSTR(HYPERHDRAPI_RESPONSE)); + QHostAddress address(host); - QHostInfo info = QHostInfo::fromName(host); - if (!info.addresses().isEmpty()) { - adr = info.addresses().first(); + if (!is_ssl && QAbstractSocket::IPv4Protocol != address.protocol() && QAbstractSocket::IPv6Protocol != address.protocol()) + { + Debug(_log, "The search for the name translated to the IP address has started..."); + QHostInfo info = QHostInfo::fromName(host); + if (!info.addresses().isEmpty()) + address = info.addresses().first(); + Debug(_log, "The search for IP has finished: %s => %s", QSTRING_CSTR(host), QSTRING_CSTR(address.toString())); } - if (is_ssl) { QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); - _clientInstance = new QMQTT::Client(host, port, sslConfig); + _clientInstance = new QMQTT::Client(host, port, sslConfig, ignore_ssl_errors, this); } else - _clientInstance = new QMQTT::Client(adr, port); - + _clientInstance = new QMQTT::Client(address, port, this); QString clientId = QString("HyperHDR:%1").arg(QHostInfo::localHostName()); - _clientInstance->setClientId(clientId); if (!username.isEmpty()) @@ -63,13 +80,15 @@ void mqtt::start(QString host, int port, QString username, QString password, boo if (is_ssl && ignore_ssl_errors) { - QObject::connect(_clientInstance, &QMQTT::Client::sslErrors, [&](const QList& errors) { - _clientInstance->ignoreSslErrors(); - }); + QObject::connect(_clientInstance, &QMQTT::Client::sslErrors, this, [this](const QList& errors) { + if (_clientInstance != nullptr) + _clientInstance->ignoreSslErrors(); + }); } QObject::connect(_clientInstance, &QMQTT::Client::error, this, &mqtt::error); QObject::connect(_clientInstance, &QMQTT::Client::connected, this, &mqtt::connected); QObject::connect(_clientInstance, &QMQTT::Client::received, this, &mqtt::received); + QObject::connect(_clientInstance, &QMQTT::Client::disconnected, this, &mqtt::disconnected); _clientInstance->connectToHost(); } @@ -77,15 +96,31 @@ void mqtt::stop() { if (_clientInstance != nullptr) { - delete _clientInstance; + Debug(_log, "Closing MQTT"); + disconnect(_clientInstance, nullptr, this, nullptr); + _clientInstance->disconnectFromHost(); + _clientInstance->deleteLater(); _clientInstance = nullptr; } } +void mqtt::disconnected() +{ + Debug(_log, "Disconnected"); +} + void mqtt::connected() { Debug(_log, "Connected"); + if (_retryTimer != nullptr) + { + Debug(_log, "Removing retry timer"); + disconnect(_retryTimer, nullptr, nullptr, nullptr); + _retryTimer->deleteLater(); + _retryTimer = nullptr; + } + if (_clientInstance != nullptr) { _clientInstance->subscribe(HYPERHDRAPI, 2); @@ -130,6 +165,35 @@ void mqtt::error(const QMQTT::ClientError error) case(QMQTT::ClientError::MqttNoPingResponse): message = "MqttNoPingResponse"; break; } Error(_log, "Error: %s", QSTRING_CSTR(message)); + + initRetry(); +} + +void mqtt::initRetry() +{ + if (_maxRetry > 0 && _retryTimer == nullptr) + { + int intervalSec = 10; + Debug(_log, "Prepare to retry in %i seconds (limit: %i)", intervalSec, _maxRetry); + _currentRetry = 0; + _retryTimer = new QTimer(this); + _retryTimer->setInterval(intervalSec * 1000); + connect(_retryTimer, &QTimer::timeout, this, [this]() { + if (_clientInstance == nullptr || _clientInstance->isConnectedToHost() || ++_currentRetry > _maxRetry) + { + Debug(_log, "Removing retry timer"); + disconnect(_retryTimer, nullptr, nullptr, nullptr); + _retryTimer->deleteLater(); + _retryTimer = nullptr; + } + else + { + Debug(_log, "Retrying %i/%i", _currentRetry, _maxRetry); + _clientInstance->connectToHost(); + } + }); + _retryTimer->start(); + } } void mqtt::handleSettingsUpdate(settings::type type, const QJsonDocument& config) @@ -138,57 +202,101 @@ void mqtt::handleSettingsUpdate(settings::type type, const QJsonDocument& config { QJsonObject obj = config.object(); + _enabled = obj["enable"].toBool(false); + _host = obj["host"].toString(); + _port = obj["port"].toInt(1883); + _username = obj["username"].toString(); + _password = obj["password"].toString(); + _is_ssl = obj["is_ssl"].toBool(false); + _ignore_ssl_errors = obj["ignore_ssl_errors"].toBool(true); + _maxRetry = obj["maxRetry"].toInt(120); + + _customTopic = obj["custom_topic"].toString().trimmed(); + if (_customTopic.isEmpty()) + _customTopic = "HyperHDR"; + + if (_initialized) + { + stop(); + if (_enabled) + start(_host, _port, _username, _password, _is_ssl, _ignore_ssl_errors, _customTopic); + } + + _initialized = true; + } +} - bool enabled = obj["enable"].toBool(); - QString host = obj["host"].toString(); - int port = obj["port"].toInt(); - QString username = obj["username"].toString(); - QString password = obj["password"].toString(); - bool is_ssl = obj["is_ssl"].toBool(); - bool ignore_ssl_errors = obj["ignore_ssl_errors"].toBool(); +void mqtt::begin() +{ + if (_initialized && _enabled) + start(_host, _port, _username, _password, _is_ssl, _ignore_ssl_errors, _customTopic); +} +void mqtt::executeJson(QString origin, const QJsonDocument& input, QJsonDocument& result) +{ + HyperAPI* _hyperAPI = new HyperAPI(origin, _log, true, this); - stop(); - if (enabled) - start(host, port, username, password, is_ssl, ignore_ssl_errors); - } - else if (type == settings::type::WEBSERVER) + _resultArray = QJsonArray(); + + _hyperAPI->initialize(); + connect(_hyperAPI, &HyperAPI::SignalCallbackJsonMessage, this, [this](QJsonObject ret) { + _resultArray.append(ret); + }); + + if (input.isObject()) + _hyperAPI->handleMessage(input.toJson()); + else if (input.isArray()) { - QJsonObject obj = config.object(); - _jsonPort = obj["port"].toInt(); + const QJsonArray& array = input.array(); + for (const auto& elem : array) + if (elem.isObject()) + { + QJsonDocument doc(elem.toObject()); + _hyperAPI->handleMessage(doc.toJson()); + } } + + result.setArray(_resultArray); + + disconnect(_hyperAPI, &HyperAPI::SignalCallbackJsonMessage, nullptr, nullptr); + _hyperAPI->deleteLater(); } +/////////////////////////////////////////////////////////// +/////////////////////// Testing /////////////////////////// +/////////////////////////////////////////////////////////// +// listener: +// mosquitto_sub -h localhost -t HyperHDR/JsonAPI/response +// commands: +// mosquitto_pub -h localhost -t HyperHDR/JsonAPI -m "[{\"command\" : \"clear\", \"priority\" :1}, {\"command\" : \"clear\", \"priority\" :2}]" +// mosquitto_pub -h localhost -t HyperHDR/JsonAPI -m "[{\"command\":\"componentstate\",\"componentstate\": {\"component\":\"HDR\",\"state\": true } }, {\"command\":\"componentstate\",\"componentstate\": {\"component\":\"HDR\",\"state\": false } }]" +// mosquitto_pub -h localhost -t HyperHDR/JsonAPI -m "[{\"command\" : \"instance\",\"subcommand\":\"switchTo\",\"instance\":1},{\"command\":\"componentstate\",\"componentstate\":{\"component\":\"LEDDEVICE\",\"state\": false}}]" +/////////////////////////////////////////////////////////// + void mqtt::received(const QMQTT::Message& message) { QString topic = message.topic(); - QString payload = QString().fromLocal8Bit(message.payload()); + QString payload = QString().fromUtf8(message.payload()); if (QString::compare(HYPERHDRAPI, topic) == 0 && payload.length() > 0) { - QString address = QString("http://localhost:%1/json-rpc").arg(_jsonPort); - QNetworkAccessManager* manager = new QNetworkAccessManager(this); - - QObject::connect(manager, &QNetworkAccessManager::finished, [manager, this](QNetworkReply* reply) { - if (_clientInstance != nullptr && reply != nullptr) - { - QByteArray bytes = reply->readAll(); - QMQTT::Message report; - - report.setTopic(HYPERHDRAPI_RESPONSE); - report.setQos(2); - if (reply->error() == QNetworkReply::NoError && bytes.length() > 0) - report.setPayload(bytes); - else - report.setPayload("{\n\t\"success\" : false\n}"); - - _clientInstance->publish(report); - } - - reply->deleteLater(); - manager->deleteLater(); - }); - - manager->post(QNetworkRequest(QUrl(address)), message.payload()); + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(payload.toUtf8(), &error); + QMQTT::Message result; + result.setTopic(HYPERHDRAPI_RESPONSE); + result.setQos(2); + if (doc.isEmpty()) + result.setPayload(QString("{\"success\" : false, \"error\" : \"%1 at offset: %2\"}").arg(error.errorString()).arg(error.offset).toUtf8()); + else + { + QJsonDocument resJson; + + executeJson("MQTT", doc, resJson); + + QString returnPayload = resJson.toJson(QJsonDocument::Compact); + Debug(_log, "JSON result: %s", QSTRING_CSTR(returnPayload)); + result.setPayload(returnPayload.toUtf8()); + } + _clientInstance->publish(result); } } diff --git a/sources/proto-nano-server/ProtoNanoClientConnection.cpp b/sources/proto-nano-server/ProtoNanoClientConnection.cpp index 3e30dc3fc..4c33402b0 100644 --- a/sources/proto-nano-server/ProtoNanoClientConnection.cpp +++ b/sources/proto-nano-server/ProtoNanoClientConnection.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -43,7 +43,7 @@ ProtoNanoClientConnection::ProtoNanoClientConnection(QTcpSocket* socket, int tim , _clientAddress(socket->peerAddress().toString()) , _timeoutTimer(new QTimer(this)) , _timeout(timeout * 1000) - , _priority() + , _priority(145) { // timer setup _timeoutTimer->setSingleShot(true); @@ -153,8 +153,8 @@ void ProtoNanoClientConnection::disconnected() { Debug(_log, "Socket Closed"); _socket->deleteLater(); - emit clearGlobalInput(_priority); - emit clientDisconnected(); + emit SignalClearGlobalInput(_priority, false); + emit SignalClientConnectionClosed(this); } void ProtoNanoClientConnection::handleImageCommand(const proto_ImageRequest& message, Image& image) @@ -165,18 +165,18 @@ void ProtoNanoClientConnection::handleImageCommand(const proto_ImageRequest& mes int width = message.imagewidth; int height = message.imageheight; - if (priority < 100 || priority >= 200) + if (priority < 50 || priority > 250) { - sendErrorReply("The priority " + std::to_string(priority) + " is not in the valid priority range between 100 and 199."); - Error(_log, "The priority %d is not in the proto-connection range between 100 and 199.", priority); + sendErrorReply("The priority " + std::to_string(priority) + " is not in the valid priority range between 50 and 250."); + Error(_log, "The priority %d is not in the proto-connection range between 50 and 250.", priority); return; } // make sure the prio is registered before setInput() if (priority != _priority) { - emit clearGlobalInput(_priority); - emit registerGlobalInput(priority, hyperhdr::COMP_PROTOSERVER, "Proto@" + _clientAddress); + emit SignalClearGlobalInput(_priority, false); + _clientDescription = "Proto@" + _clientAddress; _priority = priority; } @@ -189,22 +189,9 @@ void ProtoNanoClientConnection::handleImageCommand(const proto_ImageRequest& mes } // must resize - image.resize(width, height); + image.resize(width, height); - auto flat = FlatBufferServer::getInstance(); - if (flat != nullptr) - { - int hdrEnabled = flat->getHdrToneMappingEnabled(); - - if (hdrEnabled) - { - AUTO_CALL_3(flat, importFromProtoHandler, int, priority, int, duration, const Image&, image); - sendSuccessReply(); - return; - } - } - - emit setGlobalInputImage(_priority, image, duration); + emit SignalImportFromProto(_priority, duration, image, _clientDescription); // send reply sendSuccessReply(); @@ -217,7 +204,7 @@ void ProtoNanoClientConnection::handleClearCommand(const proto_ClearRequest& mes int priority = message.priority; // clear priority - emit clearGlobalInput(priority); + emit SignalClearGlobalInput(priority, false); // send reply sendSuccessReply(); diff --git a/sources/proto-nano-server/ProtoServer.cpp b/sources/proto-nano-server/ProtoServer.cpp index 1c74ce68c..13e4b12c4 100644 --- a/sources/proto-nano-server/ProtoServer.cpp +++ b/sources/proto-nano-server/ProtoServer.cpp @@ -10,11 +10,13 @@ #include #include -ProtoServer::ProtoServer(const QJsonDocument& config, QObject* parent) +ProtoServer::ProtoServer(std::shared_ptr netOrigin, const QJsonDocument& config, QObject* parent) : QObject(parent) , _server(new QTcpServer(this)) + , _netOrigin(netOrigin) , _log(Logger::getInstance("PROTOSERVER")) , _timeout(5000) + , _port(19445) , _config(config) { @@ -23,15 +25,12 @@ ProtoServer::ProtoServer(const QJsonDocument& config, QObject* parent) ProtoServer::~ProtoServer() { stopServer(); - delete _server; + Debug(_log, "ProtoServer instance is closed"); } void ProtoServer::initServer() -{ - _netOrigin = NetOrigin::getInstance(); +{ connect(_server, &QTcpServer::newConnection, this, &ProtoServer::newConnection); - - // apply config handleSettingsUpdate(settings::type::PROTOSERVER, _config); } @@ -68,12 +67,10 @@ void ProtoServer::newConnection() Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); ProtoNanoClientConnection* client = new ProtoNanoClientConnection(socket, _timeout, this); // internal - connect(client, &ProtoNanoClientConnection::clientDisconnected, this, &ProtoServer::clientDisconnected); - connect(client, &ProtoNanoClientConnection::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput); - connect(client, &ProtoNanoClientConnection::clearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput); - connect(client, &ProtoNanoClientConnection::setGlobalInputImage, GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage); - connect(client, &ProtoNanoClientConnection::setGlobalInputColor, GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor); - connect(GlobalSignals::getInstance(), &GlobalSignals::globalRegRequired, client, &ProtoNanoClientConnection::registationRequired); + connect(client, &ProtoNanoClientConnection::SignalClientConnectionClosed, this, &ProtoServer::signalClientConnectionClosedHandler); + connect(client, &ProtoNanoClientConnection::SignalClearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::SignalClearGlobalInput); + connect(client, &ProtoNanoClientConnection::SignalImportFromProto, this, &ProtoServer::SignalImportFromProto); + connect(client, &ProtoNanoClientConnection::SignalSetGlobalColor, GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalColor); _openConnections.append(client); } else @@ -82,11 +79,13 @@ void ProtoServer::newConnection() } } -void ProtoServer::clientDisconnected() +void ProtoServer::signalClientConnectionClosedHandler(ProtoNanoClientConnection* client) { - ProtoNanoClientConnection* client = qobject_cast(sender()); - client->deleteLater(); - _openConnections.removeAll(client); + if (client != nullptr) + { + client->deleteLater(); + _openConnections.removeAll(client); + } } void ProtoServer::startServer() @@ -108,9 +107,10 @@ void ProtoServer::stopServer() { if (_server->isListening()) { - // close client connections - for (const auto& client : _openConnections) + QVectorIterator i(_openConnections); + while (i.hasNext()) { + const auto& client = i.next(); client->forceClose(); } _server->close(); diff --git a/sources/sound-capture/CMakeLists.txt b/sources/sound-capture/CMakeLists.txt new file mode 100644 index 000000000..d2c06e17e --- /dev/null +++ b/sources/sound-capture/CMakeLists.txt @@ -0,0 +1,11 @@ +if (ENABLE_SOUNDCAPLINUX) + add_subdirectory(linux) +endif (ENABLE_SOUNDCAPLINUX) + +if (ENABLE_SOUNDCAPWINDOWS) + add_subdirectory(windows) +endif (ENABLE_SOUNDCAPWINDOWS) + +if (ENABLE_SOUNDCAPMACOS) + add_subdirectory(macos) +endif (ENABLE_SOUNDCAPMACOS) diff --git a/sources/sound-capture/linux/CMakeLists.txt b/sources/sound-capture/linux/CMakeLists.txt new file mode 100644 index 000000000..ce0892ac6 --- /dev/null +++ b/sources/sound-capture/linux/CMakeLists.txt @@ -0,0 +1,21 @@ +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/sound-capture/linux) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/sound-capture/linux) + +FILE ( GLOB SOUNDCAPLINUX_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) + +add_library(sound-capture-linux ${SOUNDCAPLINUX_SOURCES} ) + +find_package(ALSA REQUIRED) + +include_directories(${ALSA_INCLUDE_DIRS}) + +target_link_libraries(sound-capture-linux + hyperhdr-base + ${QT_LIBRARIES} + ${ALSA_LIBRARIES} +) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(sound-capture-linux REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/sound-capture/linux/SoundCaptureLinux.cpp b/sources/sound-capture/linux/SoundCaptureLinux.cpp new file mode 100644 index 000000000..d1152ea29 --- /dev/null +++ b/sources/sound-capture/linux/SoundCaptureLinux.cpp @@ -0,0 +1,301 @@ +/* SoundCaptureLinux.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define SOUNDCAPLINUX_BUF_LENP 10 + +AlsaWorkerThread::AlsaWorkerThread(Logger* logger, QString device, SoundCapture* parent) : + _logger(logger), + _device(device), + _parent(parent) +{ +} + +void AlsaWorkerThread::exitNow() +{ + _exitNow = true; +} + +void AlsaWorkerThread::run(){ + snd_pcm_sframes_t workingSizeSample = (1 << SOUNDCAPLINUX_BUF_LENP); + snd_pcm_uframes_t periodSize = workingSizeSample; + snd_pcm_uframes_t bufferSizeInBytes = periodSize * 2; + bool initialMessage = false; + snd_pcm_t* handle; + int status = 0; + bool error = false; + unsigned int exactRate = 22050; + + if ((status = snd_pcm_open(&handle, QSTRING_CSTR(_device), SND_PCM_STREAM_CAPTURE, 0)) < 0) { + Error(_logger, "Cannot open input sound device '%s'. Error: '%s'", QSTRING_CSTR(_device), snd_strerror(status)); + return; + } + + snd_pcm_hw_params_t* selected_hw_params = nullptr; + + if ((status = snd_pcm_hw_params_malloc(&selected_hw_params)) < 0) { + Error(_logger, "Cannot allocate hardware parameter buffer: '%s'", snd_strerror(status)); + snd_pcm_close(handle); + return; + } + + try + { + if ((status = snd_pcm_hw_params_any(handle, selected_hw_params)) < 0) { + Error(_logger, "Cannot set snd_pcm_hw_params_any: '%s'", snd_strerror(status)); + throw 1; + } + + if ((status = snd_pcm_hw_params_set_access(handle, selected_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + Error(_logger, "Cannot set snd_pcm_hw_params_set_access: '%s'", snd_strerror(status)); + throw 2; + } + + if ((status = snd_pcm_hw_params_set_format(handle, selected_hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + Error(_logger, "Cannot set snd_pcm_hw_params_set_format: '%s'", snd_strerror(status)); + throw 3; + } + + if ((status = snd_pcm_hw_params_set_rate_near(handle, selected_hw_params, &exactRate, 0)) < 0) { + Error(_logger, "Cannot set snd_pcm_hw_params_set_rate_near: '%s'", snd_strerror(status)); + throw 4; + } + else if (exactRate != 22050) + { + Error(_logger, "Cannot set rate to 22050"); + throw 5; + } + + if ((status = snd_pcm_hw_params_set_channels(handle, selected_hw_params, 1)) < 0) { + Error(_logger, "Cannot set snd_pcm_hw_params_set_channels: '%s'", snd_strerror(status)); + throw 6; + } + + if ((status = snd_pcm_hw_params_set_period_size_near(handle, selected_hw_params, &periodSize, 0)) < 0) + { + Error(_logger, "Cannot set snd_pcm_hw_params_set_period_size_near: '%s'", snd_strerror(status)); + throw 7; + } + else + Info(_logger, "Sound period size = %lu", (unsigned long)periodSize); + + bufferSizeInBytes = periodSize * 2; + + if ((status = snd_pcm_hw_params_set_buffer_size_near(handle, selected_hw_params, &bufferSizeInBytes)) < 0) + { + Error(_logger, "Cannot set snd_pcm_hw_params_set_buffer_size_near: '%s'", snd_strerror(status)); + throw 8; + } + else + Info(_logger, "Sound buffer size = %lu", (unsigned long)bufferSizeInBytes); + + if ((status = snd_pcm_hw_params(handle, selected_hw_params)) < 0) { + Error(_logger, "Cannot set snd_pcm_hw_params: '%s'", snd_strerror(status)); + throw 9; + } + + if ((status = snd_pcm_state(handle)) != SND_PCM_STATE_PREPARED) { + Error(_logger, "Preparing device failed: '%s'", snd_strerror(status)); + throw 10; + } + + if ((status = snd_pcm_start(handle)) < 0) { + Error(_logger, "ALSA start failed: '%s'", snd_strerror(status)); + throw 11; + } + + } + catch (...) + { + error = true; + } + + snd_pcm_hw_params_free(selected_hw_params); + + std::vector soundBuffer(workingSizeSample, 0); + + while (!error && !_exitNow) + { + snd_pcm_sframes_t retVal = snd_pcm_wait(handle, 100); + + if (retVal > 0 && !_exitNow) + { + retVal = snd_pcm_readi(handle, soundBuffer.data(), soundBuffer.size()); + if (retVal >= workingSizeSample && !_exitNow) + { + _parent->analyzeSpectrum(soundBuffer.data(), SOUNDCAPLINUX_BUF_LENP); + + if (!initialMessage) + Debug(_logger, "Got new audio frame: %lli bytes. Remains: %lli bytes", workingSizeSample, snd_pcm_avail_update(handle)); + initialMessage = true; + } + else if (!_exitNow) + printf("Underrun has occured. The sound frame is skipped\n"); + } + + if (retVal == -EPIPE && !_exitNow) + { + retVal = snd_pcm_prepare(handle); + if (retVal < 0) + { + Error(_logger, "Could not recover from EPIPE: %s. Sound capturing thread is exiting now.", snd_strerror(retVal)); + break; + } + + snd_pcm_state_t state = snd_pcm_state(handle); + Warning(_logger, "EPIPE detected. Current state: %i", state); + + if ((status = snd_pcm_start(handle)) < 0) { + Error(_logger, "ALSA restart after EPIPE failed: '%s'", snd_strerror(status)); + break; + } + } + else if (retVal < 0) + { + Error(_logger, "Critical error detected: %s. Sound capturing thread is exiting now.", snd_strerror(retVal)); + break; + } + } + + snd_pcm_drop(handle); + snd_pcm_hw_free(handle); + snd_pcm_close(handle); +}; + + +SoundCaptureLinux::SoundCaptureLinux(const QJsonDocument& effectConfig, QObject* parent) + : SoundCapture(effectConfig, parent) +{ + _thread = nullptr; + listDevices(); +} + + +void SoundCaptureLinux::listDevices() +{ + char** devices; + char** device; + + int status = snd_device_name_hint(-1, "pcm", (void***)&devices); + if (status != 0) { + Error(_logger, "Could not find sound devices for enumerating (%d)", status); + return; + } + + device = devices; + while (*device != NULL) + { + char *name = snd_device_name_get_hint(*device, "NAME"); + + if (name != nullptr) + { + char* desc = snd_device_name_get_hint(*device, "DESC"); + char* ioid = snd_device_name_get_hint(*device, "IOID"); + + if (ioid == NULL || std::strcmp(ioid, "Output") != 0) + _availableDevices.append(QString(name) + " | " + QString(desc) + " | " + QString(ioid)); + + if (name != nullptr) free(name); + if (desc != nullptr) free(desc); + if (ioid != nullptr) free(ioid); + } + device++; + } + snd_device_name_free_hint((void**)devices); +} + +SoundCaptureLinux::~SoundCaptureLinux() +{ + stop(); + snd_config_update_free_global(); +} + +void SoundCaptureLinux::start() +{ + if (_isActive && !_isRunning) + { + QStringList deviceList = _selectedDevice.split('|'); + + if (deviceList.size() == 0) + { + Error(_logger, "Invalid device name: %s", QSTRING_CSTR(_selectedDevice)); + } + + QString device = deviceList.at(0).trimmed(); + _normalizedName = _selectedDevice; + auto it = std::remove_if(_normalizedName.begin(), _normalizedName.end(), [](const QChar& c) { return !c.isLetterOrNumber() && c!=' ' && c!=':' && c!='=' && c!='|' && c!='.'; }); + _normalizedName.chop(std::distance(it, _normalizedName.end())); + Info(_logger, "Opening device: %s", QSTRING_CSTR(_normalizedName)); + + _isRunning = true; + + _thread = new AlsaWorkerThread(_logger, device, this); + _thread->setObjectName("SoundCapturing"); + connect(_thread, &AlsaWorkerThread::finished, this, [=]() {stop(); }); + _thread->start(); + } +} + +void SoundCaptureLinux::stop() +{ + if (!_isRunning) + return; + + Info(_logger, "Closing hardware sound driver: '%s'", QSTRING_CSTR(_normalizedName)); + + _isRunning = false; + + if (_thread != nullptr) + { + _thread->disconnect(this); + _thread->exitNow(); + _thread->quit(); + _thread->wait(); + delete _thread; + _thread = nullptr; + } + + Info(_logger, "Hardware sound driver is closed"); +} diff --git a/sources/sound-capture/macos/CMakeLists.txt b/sources/sound-capture/macos/CMakeLists.txt new file mode 100644 index 000000000..e1dc0e51d --- /dev/null +++ b/sources/sound-capture/macos/CMakeLists.txt @@ -0,0 +1,16 @@ +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/sound-capture/macos) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/sound-capture/macos) + +FILE ( GLOB SOUNDCAPMACOS_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" "${CURRENT_SOURCE_DIR}/*.mm" ) + +add_library(sound-capture-macos ${SOUNDCAPMACOS_SOURCES} ) + +target_link_libraries(sound-capture-macos + hyperhdr-base + ${QT_LIBRARIES} +) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(sound-capture-macos REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/grabber/SoundCapMacOS/SoundCapMacOS.mm b/sources/sound-capture/macos/SoundCaptureMacOS.mm similarity index 68% rename from sources/grabber/SoundCapMacOS/SoundCapMacOS.mm rename to sources/sound-capture/macos/SoundCaptureMacOS.mm index 6a8b6fe7b..2f84fed26 100644 --- a/sources/grabber/SoundCapMacOS/SoundCapMacOS.mm +++ b/sources/sound-capture/macos/SoundCaptureMacOS.mm @@ -1,8 +1,8 @@ -/* SoundCapMacOS.cpp +/* SoundCaptureMacOS.mm * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,22 +25,27 @@ * SOFTWARE. */ -#include -#include -#include #include +#include +#include +#include +#include #import #import -size_t SoundCapMacOS::_soundBufferIndex = 0; -int16_t SoundCapMacOS::_soundBuffer[(1< +#include +@class AudioStreamDelegate; +namespace { + AudioStreamDelegate* _avfSoundDelegate = nil; +} @interface AudioStreamDelegate : NSObject { @public - SoundCapMacOS* _avfGrabber; + SoundCaptureMacOS* _avfGrabber; dispatch_queue_t _sequencer; AVCaptureSession* _nativeSession; } @@ -65,23 +70,21 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB char* rawSoundData; if (CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &frameBytes, &rawSoundData) == kCMBlockBufferNoErr) { - _avfGrabber->RecordCallback(frameBytes, (uint8_t*) rawSoundData); + _avfGrabber->recordCallback(frameBytes, reinterpret_cast(rawSoundData)); } } } @end -AudioStreamDelegate* _avfSoundDelegate = nullptr; - -SoundCapMacOS::SoundCapMacOS(const QJsonDocument& effectConfig, QObject* parent) +SoundCaptureMacOS::SoundCaptureMacOS(const QJsonDocument& effectConfig, QObject* parent) : SoundCapture(effectConfig, parent) { - ListDevices(); + listDevices(); } -void SoundCapMacOS::ListDevices() +void SoundCaptureMacOS::listDevices() { AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone] @@ -97,73 +100,41 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB } } -bool SoundCapMacOS::getPermission() +bool SoundCaptureMacOS::getPermission() { if ([AVCaptureDevice respondsToSelector:@selector(authorizationStatusForMediaType:)]) { if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusAuthorized) { - Info(Logger::getInstance("HYPERHDR"), "HyperHDR has the sound capture's permission"); + Info(_logger, "HyperHDR has the sound capture's permission"); return true; } else { [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL grantedPerm) { if (grantedPerm) - Info(Logger::getInstance("HYPERHDR"), "Got the sound capture permission. Please restart the application."); + Info(_logger, "Got the sound capture permission. Please restart the application."); else - Error(Logger::getInstance("HYPERHDR"), "HyperHDR has NOT been granted the sound capture's permission"); + Error(_logger, "HyperHDR has NOT been granted the sound capture's permission"); } ]; } } else - Error(Logger::getInstance("HYPERHDR"), "Selector for authorizationStatusForMediaType failed"); + Error(_logger, "Selector for authorizationStatusForMediaType failed"); - Error(Logger::getInstance("HYPERHDR"), "HyperHDR have NOT got the sound capture's permission."); + Error(_logger, "HyperHDR have NOT got the sound capture's permission."); return false; } -SoundCapMacOS::~SoundCapMacOS() +SoundCaptureMacOS::~SoundCaptureMacOS() { - Stop(); + stop(); } -void SoundCapMacOS::RecordCallback(uint32_t frameBytes, uint8_t* rawAudioData) -{ - try - { - if (_isRunning) - { - size_t destSize = (1 << SOUNDCAPMACOS_BUF_LENP)*sizeof(int16_t); - uint8_t* destStart = (uint8_t*)(&_soundBuffer) + _soundBufferIndex; - uint8_t* destEnd = (uint8_t*)(&_soundBuffer) + destSize; - - for(;destStart < destEnd && _soundBufferIndex < destSize && frameBytes > 0; destStart++, _soundBufferIndex++, rawAudioData++, frameBytes--) - { - *destStart = *rawAudioData; - } - - if (_soundBufferIndex == destSize && AnaliseSpectrum(_soundBuffer, SOUNDCAPMACOS_BUF_LENP)) - { - _resultIndex++; - _soundBufferIndex = 0; - destStart = (uint8_t*)(&_soundBuffer); - } - - for(;destStart < destEnd && _soundBufferIndex < destSize && frameBytes > 0; destStart++, _soundBufferIndex++, rawAudioData++, frameBytes--) - { - *destStart = *rawAudioData; - } - } - } - catch(...) - { - } -} -void SoundCapMacOS::Start() +void SoundCaptureMacOS::start() { if (!getPermission()) return; @@ -187,7 +158,7 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB if (_avfSoundDelegate == nullptr) { - Error(Logger::getInstance("HYPERHDR"), "Audio listener creation failed"); + Error(_logger, "Audio listener creation failed"); } else { @@ -198,11 +169,11 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB if (!input) { - Error(Logger::getInstance("HYPERHDR"), "Could not open capturing sound device: %s", apiError.localizedDescription.UTF8String); + Error(_logger, "Could not open capturing sound device: %s", apiError.localizedDescription.UTF8String); } else { - Info(Logger::getInstance("HYPERHDR"), "Opening: %s", QSTRING_CSTR(current)); + Info(_logger, "Opening: %s", QSTRING_CSTR(current)); [_avfSoundDelegate->_nativeSession addInput:input]; @@ -236,14 +207,14 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB { [_avfSoundDelegate->_nativeSession startRunning]; if ((_avfSoundDelegate->_nativeSession.running)) - Info(Logger::getInstance("HYPERHDR"), "AVF audio grabber starts capturing"); + Info(_logger, "AVF audio grabber starts capturing"); else - Error(Logger::getInstance("HYPERHDR"), "AVF audio grabber isn't capturing"); + Error(_logger, "AVF audio grabber isn't capturing"); } [device unlockForConfiguration]; - Info(Logger::getInstance("HYPERHDR"), "AVF audio grabber initialized successfully"); + Info(_logger, "AVF audio grabber initialized successfully"); _isRunning = true; } } @@ -252,19 +223,19 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB if (notFound) { - Error(Logger::getInstance("HYPERHDR"), "Could not find selected audio device: '%s' in the system.", QSTRING_CSTR(_selectedDevice)); + Error(_logger, "Could not find selected audio device: '%s' in the system.", QSTRING_CSTR(_selectedDevice)); return; } } } -void SoundCapMacOS::Stop() +void SoundCaptureMacOS::stop() { if (!_isRunning) return; - Info(Logger::getInstance("HYPERHDR"), "Disconnecting from sound driver: '%s'", QSTRING_CSTR(_selectedDevice)); + Info(_logger, "Disconnecting from sound driver: '%s'", QSTRING_CSTR(_selectedDevice)); _isRunning = false; @@ -277,5 +248,29 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB _avfSoundDelegate->_sequencer = nullptr; } _avfSoundDelegate = nullptr; - Info(Logger::getInstance("HYPERHDR"), "AVF sound grabber uninit"); + Info(_logger, "AVF sound grabber uninit"); +} + +void SoundCaptureMacOS::recordCallback(uint32_t frameBytes, uint8_t* rawAudioData) +{ + int wantedSize = static_cast(sizeof(_soundBuffer)); + + while (frameBytes > 0) + { + int sourceCap = wantedSize - _soundBufferIndex; + int incomingCap = frameBytes; + int toRead = std::min(sourceCap, incomingCap); + uint8_t* destStart = reinterpret_cast(&_soundBuffer) + _soundBufferIndex; + + if (toRead <= 0) + break; + + memcpy(destStart, rawAudioData, toRead); + + rawAudioData += toRead; + frameBytes -= toRead; + _soundBufferIndex = (_soundBufferIndex + toRead) % wantedSize; + if (_soundBufferIndex == 0) + analyzeSpectrum(_soundBuffer, SOUNDCAPMACOS_BUF_LENP); + } } diff --git a/sources/sound-capture/windows/CMakeLists.txt b/sources/sound-capture/windows/CMakeLists.txt new file mode 100644 index 000000000..2cf68547d --- /dev/null +++ b/sources/sound-capture/windows/CMakeLists.txt @@ -0,0 +1,16 @@ +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/sound-capture/windows) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/sources/sound-capture/windows) + +FILE ( GLOB SOUNDCAPWINDOWS_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) + +add_library(sound-capture-windows ${SOUNDCAPWINDOWS_SOURCES} ) + +target_link_libraries(sound-capture-windows + hyperhdr-base + ${QT_LIBRARIES} +) + +if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(sound-capture-windows REUSE_FROM precompiled_hyperhdr_headers) +endif() diff --git a/sources/sound-capture/windows/SoundCaptureWindows.cpp b/sources/sound-capture/windows/SoundCaptureWindows.cpp new file mode 100644 index 000000000..db2983010 --- /dev/null +++ b/sources/sound-capture/windows/SoundCaptureWindows.cpp @@ -0,0 +1,205 @@ +/* SoundCaptureWindows.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#pragma comment(lib, "winmm.lib") + +#define SOUNDCAPWINDOWS_BUF_LENP 10 + +WindowsSoundThread::WindowsSoundThread(Logger* logger, QString device, SoundCapture* parent) : + _logger(logger), + _device(device), + _parent(parent) +{ +} + +void WindowsSoundThread::exitNow() +{ + _exitNow = true; +} + +void WindowsSoundThread::run() +{ + const int periodSize = (1 << SOUNDCAPWINDOWS_BUF_LENP); + const int bufferSizeInBytes = periodSize * 2; + + std::vector soundBuffer(periodSize); + + WAVEHDR header = {}; + HWAVEIN hWaveIn = nullptr; + bool initialMessage = false; + + UINT deviceCount = waveInGetNumDevs(), found = 0; + + for (found = 0; found < deviceCount; found++) + { + WAVEINCAPSW waveCaps; + memset(&waveCaps, 0, sizeof(waveCaps)); + waveInGetDevCapsW(found, &waveCaps, sizeof(WAVEINCAPSW)); + + if (QString::fromWCharArray(waveCaps.szPname).compare(_device) == 0) + break; + } + + if (found >= deviceCount) + { + Error(_logger, "Could not find '%s' device for open", QSTRING_CSTR(_device)); + return; + } + + WAVEFORMATEX pFormat; + pFormat.wFormatTag = WAVE_FORMAT_PCM; + pFormat.nChannels = 1; + pFormat.nSamplesPerSec = 22050; + pFormat.wBitsPerSample = 16; + pFormat.nBlockAlign = (pFormat.nChannels * pFormat.wBitsPerSample) / 8; + pFormat.nAvgBytesPerSec = (pFormat.nSamplesPerSec * pFormat.nChannels * pFormat.wBitsPerSample) / 8; + pFormat.cbSize = 0; + + MMRESULT result = waveInOpen(&hWaveIn, WAVE_MAPPER, &pFormat, 0L, 0L, WAVE_FORMAT_DIRECT); + + if (result != MMSYSERR_NOERROR) + { + Error(_logger, "Error during opening sound device '%s'. Error code: %i", QSTRING_CSTR(_device), result); + return; + } + + header.lpData = (LPSTR)&soundBuffer[0]; + header.dwBufferLength = bufferSizeInBytes; + header.dwFlags = 0L; + header.dwLoops = 0L; + + waveInPrepareHeader(hWaveIn, &header, sizeof(WAVEHDR)); + waveInAddBuffer(hWaveIn, &header, sizeof(WAVEHDR)); + + result = waveInStart(hWaveIn); + if (result != MMSYSERR_NOERROR) + { + waveInClose(hWaveIn); + Error(_logger, "Error during starting sound device '%s'. Error code: %i", QSTRING_CSTR(_device), result); + return; + } + + while (!_exitNow) + { + if (header.dwFlags & WHDR_DONE) + { + _parent->analyzeSpectrum(soundBuffer.data(), SOUNDCAPWINDOWS_BUF_LENP); + + header.dwFlags = 0L; + header.dwLoops = 0L; + + waveInPrepareHeader(hWaveIn, &header, sizeof(WAVEHDR)); + waveInAddBuffer(hWaveIn, &header, sizeof(WAVEHDR)); + } + } + + waveInStop(hWaveIn); + waveInUnprepareHeader(hWaveIn, &header, sizeof(WAVEHDR)); + waveInClose(hWaveIn); +}; + + +SoundCaptureWindows::SoundCaptureWindows(const QJsonDocument& effectConfig, QObject* parent) + : SoundCapture(effectConfig, parent) +{ + _thread = nullptr; + listDevices(); +} + + +void SoundCaptureWindows::listDevices() +{ + UINT deviceCount = waveInGetNumDevs(); + + for (UINT i = 0; i < deviceCount; i++) + { + WAVEINCAPSW waveCaps; + memset(&waveCaps, 0, sizeof(waveCaps)); + waveInGetDevCapsW(i, &waveCaps, sizeof(WAVEINCAPSW)); + + if (waveCaps.dwFormats & WAVE_FORMAT_2M16) + _availableDevices.append(QString::fromWCharArray(waveCaps.szPname)); + } +} + +SoundCaptureWindows::~SoundCaptureWindows() +{ + stop(); +} + +void SoundCaptureWindows::start() +{ + if (_isActive && !_isRunning) + { + QStringList deviceList = _selectedDevice.split('|'); + + if (deviceList.size() == 0) + { + Error(_logger, "Invalid device name: %s", QSTRING_CSTR(_selectedDevice)); + } + + QString device = deviceList.at(0).trimmed(); + _normalizedName = device; + Info(_logger, "Opening device: %s", QSTRING_CSTR(_normalizedName)); + + _isRunning = true; + + _thread = new WindowsSoundThread(_logger, device, this); + _thread->setObjectName("SoundCapturing"); + connect(_thread, &WindowsSoundThread::finished, this, [=]() {stop(); }); + _thread->start(); + } +} + +void SoundCaptureWindows::stop() +{ + if (!_isRunning) + return; + + Info(_logger, "Closing hardware sound driver: '%s'", QSTRING_CSTR(_normalizedName)); + + _isRunning = false; + + if (_thread != nullptr) + { + _thread->disconnect(this); + _thread->exitNow(); + _thread->quit(); + _thread->wait(); + delete _thread; + _thread = nullptr; + } + + Info(_logger, "Hardware sound driver is closed"); +} diff --git a/sources/ssdp/SSDPDiscover.cpp b/sources/ssdp/SSDPDiscover.cpp index 249788459..ec6fd5649 100644 --- a/sources/ssdp/SSDPDiscover.cpp +++ b/sources/ssdp/SSDPDiscover.cpp @@ -353,8 +353,8 @@ QJsonArray SSDPDiscover::getServicesDiscoveredJson() const } QJsonObject objOther; - QMap ::const_iterator o; - for (o = i.value().otherHeaders.begin(); o != i.value().otherHeaders.end(); ++o) + const QMap& otherHeaders = i.value().otherHeaders; + for (QMap::const_iterator o = otherHeaders.begin(); o != otherHeaders.end(); ++o) { objOther.insert(o.key().toLower(), o.value()); } diff --git a/sources/ssdp/SSDPHandler.cpp b/sources/ssdp/SSDPHandler.cpp index f319baa76..261b653ab 100644 --- a/sources/ssdp/SSDPHandler.cpp +++ b/sources/ssdp/SSDPHandler.cpp @@ -4,47 +4,65 @@ #include "SSDPDescription.h" #include #include -#include - +#include #include -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - // yes, we know it depracated and can handle it - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - #include - #pragma GCC diagnostic pop -#endif - #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) #include #endif +#define DEFAULT_RETRY 10 + static const QString SSDP_IDENTIFIER("urn:hyperhdr.eu:device:basic:1"); -SSDPHandler::SSDPHandler(WebServer* webserver, quint16 flatBufPort, quint16 protoBufPort, quint16 jsonServerPort, quint16 sslPort, const QString& name, QObject* parent) +SSDPHandler::SSDPHandler(QString uuid, quint16 flatBufPort, quint16 protoBufPort, quint16 jsonServerPort, quint16 sslPort, quint16 webPort, const QString& name, QObject* parent) : SSDPServer(parent) - , _webserver(webserver) + , _log(Logger::getInstance("SSDP")) , _localAddress() -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - , _NCA(nullptr) -#endif + , _uuid(uuid) + , _retry(DEFAULT_RETRY) { setFlatBufPort(flatBufPort); setProtoBufPort(protoBufPort); setJsonServerPort(jsonServerPort); setSSLServerPort(sslPort); + setWebServerPort(webPort); setHyperhdrName(name); + Debug(_log, "SSDPHandler is initialized"); } SSDPHandler::~SSDPHandler() { stopServer(); + Debug(_log, "SSDPHandler is closed"); } void SSDPHandler::initServer() { - _uuid = AuthManager::getInstance()->getID(); + Debug(_log, "SSDPHandler is initializing"); + + if (_localAddress.isEmpty()) + { + _localAddress = getLocalAddress(); + } + + if (_localAddress.isEmpty()) + { + if (_retry > 0) + { + Warning(_log, "Could not obtain the local address. Retry later (%i/%i)", (DEFAULT_RETRY - _retry + 1), DEFAULT_RETRY); + QTimer::singleShot(30000, this, [this]() { + if (!_running && _localAddress.isEmpty() && _retry > 0) + initServer(); + }); + _retry--; + } + else + Warning(_log, "Could not obtain the local address."); + } + else + _retry = 0; + SSDPServer::setUuid(_uuid); // announce targets @@ -55,34 +73,10 @@ void SSDPHandler::initServer() // prep server SSDPServer::initServer(); -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - // yes, we know it depracated and can handle it - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - _NCA = std::unique_ptr(new QNetworkConfigurationManager(this)); - connect(_NCA.get(), &QNetworkConfigurationManager::configurationChanged, this, &SSDPHandler::handleNetworkConfigurationChanged); - #pragma GCC diagnostic pop -#endif - // listen for mSearchRequestes connect(this, &SSDPServer::msearchRequestReceived, this, &SSDPHandler::handleMSearchRequest); - - - // get localAddress from interface - if (!getLocalAddress().isEmpty()) - { - _localAddress = getLocalAddress(); - } - - // startup if localAddress is found - bool isInited = false; - SAFE_CALL_0_RET(_webserver, isInited, bool, isInited); - - if (!_localAddress.isEmpty() && isInited) - { - handleWebServerStateChange(true); - } + handleWebServerStateChange(true); } void SSDPHandler::stopServer() @@ -94,18 +88,26 @@ void SSDPHandler::stopServer() void SSDPHandler::handleWebServerStateChange(bool newState) { + if (_localAddress.isEmpty()) + { + Debug(_log, "The local address is empty"); + return; + } + if (newState) { // refresh info QString param = buildDesc(); - BLOCK_CALL_1(_webserver, setSSDPDescription, QString, param); + emit newSsdpXmlDesc(param); setDescriptionAddress(getDescAddress()); if (start()) sendAnnounceList(true); + else + Warning(_log, "Could not start the SSDP server"); } else - { - BLOCK_CALL_1(_webserver, setSSDPDescription, QString, ""); + { + emit newSsdpXmlDesc(""); sendAnnounceList(false); stop(); } @@ -145,6 +147,11 @@ void SSDPHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& { SSDPServer::setSSLServerPort(obj["sslPort"].toInt()); } + + if (obj["port"].toInt() != SSDPServer::getWebServerPort()) + { + SSDPServer::setWebServerPort(obj["port"].toInt()); + } } if (type == settings::type::GENERAL) @@ -156,30 +163,6 @@ void SSDPHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& } } -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - // yes, we know it depracated and can handle it - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - void SSDPHandler::handleNetworkConfigurationChanged(const QNetworkConfiguration& config) - { - // get localAddress from interface - QString localAddress = getLocalAddress(); - if (!localAddress.isEmpty() && _localAddress != localAddress) - { - // revoke old ip - sendAnnounceList(false); - - // update desc & notify new ip - _localAddress = localAddress; - QString param = buildDesc(); - BLOCK_CALL_1(_webserver, setSSDPDescription, QString, param); - setDescriptionAddress(getDescAddress()); - sendAnnounceList(true); - } - } - #pragma GCC diagnostic pop -#endif - QString SSDPHandler::getLocalAddress() const { for (const auto& _interface : QNetworkInterface::allInterfaces()) @@ -196,7 +179,9 @@ QString SSDPHandler::getLocalAddress() const auto address = addressEntr.ip(); if (!address.isLoopback() && address.protocol() == QAbstractSocket::IPv4Protocol) { - return address.toString(); + QString retVal = address.toString(); + Debug(_log, "The local address is: %s", QSTRING_CSTR(retVal)); + return retVal; } } } @@ -207,7 +192,9 @@ QString SSDPHandler::getLocalAddress() const // is valid when, no loopback, IPv4 if (!address.isLoopback() && address.protocol() == QAbstractSocket::IPv4Protocol) { - return address.toString(); + QString retVal = address.toString(); + Debug(_log, "The local address is: %s", QSTRING_CSTR(retVal)); + return retVal; } } return QString(); @@ -215,7 +202,7 @@ QString SSDPHandler::getLocalAddress() const void SSDPHandler::handleMSearchRequest(const QString& target, const QString& mx, const QString address, quint16 port) { - const auto respond = [=]() { + const auto respond = [this, target, address, port]() { // when searched for all devices / root devices / basic device if (target == "ssdp:all") sendMSearchResponse(SSDP_IDENTIFIER, address, port); @@ -250,9 +237,7 @@ QString SSDPHandler::getDescAddress() const QString SSDPHandler::getBaseAddress() const { - quint16 port = 0; - SAFE_CALL_0_RET(_webserver, getPort, quint16, port); - return QString("http://%1:%2/").arg(_localAddress).arg(port); + return QString("http://%1:%2/").arg(_localAddress).arg(SSDPServer::getWebServerPort()); } QString SSDPHandler::buildDesc() const diff --git a/sources/ssdp/SSDPServer.cpp b/sources/ssdp/SSDPServer.cpp index 4bd777c3e..8f78fb7ad 100644 --- a/sources/ssdp/SSDPServer.cpp +++ b/sources/ssdp/SSDPServer.cpp @@ -1,10 +1,6 @@ #include #include -// utils -#include - - // HyperHDR #include @@ -89,25 +85,27 @@ SSDPServer::SSDPServer(QObject* parent) SSDPServer::~SSDPServer() { + Debug(_log, "Prepare to shutdown"); stop(); + Debug(_log, "SSDP server is closed"); } void SSDPServer::initServer() { - _udpSocket = new QUdpSocket(this); + Debug(_log, "Initialize the SSDP server"); - // get system info - SysInfo::HyperhdrSysInfo data = SysInfo::get(); + _udpSocket = new QUdpSocket(this); // create SERVER String _serverHeader = QString("%1/%2 UPnP/1.0 HyperHDR/%3") - .arg(data.prettyName, data.productVersion, HYPERHDR_VERSION); + .arg(QSysInfo::prettyProductName(), QSysInfo::productVersion(), HYPERHDR_VERSION); connect(_udpSocket, &QUdpSocket::readyRead, this, &SSDPServer::readPendingDatagrams); } bool SSDPServer::start() { + Info(_log, "Starting the SSDP server"); if (!_running && _udpSocket->bind(QHostAddress::AnyIPv4, SSDP_PORT, QAbstractSocket::ShareAddress)) { _udpSocket->joinMulticastGroup(SSDP_ADDR); @@ -119,6 +117,7 @@ bool SSDPServer::start() void SSDPServer::stop() { + Info(_log, "Stopping the SSDP server"); if (_running) { _udpSocket->close(); @@ -277,6 +276,16 @@ quint16 SSDPServer::getSSLServerPort() const return _sslPort.toInt(); } +void SSDPServer::setWebServerPort(quint16 port) +{ + _webPort = QString::number(port); +} + +quint16 SSDPServer::getWebServerPort() const +{ + return _webPort.toInt(); +} + void SSDPServer::setHyperhdrName(const QString& name) { diff --git a/sources/utils/CMakeLists.txt b/sources/utils/CMakeLists.txt index fb78ceae8..eb95fced7 100644 --- a/sources/utils/CMakeLists.txt +++ b/sources/utils/CMakeLists.txt @@ -14,11 +14,14 @@ add_library(hyperhdr-utils ${Utils_SOURCES} ) +target_include_directories(hyperhdr-utils PUBLIC ${TURBOJPEG_INCLUDE_DIRS}) + target_link_libraries(hyperhdr-utils hyperhdr-base Qt${Qt_VERSION}::Core Qt${Qt_VERSION}::Gui Qt${Qt_VERSION}::Network + ${TURBOJPEG_LINK_LIBRARIES} ) if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) diff --git a/sources/utils/ChannelCalibration.cpp b/sources/utils/ChannelCalibration.cpp new file mode 100644 index 000000000..9766bd386 --- /dev/null +++ b/sources/utils/ChannelCalibration.cpp @@ -0,0 +1,126 @@ +/* ChannelCalibration.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include +#endif + +#include + +ChannelCalibration::ChannelCalibration(quint8 instance, QString channelName, const QJsonObject& colorConfig, int defaultR, int defaultG, int defaultB) + : _channelName(channelName) + , _log(Logger::getInstance(QString("CHANNEL_%1%2").arg(channelName.toUpper()).arg(instance))) +{ + const QJsonArray& channelConfig = colorConfig[channelName].toArray(); + _targetCalibration.red = static_cast(channelConfig[0].toInt(defaultR)); + _targetCalibration.green = static_cast(channelConfig[1].toInt(defaultG)); + _targetCalibration.blue = static_cast(channelConfig[2].toInt(defaultB)); + + _enabled = (_targetCalibration.red != defaultR) || (_targetCalibration.green != defaultG) || (_targetCalibration.blue != defaultB); + + if (!_channelName.compare("red", Qt::CaseInsensitive)) + _correction = colorConfig["temperatureRed"].toInt(255); + else if (!_channelName.compare("green", Qt::CaseInsensitive)) + _correction = colorConfig["temperatureGreen"].toInt(255); + else if (!_channelName.compare("blue", Qt::CaseInsensitive)) + _correction = colorConfig["temperatureBlue"].toInt(255); + else + _correction = 255; + + if (_enabled || _correction != 255) + { + Debug(_log, "Target: [%i, %i, %i, %s], Correction: %i", + _targetCalibration.red, _targetCalibration.green, _targetCalibration.blue, (_enabled ? "active" : "default"), _correction); + } +} + +void ChannelCalibration::setAdjustment(const QJsonArray& value) +{ + if (value.size() != 3) + return; + _targetCalibration.red = static_cast(value[0].toInt(_targetCalibration.red)); + _targetCalibration.green = static_cast(value[1].toInt(_targetCalibration.green)); + _targetCalibration.blue = static_cast(value[2].toInt(_targetCalibration.blue)); + _enabled = true; + Debug(_log, "setAdjustment: [%i, %i, %i, %s]", _targetCalibration.red, _targetCalibration.green, _targetCalibration.blue, (_enabled ? "active" : "default")); +} + +void ChannelCalibration::setCorrection(int correction) +{ + if (correction < 0) + return; + + _correction = correction; + + Debug(_log, "setCorrection: %i", _correction); +} + +QString ChannelCalibration::getChannelName() +{ + return _channelName; +} + +uint8_t ChannelCalibration::getCorrection() const +{ + return _correction; +} +uint8_t ChannelCalibration::correction(uint8_t input) const +{ + return (input * _correction) / 255;; +} +ColorRgb ChannelCalibration::getAdjustment() const +{ + return _targetCalibration; +} + +uint8_t ChannelCalibration::adjustmentR(uint8_t inputR) const +{ + return (inputR * _targetCalibration.red) / 255; +} + +uint8_t ChannelCalibration::adjustmentG(uint8_t inputG) const +{ + return (inputG * _targetCalibration.green) / 255; +} + +uint8_t ChannelCalibration::adjustmentB(uint8_t inputB) const +{ + return (inputB * _targetCalibration.blue) / 255; +} + +bool ChannelCalibration::isEnabled() const +{ + return _enabled; +} + +void ChannelCalibration::apply(uint8_t input, uint8_t brightness, uint8_t& red, uint8_t& green, uint8_t& blue) +{ + red = std::min(((brightness * input * _targetCalibration.red) / 65025), (int)UINT8_MAX); + green = std::min(((brightness * input * _targetCalibration.green) / 65025), (int)UINT8_MAX); + blue = std::min(((brightness * input * _targetCalibration.blue) / 65025), (int)UINT8_MAX); +} diff --git a/sources/utils/ColorRgb.cpp b/sources/utils/ColorRgb.cpp index 157fcf29d..d98699548 100644 --- a/sources/utils/ColorRgb.cpp +++ b/sources/utils/ColorRgb.cpp @@ -1,5 +1,6 @@ -// Local includes -#include +#ifndef PCH_ENABLED + #include +#endif const ColorRgb ColorRgb::BLACK = { 0, 0, 0 }; const ColorRgb ColorRgb::RED = { 255, 0, 0 }; diff --git a/sources/utils/ColorRgbw.cpp b/sources/utils/ColorRgbw.cpp index 8f7f58965..8c6629321 100644 --- a/sources/utils/ColorRgbw.cpp +++ b/sources/utils/ColorRgbw.cpp @@ -1,4 +1,3 @@ -// Local includes #include const ColorRgbw ColorRgbw::BLACK = { 0, 0, 0, 0 }; diff --git a/sources/utils/RgbTransform.cpp b/sources/utils/ColorSpaceCalibration.cpp similarity index 56% rename from sources/utils/RgbTransform.cpp rename to sources/utils/ColorSpaceCalibration.cpp index a8e1c4ce9..49f4c27d0 100644 --- a/sources/utils/RgbTransform.cpp +++ b/sources/utils/ColorSpaceCalibration.cpp @@ -1,38 +1,74 @@ -#include -#include +/* ColorSpaceCalibration.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef PCH_ENABLED + #include + #include +#endif + +#include + +#include #include -#include -RgbTransform::RgbTransform() : - _log(Logger::getInstance(QString("RGB_TRANSFORM"))) +namespace { - init(false, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, false, 100, 100, true); + inline uint8_t clamp(int x) + { + return (x < 0) ? 0 : ((x > 255) ? 255 : static_cast(x)); + } } -RgbTransform::RgbTransform( - quint8 instance, - bool classic_config, - double saturationGain, - double luminanceGain, - double gammaR, double gammaG, double gammaB, - double backlightThreshold, bool backlightColored, - uint8_t brightness, uint8_t brightnessCompensation) : - _log(Logger::getInstance(QString("RGB_TRANSFORM") + QString::number(instance))) +ColorSpaceCalibration::ColorSpaceCalibration(uint8_t instance, const QJsonObject& colorConfig) : + _log(Logger::getInstance(QString("COLORSPACE_CALIBRATION%1").arg(instance))) { - init(classic_config, saturationGain, luminanceGain, - gammaR, gammaG, gammaB, backlightThreshold, backlightColored, brightness, brightnessCompensation); -} + const int backlightThreshold = colorConfig["backlightThreshold"].toInt(0); + const bool backlightColored = colorConfig["backlightColored"].toBool(false); + const int brightness = colorConfig["brightness"].toInt(100); + const int brightnessComp = colorConfig["brightnessCompensation"].toInt(100); + const double gammaR = colorConfig["gammaRed"].toDouble(1.0); + const double gammaG = colorConfig["gammaGreen"].toDouble(1.0); + const double gammaB = colorConfig["gammaBlue"].toDouble(1.0); -inline uint8_t clamp(int x) -{ - return (x < 0) ? 0 : ((x > 255) ? 255 : uint8_t(x)); + const bool classic_config = colorConfig["classic_config"].toBool(false); + const double saturationGain = colorConfig["saturationGain"].toDouble(1.0000); + const double luminanceGain = colorConfig["luminanceGain"].toDouble(1.0000); + + init(classic_config, + saturationGain, luminanceGain, + gammaR, gammaG, gammaB, + backlightThreshold, backlightColored, static_cast(brightness), static_cast(brightnessComp)); } -void RgbTransform::init( +void ColorSpaceCalibration::init( bool classic_config, - double saturationGain, - double luminanceGain, - double gammaR, double gammaG, double gammaB, double backlightThreshold, bool backlightColored, uint8_t brightness, uint8_t brightnessCompensation, bool _silent) + double saturationGain, double luminanceGain, + double gammaR, double gammaG, double gammaB, + int backlightThreshold, bool backlightColored, uint8_t brightness, uint8_t brightnessCompensation) { std::fill(std::begin(_mappingR), std::end(_mappingR), 0); std::fill(std::begin(_mappingG), std::end(_mappingG), 0); @@ -56,78 +92,87 @@ void RgbTransform::init( _backLightEnabled = true; _backlightColored = true; - if (!_silent) - Info(_log, "RGB transform classic_config: %i, saturationGain: %f, luminanceGain: %f, backlightThreshold: %i, backlightColored: %s", - _classic_config, _saturationGain, _luminanceGain, clamp(backlightThreshold), (backlightColored) ? "yes" : "no"); - - setGamma(gammaR, gammaG, gammaB); + _log->setInstanceEnable(false); + setGamma(gammaR, gammaG, gammaB); setBacklightThreshold(backlightThreshold); setBacklightColored(backlightColored); setBrightness(brightness); setBrightnessCompensation(brightnessCompensation); initializeMapping(); + _log->setInstanceEnable(true); + + Info(_log, "classicMode: %s, gammas:[%0.2f, %0.2f, %0.2f], saturation: %0.2f, luminance: %0.2f, backLight: [%s, threshold: %i, colored: %s]", + (_classic_config ? "yes" : "no"), gammaR, gammaG, gammaB, _saturationGain, _luminanceGain, (_backLightEnabled ? "enabled" : "disabled"),_backlightThreshold, (_backlightColored) ? "yes" : "no"); + } -double RgbTransform::getGammaR() const +double ColorSpaceCalibration::getGammaR() const { return _gammaR; } -double RgbTransform::getGammaG() const +double ColorSpaceCalibration::getGammaG() const { return _gammaG; } -double RgbTransform::getGammaB() const +double ColorSpaceCalibration::getGammaB() const { return _gammaB; } -void RgbTransform::setGamma(double gammaR, double gammaG, double gammaB) +void ColorSpaceCalibration::setGamma(double gammaR, double gammaG, double gammaB) { - _gammaR = gammaR; - _gammaG = (gammaG < 0.0) ? _gammaR : gammaG; - _gammaB = (gammaB < 0.0) ? _gammaR : gammaB; + _gammaR = std::max(gammaR, 0.05); + _gammaG = std::max(gammaG, 0.05); + _gammaB = std::max(gammaB, 0.05); initializeMapping(); + Debug(_log, "setGamma to [%f, %f, %f]", _gammaR, _gammaG, _gammaB); } -void RgbTransform::setSaturationGain(double saturationGain) +void ColorSpaceCalibration::setSaturationGain(double saturationGain) { + if (saturationGain < 0) + return; + if (_saturationGain != saturationGain) - Debug(_log, "set saturationGain to %f", saturationGain); + Debug(_log, "set SaturationGain to %f", saturationGain); _saturationGain = saturationGain; } -void RgbTransform::setLuminanceGain(double luminanceGain) +void ColorSpaceCalibration::setLuminanceGain(double luminanceGain) { + if (luminanceGain < 0) + return; + if (_luminanceGain != luminanceGain) - Debug(_log, "set luminanceGain to %f", luminanceGain); + Debug(_log, "set LuminanceGain to %f", luminanceGain); _luminanceGain = luminanceGain; } -double RgbTransform::getSaturationGain() const +double ColorSpaceCalibration::getSaturationGain() const { return _saturationGain; } -double RgbTransform::getLuminanceGain() const +double ColorSpaceCalibration::getLuminanceGain() const { return _luminanceGain; } -bool RgbTransform::getClassicConfig() const +bool ColorSpaceCalibration::getClassicConfig() const { return _classic_config; } -void RgbTransform::setClassicConfig(bool classic_config) +void ColorSpaceCalibration::setClassicConfig(bool classic_config) { _classic_config = classic_config; } -void RgbTransform::initializeMapping() +void ColorSpaceCalibration::initializeMapping() { for (int i = 0; i < 256; ++i) { @@ -138,66 +183,81 @@ void RgbTransform::initializeMapping() } -int RgbTransform::getBacklightThreshold() const +int ColorSpaceCalibration::getBacklightThreshold() const { return _backlightThreshold; } -void RgbTransform::setBacklightThreshold(int backlightThreshold) +void ColorSpaceCalibration::setBacklightThreshold(int backlightThreshold) { + if (backlightThreshold < 0) + return; + _backlightThreshold = backlightThreshold; uint8_t rgb = clamp(_backlightThreshold); if (_sumBrightnessRGBLow != rgb) - Info(_log, "setBacklightThreshold: %i", rgb); + Debug(_log, "setBacklightThreshold: %i", rgb); _sumBrightnessRGBLow = rgb; } -bool RgbTransform::getBacklightColored() const +bool ColorSpaceCalibration::getBacklightColored() const { return _backlightColored; } -void RgbTransform::setBacklightColored(bool backlightColored) +void ColorSpaceCalibration::setBacklightColored(bool backlightColored) { _backlightColored = backlightColored; + Debug(_log, "setBacklightColored: %i", _backlightColored); } -bool RgbTransform::getBackLightEnabled() const +bool ColorSpaceCalibration::getBackLightEnabled() const { return _backLightEnabled; } -void RgbTransform::setBackLightEnabled(bool enable) -{ +void ColorSpaceCalibration::setBackLightEnabled(bool enable) +{ _backLightEnabled = enable; + Debug(_log, "setBackLightEnabled: %i", _backLightEnabled); } -uint8_t RgbTransform::getBrightness() const +uint8_t ColorSpaceCalibration::getBrightness() const { return _brightness; } -void RgbTransform::setBrightness(uint8_t brightness) +void ColorSpaceCalibration::setBrightness(int brightness) { - _brightness = brightness; + if (brightness < 0) + return; + + _brightness = clamp(brightness); updateBrightnessComponents(); + + Debug(_log, "setBrightness: %i", _brightness); } -void RgbTransform::setBrightnessCompensation(uint8_t brightnessCompensation) +void ColorSpaceCalibration::setBrightnessCompensation(int brightnessCompensation) { - _brightnessCompensation = brightnessCompensation; + if (brightnessCompensation < 0) + return; + + _brightnessCompensation = clamp(brightnessCompensation); updateBrightnessComponents(); + + Debug(_log, "setBrightnessCompensation: %i", _brightnessCompensation); } -uint8_t RgbTransform::getBrightnessCompensation() const +uint8_t ColorSpaceCalibration::getBrightnessCompensation() const { return _brightnessCompensation; } -void RgbTransform::updateBrightnessComponents() +void ColorSpaceCalibration::updateBrightnessComponents() { double Fw = _brightnessCompensation * 2.0 / 100.0 + 1.0; double Fcmy = _brightnessCompensation / 100.0 + 1.0; @@ -217,21 +277,23 @@ void RgbTransform::updateBrightnessComponents() } } -void RgbTransform::getBrightnessComponents(uint8_t& rgb, uint8_t& cmy, uint8_t& w) const +void ColorSpaceCalibration::getBrightnessComponents(uint8_t& rgb, uint8_t& cmy, uint8_t& w) const { rgb = _brightness_rgb; cmy = _brightness_cmy; w = _brightness_w; } -void RgbTransform::transform(uint8_t& red, uint8_t& green, uint8_t& blue) +void ColorSpaceCalibration::applyGamma(uint8_t& red, uint8_t& green, uint8_t& blue) const { // apply gamma red = _mappingR[red]; green = _mappingG[green]; blue = _mappingB[blue]; +} - // apply brightnesss +void ColorSpaceCalibration::applyBacklight(uint8_t& red, uint8_t& green, uint8_t& blue) const +{ if (_backLightEnabled && _sumBrightnessRGBLow > 0) { if (_backlightColored) @@ -254,7 +316,7 @@ void RgbTransform::transform(uint8_t& red, uint8_t& green, uint8_t& blue) } } -void RgbTransform::transformSatLum(uint8_t& red, uint8_t& green, uint8_t& blue) +void ColorSpaceCalibration::applySaturationLuminance(uint8_t& red, uint8_t& green, uint8_t& blue) const { if (_saturationGain != 1.0 || _luminanceGain != 1.0 || _luminanceMinimum != 0.0) { @@ -283,7 +345,7 @@ void RgbTransform::transformSatLum(uint8_t& red, uint8_t& green, uint8_t& blue) } } -void RgbTransform::rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t& hue, float& saturation, float& luminance) +void ColorSpaceCalibration::rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t& hue, float& saturation, float& luminance) { float r = red / 255.0f; float g = green / 255.0f; @@ -327,7 +389,7 @@ void RgbTransform::rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t& h } -void RgbTransform::rgb2hsl_d(double r, double g, double b, double& hue, double& saturation, double& luminance) +void ColorSpaceCalibration::rgb2hsl_d(double r, double g, double b, double& hue, double& saturation, double& luminance) { double min, max, chroma; @@ -360,7 +422,7 @@ void RgbTransform::rgb2hsl_d(double r, double g, double b, double& hue, double& if (hue < 0) { hue += 360; } } -void RgbTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t& red, uint8_t& green, uint8_t& blue) +void ColorSpaceCalibration::hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t& red, uint8_t& green, uint8_t& blue) { if (saturation == 0.0f) { red = (uint8_t)(luminance * 255.0f); @@ -379,7 +441,7 @@ void RgbTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint float p = (2.0f * luminance) - q; float h = hue / 360.0f; - float t[3]; + float t[3]{}; t[0] = h + (1.0f / 3.0f); t[1] = h; @@ -392,7 +454,7 @@ void RgbTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint t[i] -= 1.0f; } - float out[3]; + float out[3]{}; for (int i = 0; i < 3; i++) { if (t[i] * 6.0f < 1.0f) @@ -411,7 +473,7 @@ void RgbTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint } -void RgbTransform::hsl2rgb_d(double hue, double saturation, double luminance, double& R, double& G, double& B) +void ColorSpaceCalibration::hsl2rgb_d(double hue, double saturation, double luminance, double& R, double& G, double& B) { // HSV Calculations -- formulas sourced from https://en.wikipedia.org/wiki/HSL_and_HSV if (saturation <= 0.001) { @@ -437,20 +499,3 @@ void RgbTransform::hsl2rgb_d(double hue, double saturation, double luminance, do } } -RgbTransform RgbTransform::createRgbTransform(quint8 instance, const QJsonObject& colorConfig) -{ - const double backlightThreshold = colorConfig["backlightThreshold"].toDouble(0.0); - const bool backlightColored = colorConfig["backlightColored"].toBool(false); - const int brightness = colorConfig["brightness"].toInt(100); - const int brightnessComp = colorConfig["brightnessCompensation"].toInt(100); - const double gammaR = colorConfig["gammaRed"].toDouble(1.0); - const double gammaG = colorConfig["gammaGreen"].toDouble(1.0); - const double gammaB = colorConfig["gammaBlue"].toDouble(1.0); - - const bool classic_config = colorConfig["classic_config"].toBool(false); - const double saturationGain = colorConfig["saturationGain"].toDouble(1.0000); - const double luminanceGain = colorConfig["luminanceGain"].toDouble(1.0000); - - return RgbTransform(instance, classic_config, saturationGain, luminanceGain, - gammaR, gammaG, gammaB, backlightThreshold, backlightColored, static_cast(brightness), static_cast(brightnessComp)); -} diff --git a/sources/utils/ColorSys.cpp b/sources/utils/ColorSys.cpp index da610fd73..93b053e0a 100644 --- a/sources/utils/ColorSys.cpp +++ b/sources/utils/ColorSys.cpp @@ -1,6 +1,8 @@ -#include +#ifndef PCH_ENABLED + #include +#endif -#include +#include inline uint8_t clamp(int x) { diff --git a/sources/utils/DefaultSignalHandler.cpp b/sources/utils/DefaultSignalHandler.cpp index 89b50a43a..3cbea2bf8 100644 --- a/sources/utils/DefaultSignalHandler.cpp +++ b/sources/utils/DefaultSignalHandler.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -151,6 +152,8 @@ namespace DefaultSignalHandler /* If the signal_handler is hit before the event loop is started, * following call will do nothing. So we queue the call. */ + HyperHdrInstance::signalTerminateTriggered(); + QUEUE_CALL_0(qApp, quit); // Reset signal handler to default (in case this handler is not capable of stopping) diff --git a/sources/utils/FileUtils.cpp b/sources/utils/FileUtils.cpp index 7bee371fc..98bbad73a 100644 --- a/sources/utils/FileUtils.cpp +++ b/sources/utils/FileUtils.cpp @@ -1,11 +1,10 @@ -#include - -// qt incl -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif -// hyperhdr include +#include #include namespace FileUtils { @@ -31,11 +30,9 @@ namespace FileUtils { return false; } - QByteArray result = file.readAll(); - if (result != nullptr && result.length() > 0) - data = QString::fromLocal8Bit(result); - else - data = ""; + QTextStream in(&file); + + data = in.readAll(); file.close(); diff --git a/sources/utils/FrameDecoder.cpp b/sources/utils/FrameDecoder.cpp index 367d8bbac..6fb13eddd 100644 --- a/sources/utils/FrameDecoder.cpp +++ b/sources/utils/FrameDecoder.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,9 +25,14 @@ * SOFTWARE. */ +#ifndef PCH_ENABLED + #include +#endif + #include #include -#include + +#include //#define TAKE_SCREEN_SHOT @@ -680,3 +685,68 @@ void FrameDecoder::processSystemImageRGBA(Image& image, int targetSize } } +void FrameDecoder::processSystemImagePQ10(Image& image, int targetSizeX, int targetSizeY, + int startX, int startY, + uint8_t* source, int _actualWidth, int _actualHeight, + int division, uint8_t* _lutBuffer, int lineSize) +{ + uint32_t ind_lutd; + size_t divisionX = (size_t)division * 4; + + if (lineSize == 0) + lineSize = _actualWidth * 4; + + for (int j = 0; j < targetSizeY; j++) + { + size_t lineSource = std::min(startY + j * division, _actualHeight - 1); + uint8_t* dLine = (image.rawMem() + (size_t)j * targetSizeX * 3); + uint8_t* dLineEnd = dLine + (size_t)targetSizeX * 3; + uint8_t* sLine = ((source + (lineSource * lineSize) + ((size_t)startX * 4))); + + if (_lutBuffer == nullptr) + { + while (dLine < dLineEnd) + { + uint32_t inS = *((uint32_t*)sLine); + *(dLine++) = (inS >> 2) & 0xFF; + *(dLine++) = (inS >> 12) & 0xFF; + *(dLine++) = (inS >> 22) & 0xFF; + sLine += divisionX; + } + } + else while (dLine < dLineEnd) + { + uint32_t inS = *((uint32_t*)sLine); + ind_lutd = LUT_INDEX(((inS >> 2) & 0xFF), ((inS >> 12) & 0xFF), ((inS >> 22) & 0xFF)); + *((uint32_t*)dLine) = *((uint32_t*)(&_lutBuffer[ind_lutd])); + sLine += divisionX; + dLine += 3; + } + } +} + +void FrameDecoder::encodeJpeg(MemoryBuffer& buffer, Image& inputImage, bool scaleDown) +{ + const int aspect = (scaleDown) ? 2 : 1; + const int width = inputImage.width(); + const int height = inputImage.height() / aspect; + int pitch = width * sizeof(ColorRgb) * aspect; + int subSample = (scaleDown) ? TJSAMP_422 : TJSAMP_444; + int quality = 75; + + unsigned long compressedImageSize = 0; + unsigned char* compressedImage = NULL; + + tjhandle _jpegCompressor = tjInitCompress(); + + tjCompress2(_jpegCompressor, inputImage.rawMem(), width, pitch, height, TJPF_RGB, + &compressedImage, &compressedImageSize, subSample, quality, TJFLAG_FASTDCT); + + buffer.resize(compressedImageSize); + memcpy(buffer.data(), compressedImage, compressedImageSize); + + tjDestroy(_jpegCompressor); + tjFree(compressedImage); +} + + diff --git a/sources/utils/Image.cpp b/sources/utils/Image.cpp index 089ec9547..27f0b00a4 100644 --- a/sources/utils/Image.cpp +++ b/sources/utils/Image.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,7 +25,9 @@ * SOFTWARE. */ -#include +#ifndef PCH_ENABLED + #include +#endif template Image::Image() : @@ -35,20 +37,27 @@ Image::Image() : template Image::Image(unsigned width, unsigned height) : - _d_ptr(new ImageData(width, height)) + _sharedData(new ImageData(width, height)) { } template Image::Image(const Image& other) : - _d_ptr(other._d_ptr) + _sharedData(other._sharedData) { } template -Image& Image::operator=(Image other) +Image& Image::operator=(const Image& other) { - _d_ptr = other._d_ptr; + _sharedData = other._sharedData; + return *this; +} + +template +Image& Image::operator=(Image&& other) noexcept +{ + _sharedData = std::move(other._sharedData); return *this; } @@ -61,85 +70,85 @@ QString Image::adjustCache() template bool Image::setBufferCacheSize() const { - return _d_ptr->setBufferCacheSize(); + return _sharedData->setBufferCacheSize(); } template bool Image::checkSignal(int x, int y, int r, int g, int b, int tolerance) { - return _d_ptr->checkSignal(x, y, r, g, b, tolerance); + return _sharedData->checkSignal(x, y, r, g, b, tolerance); } template void Image::fastBox(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t r, uint8_t g, uint8_t b) { - _d_ptr->fastBox(x1, y1, x2, y2, r, g, b); + _sharedData->fastBox(x1, y1, x2, y2, r, g, b); } template void Image::gradientHBox(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t r, uint8_t g, uint8_t b) { - _d_ptr->gradientHBox(x1, y1, x2, y2, r, g, b); + _sharedData->gradientHBox(x1, y1, x2, y2, r, g, b); } template void Image::gradientVBox(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t r, uint8_t g, uint8_t b) { - _d_ptr->gradientVBox(x1, y1, x2, y2, r, g, b); + _sharedData->gradientVBox(x1, y1, x2, y2, r, g, b); } template unsigned Image::width() const { - return _d_ptr->width(); + return _sharedData->width(); } template unsigned Image::height() const { - return _d_ptr->height(); + return _sharedData->height(); } template const ColorSpace& Image::operator()(unsigned x, unsigned y) const { - return _d_ptr->operator()(x, y); + return _sharedData->operator()(x, y); } template ColorSpace& Image::operator()(unsigned x, unsigned y) { - return _d_ptr->operator()(x, y); + return _sharedData->operator()(x, y); } template void Image::resize(unsigned width, unsigned height) { - _d_ptr->resize(width, height); + _sharedData->resize(width, height); } template uint8_t* Image::rawMem() { - return _d_ptr->rawMem(); + return _sharedData->rawMem(); } template const uint8_t* Image::rawMem() const { - return _d_ptr->rawMem(); + return _sharedData->rawMem(); } template size_t Image::size() const { - return _d_ptr->size(); + return _sharedData->size(); } template void Image::clear() { - _d_ptr->clear(); + _sharedData->clear(); } template class Image; diff --git a/sources/utils/ImageData.cpp b/sources/utils/ImageData.cpp index ce885f280..5f9c916f8 100644 --- a/sources/utils/ImageData.cpp +++ b/sources/utils/ImageData.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,15 +25,17 @@ * SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include -#include + #include +#endif #define LOCAL_VID_ALIGN_SIZE 16 @@ -46,30 +48,23 @@ template ImageData::ImageData(unsigned width, unsigned height) : _width(width), _height(height), - _initData(0), - _pixels(getMemory(width, height)) -{ -} - -template -ImageData::ImageData(const ImageData& other) : - QSharedData(other), - _width(other._width), - _height(other._height), - _initData(other._initData), - _pixels(other._pixels), - _bufferSize(other._bufferSize) + _memoryBuffer(nullptr), + _pixels(nullptr) { + getMemory(width, height); } template bool ImageData::setBufferCacheSize() { - return videoCache.setFrameSize(_bufferSize); + if (_memoryBuffer != nullptr) + return videoCache.setFrameSize(_memoryBuffer->size()); + else + return false; } template -ImageData::~ImageData() +ImageData::~ImageData() { freeMemory(); } @@ -107,7 +102,7 @@ void ImageData::resize(unsigned width, unsigned height) if ((width * height) > unsigned((_width * _height))) { freeMemory(); - _pixels = getMemory(width, height); + getMemory(width, height); } _width = width; @@ -144,7 +139,7 @@ size_t ImageData::size() const template <> void ImageData::clear() { - if (_pixels != NULL) + if (_pixels != nullptr) memset(_pixels, 0, static_cast(_width) * _height * 3); } @@ -171,27 +166,17 @@ inline unsigned ImageData::toIndex(unsigned x, unsigned y) const } template -inline uint8_t* ImageData::getMemory(size_t width, size_t height) +inline void ImageData::getMemory(size_t width, size_t height) { - if (width == 1 && height == 1) - { - _bufferSize = sizeof(ColorSpace); - return reinterpret_cast(&_initData); - } - - _bufferSize = width * height * sizeof(ColorSpace) + LOCAL_VID_ALIGN_SIZE; - return videoCache.request(_bufferSize); + size_t neededSize = width * height * sizeof(ColorSpace) + LOCAL_VID_ALIGN_SIZE; + _memoryBuffer = videoCache.request(neededSize); + _pixels = _memoryBuffer->data(); } template inline void ImageData::freeMemory() { - if (_bufferSize != sizeof(ColorSpace) && _pixels != nullptr) - { - videoCache.release(_bufferSize, _pixels); - } - - _bufferSize = 0; + videoCache.release(_memoryBuffer); _pixels = nullptr; } diff --git a/sources/utils/InternalClock.cpp b/sources/utils/InternalClock.cpp index 660255606..912551db0 100644 --- a/sources/utils/InternalClock.cpp +++ b/sources/utils/InternalClock.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,7 +25,9 @@ * SOFTWARE. */ -#include +#ifndef PCH_ENABLED + #include +#endif const std::chrono::time_point InternalClock::start = std::chrono::steady_clock::now(); const std::chrono::time_point InternalClock::startPrecise = std::chrono::high_resolution_clock::now(); diff --git a/sources/utils/JsonUtils.cpp b/sources/utils/JsonUtils.cpp index 8220083f1..035d4bfe3 100644 --- a/sources/utils/JsonUtils.cpp +++ b/sources/utils/JsonUtils.cpp @@ -1,14 +1,12 @@ -//project include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif -// util includes +#include #include -//qt includes -#include -#include -#include - namespace JsonUtils { bool readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError) diff --git a/sources/utils/Logger.cpp b/sources/utils/Logger.cpp index 6f6bc1654..db4dbe7c5 100644 --- a/sources/utils/Logger.cpp +++ b/sources/utils/Logger.cpp @@ -1,8 +1,20 @@ -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include + #include + #include + + #include +#endif -#include -#include +#include +#include + +#include #ifndef _WIN32 #include @@ -14,14 +26,6 @@ #pragma comment(lib, "Shlwapi.lib") #endif -#include -#include -#include -#include - -#include - - QMutex Logger::_mapLock; QString Logger::_lastError; bool Logger::_hasConsole; @@ -46,11 +50,6 @@ namespace const int MaxRepeatCountSize = 200; QThreadStorage RepeatCount; QThreadStorage RepeatMessage; - - QString getApplicationName() - { - return ""; - } } Logger* Logger::getInstance(const QString& name, Logger::LogLevel minLevel) @@ -62,8 +61,6 @@ Logger* Logger::getInstance(const QString& name, Logger::LogLevel minLevel) { log = new Logger(name, minLevel); _loggerMap.insert(name, log); - connect(log, &Logger::newLogMessage, LoggerManager::getInstance(), &LoggerManager::handleNewLogMessage); - connect(log, &Logger::newState, LoggerManager::getInstance(), &LoggerManager::handleNewState); if (_loggerMap.size() == 1) { @@ -143,19 +140,23 @@ void Logger::enable() Logger::Logger(const QString& name, LogLevel minLevel) : QObject() , _name(name) - , _appname(getApplicationName()) , _syslogEnabled(false) , _loggerId(LoggerId++) + , _instanceEnabled(true) , _minLevel(static_cast(minLevel)) { qRegisterMetaType(); + _logsManager = LoggerManager::getInstance(); + connect(this, &Logger::newLogMessage, _logsManager.get(), &LoggerManager::handleNewLogMessage); + connect(this, &Logger::newState, _logsManager.get(), &LoggerManager::handleNewState); + if (LoggerCount.fetchAndAddOrdered(1) == 1) { #ifndef _WIN32 if (_syslogEnabled) { - openlog(_appname.toLocal8Bit(), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); + openlog("HyperHDR", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); } #endif } @@ -177,9 +178,10 @@ Logger::~Logger() void Logger::write(const Logger::T_LOG_MESSAGE& message) { + static QMutex localWriteMutex; + if (_hasConsole) { - static QMutex localMutex; QString location, prefix, sufix; if (message.level == Logger::DEBUG) @@ -190,12 +192,12 @@ void Logger::write(const Logger::T_LOG_MESSAGE& message) .arg(message.function); } - QString name = (message.appName + " " + message.loggerName).trimmed(); + QString name = message.loggerName; name.resize(MAX_IDENTIFICATION_LENGTH, ' '); const QDateTime timestamp = QDateTime::fromMSecsSinceEpoch(message.utime); - localMutex.lock(); + localWriteMutex.lock(); #ifndef _WIN32 prefix = "\033[0m"; @@ -209,19 +211,20 @@ void Logger::write(const Logger::T_LOG_MESSAGE& message) .arg(timestamp.toString("hh:mm:ss.zzz")) .arg(name) .toStdString(); + bool isDaemon = (message.level == Logger::INFO && message.loggerName == "DAEMON" && message.message.indexOf("[") > 0); #ifndef _WIN32 switch (message.level) { - case(Logger::INFO): prefix = "\033[32;1m"; break; - case(Logger::WARNING): prefix = "\033[33;1m"; break; + case(Logger::INFO): prefix = (isDaemon) ? "\033[33;1m" : "\033[32;1m"; break; + case(Logger::WARNING): prefix = "\033[93;1m"; break; case(Logger::ERRORR): prefix = "\033[31;1m"; break; default: break; } #else switch (message.level) { - case(Logger::INFO):SetConsoleTextAttribute(consoleHandle, 10); break; + case(Logger::INFO):SetConsoleTextAttribute(consoleHandle, (isDaemon)? 6 : 10); break; case(Logger::WARNING):SetConsoleTextAttribute(consoleHandle, 14); break; case(Logger::ERRORR):SetConsoleTextAttribute(consoleHandle, 12); break; default: break; @@ -238,14 +241,23 @@ void Logger::write(const Logger::T_LOG_MESSAGE& message) #ifdef _WIN32 SetConsoleTextAttribute(consoleHandle, 11); #endif - localMutex.unlock(); + + localWriteMutex.unlock(); } newLogMessage(message); } +void Logger::setInstanceEnable(bool enabled) +{ + _instanceEnabled = enabled; +} + void Logger::Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...) { + if (!_instanceEnabled) + return; + Logger::LogLevel globalLevel = static_cast(int(GLOBAL_MIN_LOG_LEVEL)); bool writeAnyway = false; bool repeatMessage = false; @@ -300,7 +312,6 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u Logger::T_LOG_MESSAGE logMsg; - logMsg.appName = _appname; logMsg.loggerName = _name; logMsg.function = QString(func); logMsg.line = line; @@ -342,42 +353,80 @@ QString Logger::getName() const return _name; } -QString Logger::getAppName() const -{ - return _appname; -} - LoggerManager::LoggerManager() : QObject() - , _loggerMaxMsgBufferSize(350) + , _loggerMaxMsgBufferSize(500) , _enable(true) { - _logMessageBuffer.reserve(_loggerMaxMsgBufferSize); + _logs.reserve(_loggerMaxMsgBufferSize); } -void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE& msg) +LoggerManager::~LoggerManager() { - if (!_enable && msg.level != Logger::LogLevel::ERRORR) - return; + std::cout << "Clean-up logs..." << std::endl; - _logMessageBuffer.push_back(msg); - if (_logMessageBuffer.length() > _loggerMaxMsgBufferSize) + while(_logs.length() > 0) { - _logMessageBuffer.pop_front(); + delete (_logs.takeFirst()); } + + std::cout << "Goodbye!" << std::endl; +} - emit newLogMessage(msg); +QJsonArray LoggerManager::getLogMessageBuffer() +{ + QJsonArray messageArray; + for (auto item : _logs) + { + QJsonObject message; + message["loggerName"] = item->loggerName; + message["function"] = item->function; + message["line"] = QString::number(item->line); + message["fileName"] = item->fileName; + message["message"] = item->message; + message["levelString"] = item->levelString; + message["utime"] = QString::number(item->utime); + + messageArray.append(message); + } + return messageArray; } -LoggerManager* LoggerManager::getInstance() +void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE& _msg) { - static LoggerManager instance; - return &instance; + if (!_enable && _msg.level != Logger::LogLevel::ERRORR) + return; + + Logger::T_LOG_MESSAGE* msg = new Logger::T_LOG_MESSAGE(_msg); + + _logs.push_back(msg); + if (_logs.length() > _loggerMaxMsgBufferSize) + { + delete (_logs.takeFirst()); + } + + emit newLogMessage(_msg); } -const QList* LoggerManager::getLogMessageBuffer() const +std::shared_ptr LoggerManager::getInstance() { - return &_logMessageBuffer; + static QMutex locker; + static std::weak_ptr persist; + QMutexLocker lockThis(&locker); + + auto result = persist.lock(); + if (!result) + { + result = std::shared_ptr( + new LoggerManager(), + [](LoggerManager* oldManager) { + hyperhdr::SMARTPOINTER_MESSAGE("LoggerManager"); + delete oldManager; + } + ); + persist = result; + } + return result; } void LoggerManager::handleNewState(bool state) diff --git a/sources/utils/LutCalibrator.cpp b/sources/utils/LutCalibrator.cpp index b6fee7dcd..37250f537 100644 --- a/sources/utils/LutCalibrator.cpp +++ b/sources/utils/LutCalibrator.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -24,24 +24,30 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + + #include + #include + #include + + #include +#endif + +#include + #include #include -#include #include -#include +#include #include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include +#include +#include ColorRgb LutCalibrator::primeColors[] = { ColorRgb(255, 0, 0), ColorRgb(0, 255, 0), ColorRgb(0, 0, 255), ColorRgb(255, 255, 0), @@ -75,60 +81,64 @@ LutCalibrator::LutCalibrator() _currentCoef = 0; std::fill_n(_coefsResult, sizeof(_coefsResult) / sizeof(double), 0.0); _warningCRC = _warningMismatch = -1; - _lutBuffer = nullptr; _startColor = ColorRgb(0, 0, 0); _endColor = ColorRgb(0, 0, 0); _minColor = ColorRgb(255, 255, 255); for (capColors selector = capColors::Red; selector != capColors::None; selector = capColors(((int)selector) + 1)) _colorBalance[(int)selector].reset(); _maxColor = ColorRgb(0, 0, 0); - _timeStamp = 0; - - connect(this, &LutCalibrator::assign, this, &LutCalibrator::assignHandler, Qt::ConnectionType::UniqueConnection); - connect(this, &LutCalibrator::stop, this, &LutCalibrator::stopHandler, Qt::ConnectionType::UniqueConnection); + _timeStamp = 0; } LutCalibrator::~LutCalibrator() -{ - if (_lutBuffer != nullptr) - { - free(_lutBuffer); - _lutBuffer = nullptr; - } +{ } -LutCalibrator* LutCalibrator::getInstance() +void LutCalibrator::incomingCommand(QString rootpath, GrabberWrapper* grabberWrapper, hyperhdr::Components defaultComp, int checksum, ColorRgb startColor, ColorRgb endColor, bool limitedRange, double saturation, double luminance, double gammaR, double gammaG, double gammaB, int coef) { - if (instance == nullptr) - instance = new LutCalibrator(); - return instance; -} + _rootPath = rootpath; -void LutCalibrator::assignHandler(hyperhdr::Components defaultComp, int checksum, ColorRgb startColor, ColorRgb endColor, bool limitedRange, double saturation, double luminance, double gammaR, double gammaG, double gammaB, int coef) -{ if (checksum == 0) { - if (GrabberWrapper::getInstance() != nullptr && !_mjpegCalibration) + if (grabberWrapper != nullptr && !_mjpegCalibration) { - if (GrabberWrapper::getInstance()->getHdrToneMappingEnabled() != 0) + if (grabberWrapper->getHdrToneMappingEnabled() != 0) { QJsonObject report; stopHandler(); Error(_log, "Please disable LUT tone mapping and run the test again"); report["status"] = 1; report["error"] = "Please disable LUT tone mapping and run the test again"; - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); return; } - auto mode = GrabberWrapper::getInstance()->getVideoCurrentMode(); - auto vidMode = mode.contains(Grabber::currentVideoModeInfo::resolution) ? mode[Grabber::currentVideoModeInfo::resolution] : ""; + QString vidMode; + + SAFE_CALL_0_RET(grabberWrapper, getVideoCurrentModeResolution, QString, vidMode); + _mjpegCalibration = (vidMode.indexOf("mjpeg", 0, Qt::CaseInsensitive) >= 0); if (_mjpegCalibration) { Debug(_log, "Enabling pseudo-HDR mode for calibration to bypass TurboJPEG MJPEG to RGB processing"); - BLOCK_CALL_1((GrabberWrapper::getInstance()), setHdrToneMappingEnabled, int, 1); + + emit GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::Components::COMP_HDR, -1, true); + + int hdrEnabled = 0; + SAFE_CALL_0_RET(grabberWrapper, getHdrToneMappingEnabled, int, hdrEnabled); + Debug(_log, "HDR is %s", (hdrEnabled) ? "enabled" : "disabled"); + + if (!hdrEnabled) + { + QJsonObject report; + stopHandler(); + Error(_log, "Unexpected HDR state. Aborting"); + report["status"] = 1; + report["error"] = "Unexpected HDR state. Aborting"; + SignalLutCalibrationUpdated(report); + return; + } } } @@ -147,23 +157,22 @@ void LutCalibrator::assignHandler(hyperhdr::Components defaultComp, int checksum for (capColors selector = capColors::Red; selector != capColors::None; selector = capColors(((int)selector) + 1)) _colorBalance[(int)selector].reset(); - if (_lutBuffer == nullptr) - _lutBuffer = (uint8_t*)malloc(LUT_FILE_SIZE * 2); + + _lut.resize(LUT_FILE_SIZE * 2); - if (_lutBuffer != nullptr) + if (_lut.data() != nullptr) { - memset(_lutBuffer, 0, LUT_FILE_SIZE * 2); + memset(_lut.data(), 0, LUT_FILE_SIZE * 2); finalize(true); - memset(_lutBuffer, 0, LUT_FILE_SIZE * 2); - - auto wrapperInstance = GrabberWrapper::getInstance(); - if (wrapperInstance != nullptr) + memset(_lut.data(), 0, LUT_FILE_SIZE * 2); + + if (grabberWrapper != nullptr) { _log->disable(); - BLOCK_CALL_0(wrapperInstance, stop); - BLOCK_CALL_0(wrapperInstance, start); + BLOCK_CALL_0(grabberWrapper, stop); + BLOCK_CALL_0(grabberWrapper, start); for (int i = 0; i < 8; i++) { @@ -176,17 +185,17 @@ void LutCalibrator::assignHandler(hyperhdr::Components defaultComp, int checksum if (defaultComp == hyperhdr::COMP_VIDEOGRABBER) { Debug(_log, "Using video grabber as a source"); - connect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, this, &LutCalibrator::setVideoImage, Qt::ConnectionType::UniqueConnection); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewVideoImage, this, &LutCalibrator::setVideoImage, Qt::ConnectionType::UniqueConnection); } else if (defaultComp == hyperhdr::COMP_SYSTEMGRABBER) { Debug(_log, "Using system grabber as a source"); - connect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, &LutCalibrator::setSystemImage, Qt::ConnectionType::UniqueConnection); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewSystemImage, this, &LutCalibrator::setSystemImage, Qt::ConnectionType::UniqueConnection); } else { Debug(_log, "Using flatbuffers/protobuffers as a source"); - connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &LutCalibrator::setGlobalInputImage, Qt::ConnectionType::UniqueConnection); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalImage, this, &LutCalibrator::signalSetGlobalImageHandler, Qt::ConnectionType::UniqueConnection); } } else @@ -196,7 +205,7 @@ void LutCalibrator::assignHandler(hyperhdr::Components defaultComp, int checksum Error(_log, "Could not allocated memory (~100MB) for internal temporary buffer. Stopped."); report["status"] = 1; report["error"] = "Could not allocated memory (~100MB) for internal temporary buffer. Stopped."; - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); return; } } @@ -213,19 +222,15 @@ void LutCalibrator::assignHandler(hyperhdr::Components defaultComp, int checksum void LutCalibrator::stopHandler() { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, this, &LutCalibrator::setVideoImage); - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &LutCalibrator::setGlobalInputImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewVideoImage, this, &LutCalibrator::setVideoImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalImage, this, &LutCalibrator::signalSetGlobalImageHandler); _mjpegCalibration = false; _finish = false; _checksum = -1; _warningCRC = _warningMismatch = -1; std::fill_n(_coefsResult, sizeof(_coefsResult) / sizeof(double), 0.0); - if (_lutBuffer != nullptr) - { - free(_lutBuffer); - _lutBuffer = nullptr; - } + _lut.releaseMemory(); } void LutCalibrator::setVideoImage(const QString& name, const Image& image) @@ -238,7 +243,7 @@ void LutCalibrator::setSystemImage(const QString& name, const Image& i handleImage(image); } -void LutCalibrator::setGlobalInputImage(int priority, const Image& image, int timeout_ms, bool clearEffect) +void LutCalibrator::signalSetGlobalImageHandler(int priority, const Image& image, int timeout_ms, hyperhdr::Components origin) { handleImage(image); } @@ -262,7 +267,7 @@ void LutCalibrator::handleImage(const Image& image) Error(_log, "Too low resolution: 384x216 is the minimum. Received video frame: %ix%i. Stopped.", image.width(), image.height()); report["status"] = 1; report["error"] = "Too low resolution: 384x216 is the minimum. Received video frame: %ix%i. Stopped."; - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); return; } @@ -272,7 +277,7 @@ void LutCalibrator::handleImage(const Image& image) Error(_log, "Invalid resolution width/height ratio. Expected aspect: 1920/1080 (or the same 1280/720 etc). Stopped."); report["status"] = 1; report["error"] = "Invalid resolution width/height ratio. Expected aspect: 1920/1080 (or the same 1280/720 etc). Stopped."; - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); return; } @@ -377,7 +382,7 @@ void LutCalibrator::handleImage(const Image& image) stopHandler(); Error(_log, "Unexpected color %s != %s", QSTRING_CSTR(_startColor.toQString()), QSTRING_CSTR(_endColor.toQString())); report["status"] = 1; - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); return; } @@ -401,7 +406,7 @@ void LutCalibrator::handleImage(const Image& image) report["status"] = 1; } - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); } void LutCalibrator::storeColor(const ColorRgb& inputColor, const ColorRgb& color) @@ -567,7 +572,7 @@ void LutCalibrator::colorCorrection(double& r, double& g, double& b) { double hue, saturation, luminance; - RgbTransform::rgb2hsl_d(r, g, b, hue, saturation, luminance); + ColorSpaceCalibration::rgb2hsl_d(r, g, b, hue, saturation, luminance); double s = saturation * _saturation; saturation = s; @@ -575,7 +580,7 @@ void LutCalibrator::colorCorrection(double& r, double& g, double& b) double l = luminance * _luminance; luminance = l; - RgbTransform::hsl2rgb_d(hue, saturation, luminance, r, g, b); + ColorSpaceCalibration::hsl2rgb_d(hue, saturation, luminance, r, g, b); } void LutCalibrator::balanceGray(int r, int g, int b, double& _r, double& _g, double& _b) @@ -802,9 +807,9 @@ QString LutCalibrator::calColorToQStr(capColors index) uint32_t indexRgb = LUT_INDEX(qRound(real.red), qRound(real.green), qRound(real.blue)); - finalColor.red = _lutBuffer[indexRgb]; - finalColor.green = _lutBuffer[indexRgb + 1]; - finalColor.blue = _lutBuffer[indexRgb + 2]; + finalColor.red = _lut.data()[indexRgb]; + finalColor.green = _lut.data()[indexRgb + 1]; + finalColor.blue = _lut.data()[indexRgb + 2]; return QString("%1 => %2").arg(color.toQString()).arg(finalColor.toQString()); } @@ -878,9 +883,9 @@ void LutCalibrator::displayPostCalibrationInfo() bool LutCalibrator::correctionEnd() { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, this, &LutCalibrator::setVideoImage); - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, &LutCalibrator::setSystemImage); - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &LutCalibrator::setGlobalInputImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewVideoImage, this, &LutCalibrator::setVideoImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewSystemImage, this, &LutCalibrator::setSystemImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalImage, this, &LutCalibrator::signalSetGlobalImageHandler); double floor = qMax(_minColor.red, qMax(_minColor.green, _minColor.blue)); double ceiling = qMin(_maxColor.red, qMin(_maxColor.green, _maxColor.blue)); @@ -900,7 +905,7 @@ bool LutCalibrator::correctionEnd() QJsonObject report; report["limited"] = 1; - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); return false; } @@ -939,7 +944,7 @@ bool LutCalibrator::correctionEnd() QJsonObject report; report["coef"] = nextIndex; - lutCalibrationUpdate(report); + SignalLutCalibrationUpdated(report); return false; } @@ -1022,9 +1027,9 @@ bool LutCalibrator::correctionEnd() // save it uint32_t ind_lutd = LUT_INDEX(r, g, b); - _lutBuffer[ind_lutd ] = clampToInt(((finalR) * 255), 0, 255); - _lutBuffer[ind_lutd + 1] = clampToInt(((finalG) * 255), 0, 255); - _lutBuffer[ind_lutd + 2] = clampToInt(((finalB) * 255), 0, 255); + _lut.data()[ind_lutd ] = clampToInt(((finalR) * 255), 0, 255); + _lut.data()[ind_lutd + 1] = clampToInt(((finalG) * 255), 0, 255); + _lut.data()[ind_lutd + 2] = clampToInt(((finalB) * 255), 0, 255); } // display final colors @@ -1035,7 +1040,7 @@ bool LutCalibrator::correctionEnd() void LutCalibrator::applyFilter() { - uint8_t* _secondBuffer = &(_lutBuffer[LUT_FILE_SIZE]); + uint8_t* _secondBuffer = &(_lut.data()[LUT_FILE_SIZE]); memset(_secondBuffer, 0, LUT_FILE_SIZE); @@ -1059,9 +1064,9 @@ void LutCalibrator::applyFilter() uint32_t ind = LUT_INDEX(X, Y, Z); uint32_t scale = (x == 0 && y == 0 && z == 0) ? 13 : 1; - uint32_t R = _lutBuffer[ind]; - uint32_t G = _lutBuffer[ind + 1]; - uint32_t B = _lutBuffer[ind + 2]; + uint32_t R = _lut.data()[ind]; + uint32_t G = _lut.data()[ind + 1]; + uint32_t B = _lut.data()[ind + 2]; if (scale != 1 && R == G && G == B) { @@ -1081,7 +1086,7 @@ void LutCalibrator::applyFilter() _secondBuffer[index + 2] = clampToInt(((avB / (double)avCount)), 0, 255); } - memcpy(_lutBuffer, _secondBuffer, LUT_FILE_SIZE); + memcpy(_lut.data(), _secondBuffer, LUT_FILE_SIZE); } double LutCalibrator::fineTune(double& optimalRange, double& optimalScale, int& optimalWhite, int& optimalStrategy) @@ -1223,16 +1228,16 @@ double LutCalibrator::fineTune(double& optimalRange, double& optimalScale, int& bool LutCalibrator::finalize(bool fastTrack) { - QString fileName = QString("%1%2").arg(HyperHdrIManager::getInstance()->getRootPath()).arg("/lut_lin_tables.3d"); + QString fileName = QString("%1%2").arg(_rootPath).arg("/lut_lin_tables.3d"); QFile file(fileName); bool ok = true; if (!fastTrack) { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setVideoImage, this, &LutCalibrator::setVideoImage); - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, &LutCalibrator::setSystemImage); - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &LutCalibrator::setGlobalInputImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewVideoImage, this, &LutCalibrator::setVideoImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalNewSystemImage, this, &LutCalibrator::setSystemImage); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::SignalSetGlobalImage, this, &LutCalibrator::signalSetGlobalImageHandler); } if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) @@ -1261,11 +1266,11 @@ bool LutCalibrator::finalize(bool fastTrack) Debug(_log, "Max RGB range => %s", QSTRING_CSTR(_maxColor.toQString())); } - file.write((const char*)_lutBuffer, LUT_FILE_SIZE); + file.write((const char*)_lut.data(), LUT_FILE_SIZE); Debug(_log, "LUT RGB HDR table (1/3) is ready"); // YUV HDR - uint8_t* _yuvBuffer = &(_lutBuffer[LUT_FILE_SIZE]); + uint8_t* _yuvBuffer = &(_lut.data()[LUT_FILE_SIZE]); memset(_yuvBuffer, 0, LUT_FILE_SIZE); for (int y = 0; y < 256 && !fastTrack; y++) @@ -1295,9 +1300,9 @@ bool LutCalibrator::finalize(bool fastTrack) uint32_t indexRgb = LUT_INDEX(_R, _G, _B); uint32_t index = LUT_INDEX(y, u, v); - _yuvBuffer[index] = _lutBuffer[indexRgb]; - _yuvBuffer[index + 1] = _lutBuffer[indexRgb + 1]; - _yuvBuffer[index + 2] = _lutBuffer[indexRgb + 2]; + _yuvBuffer[index] = _lut.data()[indexRgb]; + _yuvBuffer[index + 1] = _lut.data()[indexRgb + 1]; + _yuvBuffer[index + 2] = _lut.data()[indexRgb + 2]; } file.write((const char*)_yuvBuffer, LUT_FILE_SIZE); Debug(_log, "LUT YUV HDR table (2/3) is ready"); @@ -1324,17 +1329,17 @@ bool LutCalibrator::finalize(bool fastTrack) b = y + 2 * (u - 128) * (1 - Kb); } - _lutBuffer[ind_lutd] = clampToInt(r, 0, 255); - _lutBuffer[ind_lutd + 1] = clampToInt(g, 0, 255); - _lutBuffer[ind_lutd + 2] = clampToInt(b, 0, 255); + _lut.data()[ind_lutd] = clampToInt(r, 0, 255); + _lut.data()[ind_lutd + 1] = clampToInt(g, 0, 255); + _lut.data()[ind_lutd + 2] = clampToInt(b, 0, 255); } - file.write((const char*)_lutBuffer, LUT_FILE_SIZE); + file.write((const char*)_lut.data(), LUT_FILE_SIZE); if (_mjpegCalibration && fastTrack) { file.seek(LUT_FILE_SIZE); - file.write((const char*)_lutBuffer, LUT_FILE_SIZE); + file.write((const char*)_lut.data(), LUT_FILE_SIZE); } file.flush(); diff --git a/sources/utils/Macros.cpp b/sources/utils/Macros.cpp new file mode 100644 index 000000000..2ed168a12 --- /dev/null +++ b/sources/utils/Macros.cpp @@ -0,0 +1,63 @@ +/* Macros.cpp +* +* MIT License +* +* Copyright (c) 2020-2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include + +void hyperhdr::THREAD_REMOVER(QString message, QThread* parentThread, QObject* client) +{ + SMARTPOINTER_MESSAGE(message); + if (parentThread->isRunning()) + { + if (client != nullptr) + client->deleteLater(); + parentThread->quit(); + parentThread->wait(); + } + else if (client != nullptr) + delete client; + delete parentThread; +} + +void hyperhdr::THREAD_MULTI_REMOVER(QString message, QThread* parentThread, std::vector clients) +{ + SMARTPOINTER_MESSAGE(message); + if (parentThread->isRunning()) + { + for(QObject*& client : clients) + client->deleteLater(); + parentThread->quit(); + parentThread->wait(); + } + else for (QObject*& client : clients) + delete client; + delete parentThread; +} + +void hyperhdr::SMARTPOINTER_MESSAGE(QString message) +{ + printf("SmartPointer is removing: %s\n", QSTRING_CSTR(message)); +} diff --git a/sources/utils/NetOrigin.cpp b/sources/utils/NetOrigin.cpp index 3e1b1377a..ef8b6e545 100644 --- a/sources/utils/NetOrigin.cpp +++ b/sources/utils/NetOrigin.cpp @@ -1,8 +1,10 @@ -#include - -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif -NetOrigin* NetOrigin::instance = nullptr; +#include NetOrigin::NetOrigin(QObject* parent, Logger* log) : QObject(parent) @@ -10,11 +12,12 @@ NetOrigin::NetOrigin(QObject* parent, Logger* log) , _internetAccessAllowed(false) , _ipWhitelist() { - NetOrigin::instance = this; } -bool NetOrigin::accessAllowed(const QHostAddress& address, const QHostAddress& local) const +bool NetOrigin::accessAllowed(const QHostAddress& address, const QHostAddress& local) { + QMutexLocker locker(&_mutex); + if (_internetAccessAllowed) return true; @@ -29,7 +32,7 @@ bool NetOrigin::accessAllowed(const QHostAddress& address, const QHostAddress& l return true; } -bool NetOrigin::isLocalAddress(const QHostAddress& address, const QHostAddress& local) const +bool NetOrigin::isLocalAddress(const QHostAddress& address, const QHostAddress& local) { if (address.protocol() == QAbstractSocket::IPv4Protocol) { @@ -48,10 +51,12 @@ bool NetOrigin::isLocalAddress(const QHostAddress& address, const QHostAddress& return true; } -void NetOrigin::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +void NetOrigin::settingsChangedHandler(settings::type type, const QJsonDocument& config) { if (type == settings::type::NETWORK) { + QMutexLocker locker(&_mutex); + const QJsonObject& obj = config.object(); _internetAccessAllowed = obj["internetAccessAPI"].toBool(false); diff --git a/sources/utils/PerformanceCounters.cpp b/sources/utils/PerformanceCounters.cpp index 63c788f99..c67b9793a 100644 --- a/sources/utils/PerformanceCounters.cpp +++ b/sources/utils/PerformanceCounters.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,19 +25,37 @@ * SOFTWARE. */ -#include -#include -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include + + #include +#endif + #include +#include +#include +#include #ifdef ENABLE_BONJOUR #include #endif -std::unique_ptr PerformanceCounters::_instance = nullptr; +using namespace hyperhdr; + +PerformanceReport::PerformanceReport(): + type((int)hyperhdr::PerformanceReportType::UNKNOWN), + id(-1), + param1(0), + param2(0), + param3(0), + param4(0), + timeStamp(0), + token(0) +{ +} PerformanceReport::PerformanceReport(int _type, qint64 _token, QString _name, double _param1, qint64 _param2, qint64 _param3, qint64 _param4, int _id) { @@ -70,6 +88,9 @@ PerformanceReport::PerformanceReport(int _type, qint64 _token, QString _name, do PerformanceCounters::PerformanceCounters() { qRegisterMetaType(); + qRegisterMetaType("hyperhdr::PerformanceReportType"); + + _system = std::unique_ptr(new SystemPerformanceCounters()); _lastRead = 0; _lastNetworkScan = 0; @@ -83,13 +104,16 @@ PerformanceCounters::PerformanceCounters() Debug(_log, "Failed to initialized"); } - connect(this, &PerformanceCounters::newCounter, this, &PerformanceCounters::receive); - connect(this, &PerformanceCounters::removeCounter, this, &PerformanceCounters::remove); - connect(this, &PerformanceCounters::triggerBroadcast, this, &PerformanceCounters::broadcast); - connect(this, &PerformanceCounters::performanceInfoRequest, this, &PerformanceCounters::request); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalPerformanceNewReport, this, &PerformanceCounters::signalPerformanceNewReportHandler); + connect(GlobalSignals::getInstance(), &GlobalSignals::SignalPerformanceStateChanged, this, &PerformanceCounters::signalPerformanceStateChangedHandler); +} + +PerformanceCounters::~PerformanceCounters() +{ + Debug(_log, "PerformanceCounters has been removed"); } -void PerformanceCounters::request(bool all) +void PerformanceCounters::performanceInfoRequest(bool all) { if (InternalClock::now() - _lastRead < 980) return; @@ -100,11 +124,11 @@ void PerformanceCounters::request(bool all) if (InternalClock::now() - _lastNetworkScan > 10*1000) { _lastNetworkScan = InternalClock::now(); - QUEUE_CALL_0((DiscoveryWrapper::getInstance()), requestServicesScan); + emit GlobalSignals::getInstance()->SignalDiscoveryRequestToScan(DiscoveryRecord::Service::REFRESH_ALL); } #endif - QString cpu = _system.getCPU(); + QString cpu = _system->getCPU(); if (cpu != "") { PerformanceReport pr; @@ -113,7 +137,7 @@ void PerformanceCounters::request(bool all) createUpdate(pr); } - QString ram = _system.getRAM(); + QString ram = _system->getRAM(); if (ram != "") { PerformanceReport pr; @@ -122,7 +146,7 @@ void PerformanceCounters::request(bool all) createUpdate(pr); } - QString temp = _system.getTEMP(); + QString temp = _system->getTEMP(); if (temp != "") { PerformanceReport pr; @@ -131,7 +155,7 @@ void PerformanceCounters::request(bool all) createUpdate(pr); } - QString under = _system.getUNDERVOLATGE(); + QString under = _system->getUNDERVOLATGE(); if (under != "") { PerformanceReport pr; @@ -149,20 +173,12 @@ void PerformanceCounters::request(bool all) } } -PerformanceCounters* PerformanceCounters::getInstance() -{ - if (PerformanceCounters::_instance == nullptr) - PerformanceCounters::_instance = std::unique_ptr(new PerformanceCounters()); - - return PerformanceCounters::_instance.get(); -} - qint64 PerformanceCounters::currentToken() { return (InternalClock::now() / 1000) / (qint64)60; } -void PerformanceCounters::receive(PerformanceReport pr) +void PerformanceCounters::signalPerformanceNewReportHandler(PerformanceReport pr) { QMutableListIterator cleaner(this->_reports); qint64 now = InternalClock::now() / 1000; @@ -182,7 +198,7 @@ void PerformanceCounters::receive(PerformanceReport pr) bool _inserted = false; - for (auto ins = this->_reports.begin(); ins != this->_reports.end(); ins++) + for (auto ins = this->_reports.begin(); ins != this->_reports.end(); ++ins) if ((*ins).id > pr.id || ((*ins).id == pr.id && (*ins).type >= pr.type)) { this->_reports.insert(ins, pr); @@ -198,24 +214,31 @@ void PerformanceCounters::receive(PerformanceReport pr) createUpdate(pr); } -void PerformanceCounters::remove(int type, int id) +void PerformanceCounters::signalPerformanceStateChangedHandler(bool state, PerformanceReportType reportType, int id, QString name) { - QMutableListIterator i(this->_reports); - int token = -1; + int type = static_cast(reportType); - while (i.hasNext()) + if (!state) { - PerformanceReport del = i.next(); - if (del.type == type && - del.id == id) + QMutableListIterator i(this->_reports); + int token = -1; + + while (i.hasNext()) { - token = del.token; - i.remove(); + PerformanceReport del = i.next(); + if (del.type == type && + del.id == id) + { + token = del.token; + i.remove(); + } } - } - consoleReport(type, token); + consoleReport(type, token); - deleteUpdate(type, id); + deleteUpdate(type, id); + } + else + signalPerformanceNewReportHandler(PerformanceReport(type, -1, name, -1, -1, -1, -1, id)); } void PerformanceCounters::consoleReport(int type, int token) @@ -243,7 +266,7 @@ void PerformanceCounters::consoleReport(int type, int token) if (del.type == static_cast(PerformanceReportType::VIDEO_GRABBER)) { if (del.token > 0) - list.append(QString("[USB capturing: FPS = %1, decoding = %2ms, frames = %3, invalid = %4]").arg(del.param1, 0, 'f', 2).arg(del.param2).arg(del.param3).arg(del.param4)); + list.append(QString("[USB: FPS = %1, decoding = %2ms, frames = %3, invalid = %4, mode = %5]").arg(del.param1, 0, 'f', 2).arg(del.param2).arg(del.param3).arg(del.param4).arg((del.name.indexOf("(direct)") ? "direct" : "signal-detection" ))); } else if (del.type == static_cast(PerformanceReportType::INSTANCE)) { @@ -281,7 +304,7 @@ void PerformanceCounters::createUpdate(PerformanceReport pr) else report["refresh"] = qMax(1 - (helper / 60 - pr.timeStamp / 60), 0ll) * 60 + 60 - (helper % 60); - emit performanceUpdate(report); + emit SignalPerformanceStatisticsUpdated(report); } void PerformanceCounters::deleteUpdate(int type, int id) @@ -292,10 +315,10 @@ void PerformanceCounters::deleteUpdate(int type, int id) report["type"] = type; report["id"] = id; - emit performanceUpdate(report); + emit SignalPerformanceStatisticsUpdated(report); } -void PerformanceCounters::broadcast() +void PerformanceCounters::triggerBroadcast() { QJsonArray arr; for (auto pr : this->_reports) @@ -325,5 +348,5 @@ void PerformanceCounters::broadcast() report["broadcast"] = arr; - emit performanceUpdate(report); + emit SignalPerformanceStatisticsUpdated(report); } diff --git a/sources/utils/RgbChannelAdjustment.cpp b/sources/utils/RgbChannelAdjustment.cpp deleted file mode 100644 index 290d258f9..000000000 --- a/sources/utils/RgbChannelAdjustment.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include -#include - -RgbChannelAdjustment::RgbChannelAdjustment(QString channelName) - : _channelName(channelName) - , _log(Logger::getInstance(channelName)) -{ - resetInitialized(); - _enabled = true; -} - - -RgbChannelAdjustment::RgbChannelAdjustment(quint8 instance, uint8_t adjustR, uint8_t adjustG, uint8_t adjustB, QString channelName, bool enabled) - : _channelName(channelName) - , _log(Logger::getInstance(channelName.replace("ChannelAdjust_", "ADJUST_") + QString::number(instance))) -{ - resetInitialized(); - setAdjustment(adjustR, adjustG, adjustB); - _enabled = enabled; -} - -void RgbChannelAdjustment::resetInitialized() -{ - _correction = 0; - _brightness = 0; - - std::fill(std::begin(_adjust), std::end(_adjust), 0); - std::fill(std::begin(_initialized), std::end(_initialized), false); - std::fill(std::begin(_mappingAdjR), std::end(_mappingAdjR), 0); - std::fill(std::begin(_mappingAdjG), std::end(_mappingAdjG), 0); - std::fill(std::begin(_mappingAdjB), std::end(_mappingAdjB), 0); - std::fill(std::begin(_mappingCorection), std::end(_mappingCorection), 0); - - memset(&_mapping, 0, sizeof(_mapping)); -} - -void RgbChannelAdjustment::setAdjustment(uint8_t adjustR, uint8_t adjustG, uint8_t adjustB) -{ - _adjust[RED] = adjustR; - _adjust[GREEN] = adjustG; - _adjust[BLUE] = adjustB; - - std::fill(std::begin(_initialized), std::end(_initialized), false); - initializeAdjustMapping(adjustR, adjustG, adjustB); -} - -void RgbChannelAdjustment::setCorrection(uint8_t correction) -{ - if (_correction != correction) - Info(_log, "Set correction to %d", correction); - - _correction = correction; - initializeCorrectionMapping(correction); -} -uint8_t RgbChannelAdjustment::getCorrection() const -{ - return _correction; -} -uint8_t RgbChannelAdjustment::correction(uint8_t input) const -{ - return _mappingCorection[input]; -} -uint8_t RgbChannelAdjustment::getAdjustmentR() const -{ - return _adjust[RED]; -} - -uint8_t RgbChannelAdjustment::getAdjustmentG() const -{ - return _adjust[GREEN]; -} - -uint8_t RgbChannelAdjustment::getAdjustmentB() const -{ - return _adjust[BLUE]; -} - -uint8_t RgbChannelAdjustment::adjustmentR(uint8_t inputR) const -{ - return _mappingAdjR[inputR]; -} - -uint8_t RgbChannelAdjustment::adjustmentG(uint8_t inputG) const -{ - return _mappingAdjG[inputG]; -} - -uint8_t RgbChannelAdjustment::adjustmentB(uint8_t inputB) const -{ - return _mappingAdjB[inputB]; -} - -bool RgbChannelAdjustment::isEnabled() -{ - return _enabled; -} - -void RgbChannelAdjustment::apply(uint8_t input, uint8_t brightness, uint8_t& red, uint8_t& green, uint8_t& blue) -{ - if (_brightness != brightness) - { - _brightness = brightness; - std::fill(std::begin(_initialized), std::end(_initialized), false); - } - - if (!_initialized[input]) - { - _mapping[RED][input] = qMin(((_brightness * input * _adjust[RED]) / 65025), (int)UINT8_MAX); - _mapping[GREEN][input] = qMin(((_brightness * input * _adjust[GREEN]) / 65025), (int)UINT8_MAX); - _mapping[BLUE][input] = qMin(((_brightness * input * _adjust[BLUE]) / 65025), (int)UINT8_MAX); - _initialized[input] = true; - } - red = _mapping[RED][input]; - green = _mapping[GREEN][input]; - blue = _mapping[BLUE][input]; -} - -void RgbChannelAdjustment::initializeAdjustMapping(uint8_t _adjustR, uint8_t _adjustG, uint8_t _adjustB) -{ - // initialize the mapping - for (int i = 0; i < 256; ++i) - { - int outputR = (i * _adjustR) / 255; - if (outputR > 255) - { - outputR = 255; - } - _mappingAdjR[i] = outputR; - } - for (int i = 0; i < 256; ++i) - { - int outputG = (i * _adjustG) / 255; - if (outputG > 255) - { - outputG = 255; - } - _mappingAdjG[i] = outputG; - } - for (int i = 0; i < 256; ++i) - { - int outputB = (i * _adjustB) / 255; - if (outputB > 255) - { - outputB = 255; - } - _mappingAdjB[i] = outputB; - } -} - - -void RgbChannelAdjustment::initializeCorrectionMapping(uint8_t correction) -{ - // initialize the mapping - for (int i = 0; i < 256; ++i) - { - int outputR = (i * correction) / 255; - if (outputR < -255) - { - outputR = -255; - } - else if (outputR > 255) - { - outputR = 255; - } - _mappingCorection[i] = outputR; - } -} - -RgbChannelAdjustment RgbChannelAdjustment::createRgbChannelAdjustment(quint8 instance, const QJsonObject& colorConfig, const QString& channelName, int defaultR, int defaultG, int defaultB) -{ - const QJsonArray& channelConfig = colorConfig[channelName].toArray(); - auto rr = static_cast(channelConfig[0].toInt(defaultR)); - auto gg = static_cast(channelConfig[1].toInt(defaultG)); - auto bb = static_cast(channelConfig[2].toInt(defaultB)); - return RgbChannelAdjustment( - instance, - rr, - gg, - bb, - "ChannelAdjust_" + channelName.toUpper(), - (rr != defaultR) || (gg != defaultG) || (bb != defaultB) - ); -} - - - diff --git a/sources/utils/SysInfo.cpp b/sources/utils/SysInfo.cpp deleted file mode 100644 index 40fdc7893..000000000 --- a/sources/utils/SysInfo.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "utils/SysInfo.h" -#include "utils/FileUtils.h" - -#include -#include -#include -#include - -#include - -std::unique_ptr SysInfo::_instance = nullptr; - -SysInfo::SysInfo() - : QObject() -{ - _sysinfo.kernelType = QSysInfo::kernelType(); - _sysinfo.kernelVersion = QSysInfo::kernelVersion(); - _sysinfo.architecture = QSysInfo::currentCpuArchitecture(); - _sysinfo.wordSize = QString::number(QSysInfo::WordSize); - _sysinfo.productType = QSysInfo::productType(); - _sysinfo.productVersion = QSysInfo::productVersion(); - _sysinfo.prettyName = QSysInfo::prettyProductName(); - _sysinfo.hostName = QHostInfo::localHostName(); - _sysinfo.domainName = QHostInfo::localDomainName(); - getCPUInfo(); - _sysinfo.qtVersion = QT_VERSION_STR; - _sysinfo.pyVersion = ""; -} - -SysInfo::HyperhdrSysInfo SysInfo::get() -{ - if (SysInfo::_instance == nullptr) - SysInfo::_instance = std::unique_ptr(new SysInfo()); - - return SysInfo::_instance->_sysinfo; -} - -void SysInfo::getCPUInfo() -{ - QString cpuInfo; - if (FileUtils::readFile("/proc/cpuinfo", cpuInfo, Logger::getInstance("DAEMON"), true)) - { - QRegularExpression regEx("^model\\s*:\\s(.*)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption); - QRegularExpressionMatch match; - - match = regEx.match(cpuInfo); - if (match.hasMatch()) - { - _sysinfo.cpuModelType = match.captured(1); - } - - regEx.setPattern("^model name\\s*:\\s(.*)"); - match = regEx.match(cpuInfo); - if (match.hasMatch()) - { - _sysinfo.cpuModelName = match.captured(1); - } - - regEx.setPattern("^hardware\\s*:\\s(.*)"); - match = regEx.match(cpuInfo); - if (match.hasMatch()) - { - _sysinfo.cpuHardware = match.captured(1); - } - - regEx.setPattern("^revision\\s*:\\s(.*)"); - match = regEx.match(cpuInfo); - if (match.hasMatch()) - { - _sysinfo.cpuRevision = match.captured(1); - } - - regEx.setPattern("^revision\\s*:\\s(.*)"); - match = regEx.match(cpuInfo); - if (match.hasMatch()) - { - _sysinfo.cpuRevision = match.captured(1); - } - } -} - diff --git a/sources/utils/SystemPerformanceCounters.cpp b/sources/utils/SystemPerformanceCounters.cpp index e43559011..355d976ee 100644 --- a/sources/utils/SystemPerformanceCounters.cpp +++ b/sources/utils/SystemPerformanceCounters.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -24,32 +24,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include -#include -#include -#ifdef _WIN32 +#ifndef PCH_ENABLED + #include + #include -#include -#include -#include -#pragma comment(lib, "pdh.lib") + #include + #include + #include + #include -PDH_HQUERY cpuPerfQuery = nullptr; -PDH_HCOUNTER cpuPerfTotal = nullptr; + #include +#endif -#else +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef _WIN32 + #include + #include + #include + #pragma comment(lib, "pdh.lib") + PDH_HQUERY cpuPerfQuery = nullptr; + PDH_HCOUNTER cpuPerfTotal = nullptr; +#else + #include + #include + #include #endif #ifdef __linux__ @@ -176,6 +177,9 @@ QString SystemPerformanceCounters::getCPU() { try { + QJsonObject jResult; + QJsonArray jCores; + if (!isInitialized) { init(); @@ -191,38 +195,34 @@ QString SystemPerformanceCounters::getCPU() if (PdhGetFormattedCounterArray(cpuPerfTotal, PDH_FMT_LONG, &size, &count, nullptr) != PDH_MORE_DATA) return ""; - PDH_FMT_COUNTERVALUE_ITEM* buffer = (PDH_FMT_COUNTERVALUE_ITEM*)malloc(size); - if (PdhGetFormattedCounterArray(cpuPerfTotal, PDH_FMT_LONG, &size, &count, buffer) != ERROR_SUCCESS) + QByteArray myByteArray(size, 0); + PDH_FMT_COUNTERVALUE_ITEM* bufferData = (PDH_FMT_COUNTERVALUE_ITEM*)myByteArray.data(); + + if (PdhGetFormattedCounterArray(cpuPerfTotal, PDH_FMT_LONG, &size, &count, bufferData) != ERROR_SUCCESS) { - free(buffer); return ""; } - QString retVal, retTotal; - for (DWORD i = 0; i < count; i++) { - auto valCPU = buffer[i].FmtValue.longValue; + auto valCPU = bufferData[i].FmtValue.longValue; #ifdef UNICODE - QString convertedStr = QString::fromWCharArray(buffer[i].szName); + QString convertedStr = QString::fromWCharArray(bufferData[i].szName); #else QString convertedStr = QString::fromLocal8Bit(buffer[i].szName); #endif if (convertedStr != "_Total") - retVal += QString("%1").arg(getChar(valCPU / 100.0f)); + { + jCores.append(valCPU / 100.0f); + } else - retTotal = QString( - (valCPU < 50) ? "%1%" : - ((valCPU < 90) ? "%1%" : - "%1%")).arg(QString::number(valCPU), 2); + { + jResult["total"] = (double)valCPU; + } } - free(buffer); - - return QString("%1 (%2)").arg(retVal).arg(retTotal); - #else if (totalPerfCPU > 0) @@ -231,8 +231,6 @@ QString SystemPerformanceCounters::getCPU() if (readCpuLines() == totalPerfCPU) { - QString retVal, retTotal; - for (int i = -1; i < totalPerfCPU; i++) { int j = (i + 1) * 4; @@ -259,22 +257,20 @@ QString SystemPerformanceCounters::getCPU() if (i >= 0) { - retVal += QString("%1").arg(getChar(valCPU / 100.0f)); + jCores.append(valCPU / 100.0f); } else { - retTotal = QString( - (valCPU < 50) ? "%1%" : - ((valCPU < 90) ? "%1%" : - "%1%")).arg(QString::number(valCPU, 'f', 0), 2); + jResult["total"] = valCPU; } } - - return QString("%1 (%2)").arg(retVal).arg(retTotal); } } #endif + + jResult["cores"] = jCores; + return QJsonDocument(jResult).toJson(QJsonDocument::Compact); } catch (...) { @@ -287,6 +283,8 @@ QString SystemPerformanceCounters::getRAM() { try { + QJsonObject jResult; + if (!isInitialized) { init(); @@ -303,8 +301,6 @@ QString SystemPerformanceCounters::getRAM() qint64 physMemAv = qint64(memInfo.ullAvailPhys) / (1024 * 1024); qint64 takenMem = totalPhysMem - physMemAv; qint64 aspect = (takenMem * 100) / totalPhysMem; - QString color = (aspect < 50) ? "cpu_low_usage" : ((aspect < 90) ? "cpu_medium_usage" : "cpu_high_usage"); - return QString("%1 / %2MB (%4%)").arg(takenMem).arg(totalPhysMem).arg(color).arg(aspect, 2); #else @@ -335,11 +331,13 @@ QString SystemPerformanceCounters::getRAM() physMemAv /= (1024 * 1024); long long takenMem = totalPhysMem - physMemAv; - qint64 aspect = (takenMem * 100) / totalPhysMem; - QString color = (aspect < 50) ? "cpu_low_usage" : ((aspect < 90) ? "cpu_medium_usage" : "cpu_high_usage"); - return QString("%1 / %2MB (%4%)").arg(takenMem).arg(totalPhysMem).arg(color).arg(aspect, 2); - + qint64 aspect = (takenMem * 100) / totalPhysMem; #endif + jResult["totalPhysMem"] = (qint64) totalPhysMem; + jResult["takenMem"] = (qint64)takenMem; + jResult["aspect"] = (qint64) aspect; + + return QJsonDocument(jResult).toJson(QJsonDocument::Compact); } catch (...) { diff --git a/sources/utils/SystemPerformanceCounters.mm b/sources/utils/SystemPerformanceCounters.mm index 20a3907b2..29abcc1d1 100644 --- a/sources/utils/SystemPerformanceCounters.mm +++ b/sources/utils/SystemPerformanceCounters.mm @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -91,7 +91,8 @@ QString SystemPerformanceCounters::getCPU() { - QString result = ""; + QJsonObject jResult; + QJsonArray jCores; try { @@ -125,7 +126,7 @@ valCPU = usage / std::max(total, 0.00001); - retVal += QString("%1").arg(getChar(valCPU)); + jCores.append(valCPU / 100.0f); totUsage += usage; totTotal += total; @@ -136,12 +137,9 @@ valCPU = std::min(std::max(valCPU, 0.0), 100.0); - retTotal = QString( - (valCPU < 50) ? "%1%" : - ((valCPU < 90) ? "%1%" : - "%1%")).arg(QString::number(valCPU, 'f', 0), 2); + jResult["total"] = valCPU; - result = QString("%1 (%2)").arg(retVal).arg(retTotal); + jResult["cores"] = jCores; size_t prevCpuInfoSize = sizeof(integer_t) * prevPerfNum; vm_deallocate(mach_task_self(), (vm_address_t)prevPerfStats, prevCpuInfoSize); @@ -157,7 +155,7 @@ { } - return result; + return QJsonDocument(jResult).toJson(QJsonDocument::Compact); } QString SystemPerformanceCounters::getRAM() @@ -184,11 +182,15 @@ (int64_t)vmStats.inactive_count + (int64_t)vmStats.wire_count) * (int64_t)pageSize; + QJsonObject jResult; + qint64 totalPhysMem = qint64(physicalMemory) / (1024 * 1024); + jResult["totalPhysMem"] = totalPhysMem; qint64 takenMem = qint64(usedMemory) / (1024 * 1024); - qint64 aspect = (takenMem * 100) / totalPhysMem; - QString color = (aspect < 50) ? "ForestGreen" : ((aspect < 90) ? "orange" : "red"); - return QString("%1 / %2MB (%4%)").arg(takenMem).arg(totalPhysMem).arg(color).arg(aspect, 2); + jResult["takenMem"] = takenMem; + jResult["aspect"] = (qint64) (takenMem * 100) / totalPhysMem; + + return QJsonDocument(jResult).toJson(QJsonDocument::Compact); } } } diff --git a/sources/utils/VideoMemoryManager.cpp b/sources/utils/VideoMemoryManager.cpp index 33dd3b3d9..841313f01 100644 --- a/sources/utils/VideoMemoryManager.cpp +++ b/sources/utils/VideoMemoryManager.cpp @@ -2,7 +2,7 @@ * * MIT License * -* Copyright (c) 2023 awawa-dev +* Copyright (c) 2020-2023 awawa-dev * * Project homesite: https://github.com/awawa-dev/HyperHDR * @@ -25,13 +25,12 @@ * SOFTWARE. */ -#include +#ifndef PCH_ENABLED + #include +#endif #define BUFFER_LOWER_LIMIT 1 -bool VideoMemoryManager::_enabled(false); -bool VideoMemoryManager::_dirty(false); - VideoMemoryManager::VideoMemoryManager(int bufferSize) : _currentSize(0), _prevSize(0), @@ -52,14 +51,12 @@ VideoMemoryManager::~VideoMemoryManager() bool VideoMemoryManager::setFrameSize(size_t size) { - if (_currentSize != size || (!_enabled && _dirty)) + if (_currentSize != size) { QMutexLocker lockme(&_locker); releaseBuffer(); - _dirty = false; - _hits = 0; _needed = false; @@ -78,98 +75,81 @@ bool VideoMemoryManager::setFrameSize(size_t size) return false; } -uint8_t* VideoMemoryManager::request(size_t size) +std::unique_ptr> VideoMemoryManager::request(size_t size) { - uint8_t* retVal; - - if (size != _currentSize || !_enabled) + if (size != _currentSize) { - return static_cast(malloc(size)); + return std::unique_ptr>(new MemoryBuffer(size)); } QMutexLocker lockme(&_locker); - if (_stack.isEmpty()) + if (_stack.size() == 0) { - retVal = static_cast(malloc(size)); + return std::unique_ptr>(new MemoryBuffer(size)); } else { _hits++; - retVal = _stack.pop(); + std::unique_ptr> retVal = std::move(_stack.back()); + _stack.pop_back(); - if (_stack.length() <= BUFFER_LOWER_LIMIT) + if (_stack.size() <= BUFFER_LOWER_LIMIT) _needed = true; - } - return retVal; + return retVal; + } } -void VideoMemoryManager::release(size_t size, uint8_t* buffer) +void VideoMemoryManager::release(std::unique_ptr>& frame) { - if (size != _currentSize || !_enabled) + if (frame->size() != _currentSize) { - free(buffer); return; } QMutexLocker lockme(&_locker); - if (_stack.length() >= _bufferLimit) + if (_stack.size() >= _bufferLimit) { - free(buffer); return; } - _stack.push(buffer); + _stack.push_back(std::move(frame)); } void VideoMemoryManager::releaseBuffer() { - while (!_stack.isEmpty()) - { - auto pointer = _stack.pop(); - free(pointer); - } -} - -void VideoMemoryManager::enableCache(bool frameCache) -{ - if (_enabled == frameCache) - return; - - _enabled = frameCache; - - _dirty = true; + _stack.clear(); } QString VideoMemoryManager::adjustCache() { - int curSize = -1, hits = -1; + size_t curSize = 0; + int hits = 0; bool needed = false, cleanup = false; bool info = false; QMutexLocker lockme(&_locker); - if (_prevNeeded != _needed || _prevSize != _stack.length()) + if (_prevNeeded != _needed || _prevSize != _stack.size()) info = true; // clean up buffer if neccesery - if (!_needed && !_prevNeeded && _stack.length() > 0) + if (!_needed && !_prevNeeded && _stack.size() > 0) { - auto pointer = _stack.pop(); - free(pointer); + _stack.pop_back(); cleanup = true; _prevNeeded = true; } else _prevNeeded = _needed; - _prevSize = _stack.length(); + _prevSize = _stack.size(); // get stats - curSize = _stack.length(); + curSize = _stack.size(); hits = _hits; needed = _needed; @@ -179,8 +159,8 @@ QString VideoMemoryManager::adjustCache() _needed = false; if (info || cleanup) - return QString("Video cache: %1, size: %2, hits: %3, needed: %4, cleanup: %5, limit: %6"). - arg((_enabled) ? "enabled" : "disabled").arg(curSize).arg(hits).arg(needed).arg(cleanup).arg(_bufferLimit); + return QString("Video cache: size: %1, hits: %2, needed: %3, cleanup: %4, limit: %5"). + arg(curSize).arg(hits).arg(needed).arg(cleanup).arg(_bufferLimit); else return ""; } diff --git a/sources/utils/jsonschema/QJsonSchemaChecker.cpp b/sources/utils/jsonschema/QJsonSchemaChecker.cpp index 2307b7f26..bbfb8b15d 100644 --- a/sources/utils/jsonschema/QJsonSchemaChecker.cpp +++ b/sources/utils/jsonschema/QJsonSchemaChecker.cpp @@ -1,9 +1,9 @@ -// stdlib includes -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif -// Utils-Jsonschema includes #include #include @@ -223,27 +223,28 @@ void QJsonSchemaChecker::checkProperties(const QJsonObject& value, const QJsonOb for (QJsonObject::const_iterator i = schema.begin(); i != schema.end(); ++i) { QString property = i.key(); - - const QJsonValue& propertyValue = *i; + const QJsonValue& propertyValue = i.value(); + const QJsonObject& propertyValueObj = propertyValue.toObject(); _currentPath.append("." + property); - QJsonObject::const_iterator required = propertyValue.toObject().find("required"); + QJsonObject::const_iterator required = propertyValueObj.find("required"); if (value.contains(property)) { - validate(value[property], propertyValue.toObject()); + validate(value[property], propertyValueObj); } else if (verifyDeps(property, value, schema)) { } - else if (required != propertyValue.toObject().end() && propertyValue.toObject().find("required").value().toBool() && !_ignoreRequired) + else if (required != propertyValueObj.end() && required.value().toBool() && !_ignoreRequired) { _error = true; if (_correct == "create") { - QJsonUtils::modify(_autoCorrected, _currentPath, QJsonUtils::create(propertyValue, _ignoreRequired), property); - setMessage("Create property: " + property + " with value: " + QJsonUtils::getDefaultValue(propertyValue)); + QJsonValue newProperty = QJsonUtils::create(propertyValue, _ignoreRequired); + QJsonUtils::modify(_autoCorrected, _currentPath, newProperty, property); + setMessage("Create property: " + property + " with value: " + QJsonUtils::outputNode(newProperty)); } if (_correct == "") @@ -484,7 +485,7 @@ void QJsonSchemaChecker::checkItems(const QJsonValue& value, const QJsonObject& QJsonArray jArray = value.toArray(); if (_correct == "remove") - if (jArray.isEmpty() && !schema.contains("allowEmptyArray")) + if (jArray.isEmpty() && !schema.contains("allowEmptyArray") && schema.contains("required")) { QJsonUtils::modify(_autoCorrected, _currentPath); setMessage("Remove empty array"); diff --git a/sources/webserver/CgiHandler.cpp b/sources/webserver/CgiHandler.cpp index db955fe2a..e8b6c0856 100644 --- a/sources/webserver/CgiHandler.cpp +++ b/sources/webserver/CgiHandler.cpp @@ -6,9 +6,13 @@ #include #include #include +#include +#include +#include #include "CgiHandler.h" #include "QtHttpHeader.h" +#include "QtHttpReply.h" CgiHandler::CgiHandler(QObject* parent) : QObject(parent) diff --git a/sources/webserver/CgiHandler.h b/sources/webserver/CgiHandler.h index d8cb6227d..8d596c78d 100644 --- a/sources/webserver/CgiHandler.h +++ b/sources/webserver/CgiHandler.h @@ -1,14 +1,15 @@ -#ifndef CGIHANDLER_H -#define CGIHANDLER_H +#pragma once -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include +#endif -#include +class QtHttpReply; +class QtHttpRequest; +class Logger; -#include "QtHttpReply.h" -#include "QtHttpRequest.h" class CgiHandler : public QObject { @@ -21,7 +22,6 @@ class CgiHandler : public QObject void exec(const QStringList& args, QtHttpRequest* request, QtHttpReply* reply); private: - // CGI commands void cmd_cfg_jsonserver(); QtHttpReply* _reply; @@ -30,5 +30,3 @@ class CgiHandler : public QObject QString _baseUrl; Logger* _log; }; - -#endif // CGIHANDLER_H diff --git a/sources/webserver/QtHttpClientWrapper.cpp b/sources/webserver/QtHttpClientWrapper.cpp index 757e19a85..a8bfbd070 100644 --- a/sources/webserver/QtHttpClientWrapper.cpp +++ b/sources/webserver/QtHttpClientWrapper.cpp @@ -1,3 +1,16 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + #include #include "QtHttpClientWrapper.h" #include "QtHttpRequest.h" @@ -7,18 +20,13 @@ #include "WebSocketClient.h" #include "WebJsonRpc.h" -#include -#include -#include -#include -#include - #define REQ "request=" #define RPC "json-rpc" const QByteArray& QtHttpClientWrapper::CRLF = QByteArrayLiteral("\r\n"); -QtHttpClientWrapper::QtHttpClientWrapper(QTcpSocket* sock, const bool& localConnection, QtHttpServer* parent) +QtHttpClientWrapper::QtHttpClientWrapper( + QTcpSocket* sock, const bool& localConnection, QtHttpServer* parent) : QObject(parent) , m_guid("") , m_parsingStatus(AwaitingRequest) @@ -47,6 +55,29 @@ QString QtHttpClientWrapper::getGuid(void) return m_guid; } + +void QtHttpClientWrapper::onReplySendDataRequested(void) +{ + QtHttpReply* reply = qobject_cast (sender()); + if (reply != Q_NULLPTR && m_sockClient != nullptr && m_sockClient->state() == QAbstractSocket::ConnectedState) + { + // content raw data + QByteArray data = reply->getRawData(); + + if (reply->useChunked()) + { + data.prepend(QByteArray::number(data.size(), 16) % CRLF); + data.append(CRLF); + reply->resetRawData(); + } + if (m_sockClient->state() == QAbstractSocket::ConnectedState) + { + m_sockClient->write(data); + m_sockClient->flush(); + } + } +} + void QtHttpClientWrapper::onClientDataReceived(void) { if (m_sockClient != Q_NULLPTR) @@ -120,7 +151,7 @@ void QtHttpClientWrapper::onClientDataReceived(void) } else // end of headers { - if (m_currentRequest->getHeader(QtHttpHeader::ContentLength).toInt() > 0) + if (m_currentRequest->getHeader(QtHttpHeader::ContentLength, 0) > 0) { m_parsingStatus = AwaitingContent; } @@ -136,7 +167,7 @@ void QtHttpClientWrapper::onClientDataReceived(void) { m_currentRequest->appendRawData(line); - if (m_currentRequest->getRawDataSize() == m_currentRequest->getHeader(QtHttpHeader::ContentLength).toInt()) + if (m_currentRequest->getRawDataSize() == m_currentRequest->getHeader(QtHttpHeader::ContentLength, 0)) { m_parsingStatus = RequestParsed; } @@ -251,6 +282,23 @@ void QtHttpClientWrapper::onClientDataReceived(void) } } + +void QtHttpClientWrapper::closeConnection() +{ + // probably filter for request to follow http spec + if (m_currentRequest != Q_NULLPTR) + { + QtHttpReply reply(m_serverHandle); + reply.setStatusCode(QtHttpReply::StatusCode::Forbidden); + + connect(&reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested, Qt::UniqueConnection); + connect(&reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested, Qt::UniqueConnection); + + m_parsingStatus = sendReplyToClient(&reply); + } + m_sockClient->close(); +} + void QtHttpClientWrapper::onReplySendHeadersRequested(void) { QtHttpReply* reply = qobject_cast (sender()); @@ -294,27 +342,6 @@ void QtHttpClientWrapper::onReplySendHeadersRequested(void) } } -void QtHttpClientWrapper::onReplySendDataRequested(void) -{ - QtHttpReply* reply = qobject_cast (sender()); - if (reply != Q_NULLPTR) - { - // content raw data - QByteArray data = reply->getRawData(); - - if (reply->useChunked()) - { - data.prepend(QByteArray::number(data.size(), 16) % CRLF); - data.append(CRLF); - reply->resetRawData(); - } - - // write to socket - m_sockClient->write(data); - m_sockClient->flush(); - } -} - void QtHttpClientWrapper::sendToClientWithReply(QtHttpReply* reply) { connect(reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested, Qt::UniqueConnection); @@ -358,18 +385,3 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient(QtHttp return AwaitingRequest; } -void QtHttpClientWrapper::closeConnection() -{ - // probably filter for request to follow http spec - if (m_currentRequest != Q_NULLPTR) - { - QtHttpReply reply(m_serverHandle); - reply.setStatusCode(QtHttpReply::StatusCode::Forbidden); - - connect(&reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested, Qt::UniqueConnection); - connect(&reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested, Qt::UniqueConnection); - - m_parsingStatus = sendReplyToClient(&reply); - } - m_sockClient->close(); -} diff --git a/sources/webserver/QtHttpClientWrapper.h b/sources/webserver/QtHttpClientWrapper.h index 204dbf48b..dba9844ca 100644 --- a/sources/webserver/QtHttpClientWrapper.h +++ b/sources/webserver/QtHttpClientWrapper.h @@ -1,8 +1,9 @@ -#ifndef QTHTTPCLIENTWRAPPER_H -#define QTHTTPCLIENTWRAPPER_H +#pragma once -#include -#include +#ifndef PCH_ENABLED + #include + #include +#endif class QTcpSocket; @@ -11,6 +12,7 @@ class QtHttpReply; class QtHttpServer; class WebSocketClient; class WebJsonRpc; +class HyperHdrManager; class QtHttpClientWrapper : public QObject { Q_OBJECT @@ -31,12 +33,8 @@ class QtHttpClientWrapper : public QObject { }; QString getGuid(void); - /// @brief Wrapper for sendReplyToClient(), handles m_parsingStatus and signal connect - void sendToClientWithReply(QtHttpReply* reply); - /// - /// @brief close a connection with FORBIDDEN header (used from JsonAPI over HTTP) - /// + void sendToClientWithReply(QtHttpReply* reply); void closeConnection(); private slots: @@ -59,5 +57,3 @@ protected slots: WebSocketClient* m_websocketClient; WebJsonRpc* m_webJsonRpc; }; - -#endif // QTHTTPCLIENTWRAPPER_H diff --git a/sources/webserver/QtHttpHeader.cpp b/sources/webserver/QtHttpHeader.cpp index 1eaf56ae1..e89385b4b 100644 --- a/sources/webserver/QtHttpHeader.cpp +++ b/sources/webserver/QtHttpHeader.cpp @@ -1,4 +1,3 @@ - #include "QtHttpHeader.h" #include diff --git a/sources/webserver/QtHttpHeader.h b/sources/webserver/QtHttpHeader.h index a12aad9a1..eddad0701 100644 --- a/sources/webserver/QtHttpHeader.h +++ b/sources/webserver/QtHttpHeader.h @@ -1,5 +1,4 @@ -#ifndef QTHTTPHEADER_H -#define QTHTTPHEADER_H +#pragma once class QByteArray; @@ -40,5 +39,3 @@ class QtHttpHeader static const QByteArray& SecWebSocketProtocol; static const QByteArray& SecWebSocketVersion; }; - -#endif // QTHTTPHEADER_H diff --git a/sources/webserver/QtHttpReply.cpp b/sources/webserver/QtHttpReply.cpp index 714661fd8..5f02170c0 100644 --- a/sources/webserver/QtHttpReply.cpp +++ b/sources/webserver/QtHttpReply.cpp @@ -1,9 +1,14 @@ +#include +#include +#include +#include +#include +#include #include "QtHttpReply.h" #include "QtHttpHeader.h" #include "QtHttpServer.h" -#include QtHttpReply::QtHttpReply(QtHttpServer* parent) : QObject(parent) @@ -17,6 +22,16 @@ QtHttpReply::QtHttpReply(QtHttpServer* parent) addHeader(QtHttpHeader::Server, m_serverHandle->getServerName().toUtf8()); } +void QtHttpReply::addHeader(const QByteArray& header, const QByteArray& value) +{ + QByteArray key = header.trimmed(); + + if (!key.isEmpty()) + { + m_headersHash.insert(key, value); + } +} + const QByteArray QtHttpReply::getStatusTextForCode(QtHttpReply::StatusCode statusCode) { switch (statusCode) @@ -29,17 +44,6 @@ const QByteArray QtHttpReply::getStatusTextForCode(QtHttpReply::StatusCode statu } } -void QtHttpReply::addHeader(const QByteArray& header, const QByteArray& value) -{ - QByteArray key = header.trimmed(); - - if (!key.isEmpty()) - { - m_headersHash.insert(key, value); - } -} - - int QtHttpReply::getRawDataSize(void) const { return m_data.size(); diff --git a/sources/webserver/QtHttpReply.h b/sources/webserver/QtHttpReply.h index 676278f1b..e99052b35 100644 --- a/sources/webserver/QtHttpReply.h +++ b/sources/webserver/QtHttpReply.h @@ -1,10 +1,11 @@ -#ifndef QTHTTPREPLY_H -#define QTHTTPREPLY_H +#pragma once -#include -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include +#endif class QtHttpServer; @@ -58,5 +59,3 @@ public slots: QtHttpServer* m_serverHandle; QHash m_headersHash; }; - -#endif // QTHTTPREPLY_H diff --git a/sources/webserver/QtHttpRequest.cpp b/sources/webserver/QtHttpRequest.cpp index b71fb3cb3..2de04b082 100644 --- a/sources/webserver/QtHttpRequest.cpp +++ b/sources/webserver/QtHttpRequest.cpp @@ -1,3 +1,8 @@ +#include +#include +#include +#include +#include #include "QtHttpRequest.h" #include "QtHttpHeader.h" @@ -48,16 +53,11 @@ QString QtHttpRequest::getCommand(void) const return m_command; }; -QByteArray QtHttpRequest::getRawData(void) const +QByteArray QtHttpRequest::getRawData(void) const & { return m_data; }; -QList QtHttpRequest::getHeadersList(void) const -{ - return m_headersHash.keys(); -}; - QtHttpClientWrapper* QtHttpRequest::getClient(void) const { return m_clientHandle; @@ -73,11 +73,23 @@ QtHttpRequest::ClientInfo QtHttpRequest::getClientInfo(void) const return m_clientInfo; }; -QByteArray QtHttpRequest::getHeader(const QByteArray& header) const +QByteArray QtHttpRequest::getHeader(const QByteArray& header) const & { return m_headersHash.value(header, QByteArray()); }; +int QtHttpRequest::getHeader(const QByteArray& header, int defValue) +{ + const QByteArray& temp = m_headersHash.value(header, QByteArray()); + bool ok = false; + int resValue = temp.toInt(&ok); + + if (ok) + return resValue; + else + return defValue; +} + void QtHttpRequest::setUrl(const QUrl& url) { diff --git a/sources/webserver/QtHttpRequest.h b/sources/webserver/QtHttpRequest.h index 2cbf256cb..6ca28a017 100644 --- a/sources/webserver/QtHttpRequest.h +++ b/sources/webserver/QtHttpRequest.h @@ -1,13 +1,14 @@ -#ifndef QTHTTPREQUEST_H -#define QTHTTPREQUEST_H - -#include -#include -#include -#include -#include -#include -#include +#pragma once + +#ifndef PCH_ENABLED + #include + #include + #include + #include + #include + #include + #include +#endif class QtHttpServer; class QtHttpClientWrapper; @@ -30,13 +31,13 @@ class QtHttpRequest : public QObject int getRawDataSize(void) const; QUrl getUrl(void) const; QString getCommand(void) const; - QByteArray getRawData(void) const; - QList getHeadersList(void) const; + QByteArray getRawData(void) const &; QtHttpClientWrapper* getClient(void) const; QtHttpPostData getPostData(void) const; ClientInfo getClientInfo(void) const; - QByteArray getHeader(const QByteArray& header) const; + QByteArray getHeader(const QByteArray& header) const &; + int getHeader(const QByteArray& header, int defValue); public slots: void setUrl(const QUrl& url); @@ -57,5 +58,3 @@ public slots: ClientInfo m_clientInfo; QtHttpPostData m_postData; }; - -#endif // QTHTTPREQUEST_H diff --git a/sources/webserver/QtHttpServer.cpp b/sources/webserver/QtHttpServer.cpp index cc5c28246..fc07691a1 100644 --- a/sources/webserver/QtHttpServer.cpp +++ b/sources/webserver/QtHttpServer.cpp @@ -1,13 +1,17 @@ +#include +#include +#include +#include +#include +#include + +#include #include "QtHttpServer.h" #include "QtHttpRequest.h" #include "QtHttpReply.h" #include "QtHttpClientWrapper.h" -#include - -#include - const QString& QtHttpServer::HTTP_VERSION = QStringLiteral("HTTP/1.1"); QtHttpServerWrapper::QtHttpServerWrapper(QObject* parent) @@ -33,11 +37,11 @@ void QtHttpServerWrapper::incomingConnection(qintptr handle) sock->setSocketDescriptor(handle) ? addPendingConnection(sock) : delete sock; } -QtHttpServer::QtHttpServer(QObject* parent) +QtHttpServer::QtHttpServer(std::shared_ptr netOrigin, QObject* parent) : QObject(parent) , m_useSsl(false) , m_serverName(QStringLiteral("The Qt5 HTTP Server")) - , m_netOrigin(NetOrigin::getInstance()) + , m_netOrigin(netOrigin) { m_sockServer = new QtHttpServerWrapper(this); connect(m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected); @@ -69,12 +73,6 @@ void QtHttpServer::stop(void) } } -void QtHttpServer::setUseSecure(const bool ssl) -{ - m_useSsl = ssl; - m_sockServer->setUseSecure(m_useSsl); -} - void QtHttpServer::onClientConnected(void) { while (m_sockServer->hasPendingConnections()) @@ -100,7 +98,8 @@ void QtHttpServer::onClientConnected(void) } } - QtHttpClientWrapper* wrapper = new QtHttpClientWrapper(sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this); + QtHttpClientWrapper* wrapper = new QtHttpClientWrapper( + sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this); m_socksClientsHash.insert(sock, wrapper); emit clientConnected(wrapper->getGuid()); } @@ -125,6 +124,12 @@ void QtHttpServer::onClientDisconnected(void) } } +void QtHttpServer::setUseSecure(const bool ssl) +{ + m_useSsl = ssl; + m_sockServer->setUseSecure(m_useSsl); +} + const QString& QtHttpServer::getServerName(void) const { return m_serverName; diff --git a/sources/webserver/QtHttpServer.h b/sources/webserver/QtHttpServer.h index f99c57cd6..10aad685c 100644 --- a/sources/webserver/QtHttpServer.h +++ b/sources/webserver/QtHttpServer.h @@ -1,22 +1,23 @@ -#ifndef QTHTTPSERVER_H -#define QTHTTPSERVER_H +#pragma once -#include -#include -#include +#ifndef PCH_ENABLED + #include + #include + #include + #include +#endif + +#include #include -#include #include -#include #include -#include class QTcpSocket; -class QTcpServer; class QtHttpRequest; class QtHttpReply; class QtHttpClientWrapper; class NetOrigin; +class HyperHdrManager; class QtHttpServerWrapper : public QTcpServer { @@ -40,7 +41,7 @@ class QtHttpServer : public QObject Q_OBJECT public: - explicit QtHttpServer(QObject* parent = Q_NULLPTR); + explicit QtHttpServer(std::shared_ptr netOrigin, QObject* parent = Q_NULLPTR); static const QString& HTTP_VERSION; @@ -83,9 +84,7 @@ private slots: QSslKey m_sslKey; QList m_sslCerts; QString m_serverName; - NetOrigin* m_netOrigin; + std::shared_ptr m_netOrigin; QtHttpServerWrapper* m_sockServer; QHash m_socksClientsHash; }; - -#endif // QTHTTPSERVER_H diff --git a/sources/webserver/StaticFileServing.cpp b/sources/webserver/StaticFileServing.cpp index e41d54cdd..0def49fdc 100644 --- a/sources/webserver/StaticFileServing.cpp +++ b/sources/webserver/StaticFileServing.cpp @@ -1,6 +1,10 @@ -#include -#include "StaticFileServing.h" - +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -9,70 +13,78 @@ #include #include #include +#include + + +#include + +#include +#include "QtHttpRequest.h" +#include "QtHttpReply.h" +#include "QtHttpHeader.h" +#include "CgiHandler.h" + +#include "StaticFileServing.h" StaticFileServing::StaticFileServing(QObject* parent) : QObject(parent) , _baseUrl() - , _cgi(this) + , _cgi(new CgiHandler(this)) , _log(Logger::getInstance("WEBSERVER")) { Q_INIT_RESOURCE(WebConfig); - _mimeDb = new QMimeDatabase; + _mimeDb["js"] = "application/javascript"; + _mimeDb["html"] = "text/html"; + _mimeDb["json"] = "application/json"; + _mimeDb["css"] = "text/css"; + _mimeDb["png"] = "image/png"; + _mimeDb["woff"] = "font/woff"; + _mimeDb["svg"] = "image/svg+xml"; + _mimeDb["jpg"] = "image/jpeg"; + _mimeDb["jpeg"] = "image/jpeg"; } StaticFileServing::~StaticFileServing() { - delete _mimeDb; + delete _cgi; } void StaticFileServing::setBaseUrl(const QString& url) { _baseUrl = url; - _cgi.setBaseUrl(url); + _cgi->setBaseUrl(url); } -void StaticFileServing::setSSDPDescription(const QString& desc) +void StaticFileServing::setSsdpXmlDesc(const QString& desc) { if (desc.isEmpty()) - _ssdpDescription.clear(); + { + Warning(_log, "SSDP description is empty"); + _ssdpXmlDesc.clear(); + } else - _ssdpDescription = desc.toLocal8Bit(); + { + Debug(_log, "SSDP description is set up"); + _ssdpXmlDesc = desc; + } } void StaticFileServing::printErrorToReply(QtHttpReply* reply, QtHttpReply::StatusCode code, QString errorMessage) { reply->setStatusCode(code); - reply->addHeader("Content-Type", QByteArrayLiteral("text/html")); - QFile errorPageHeader(_baseUrl % "/errorpages/header.html"); - QFile errorPageFooter(_baseUrl % "/errorpages/footer.html"); - QFile errorPage(_baseUrl % "/errorpages/" % QString::number((int)code) % ".html"); - - if (errorPageHeader.open(QFile::ReadOnly)) - { - QByteArray data = errorPageHeader.readAll(); - reply->appendRawData(data); - errorPageHeader.close(); - } + reply->addHeader("Content-Type", QByteArrayLiteral("text/html")); + reply->appendRawData(QString(QString::number(code) + " - " + errorMessage).toLocal8Bit()); +} - if (errorPage.open(QFile::ReadOnly)) - { - QByteArray data = errorPage.readAll(); - data = data.replace("{MESSAGE}", errorMessage.toLocal8Bit()); - reply->appendRawData(data); - errorPage.close(); - } - else - { - reply->appendRawData(QString(QString::number(code) + " - " + errorMessage).toLocal8Bit()); - } +QString StaticFileServing::getMimeName(QString filename) +{ + QString extension = QFileInfo(filename).suffix(); - if (errorPageFooter.open(QFile::ReadOnly)) - { - QByteArray data = errorPageFooter.readAll(); - reply->appendRawData(data); - errorPageFooter.close(); - } + if (extension.isEmpty() || !_mimeDb.contains(extension)) + return QMimeDatabase().mimeTypeForFile(filename).name(); + + return _mimeDb[extension]; } void StaticFileServing::onRequestNeedsReply(QtHttpRequest* request, QtHttpReply* reply) @@ -90,7 +102,7 @@ void StaticFileServing::onRequestNeedsReply(QtHttpRequest* request, QtHttpReply* uri_parts.removeAt(0); try { - _cgi.exec(uri_parts, request, reply); + _cgi->exec(uri_parts, request, reply); } catch (std::exception& e) { @@ -99,10 +111,10 @@ void StaticFileServing::onRequestNeedsReply(QtHttpRequest* request, QtHttpReply* } return; } - else if (uri_parts.at(0) == "description.xml" && !_ssdpDescription.isNull()) + else if (uri_parts.at(0) == "description.xml" && !_ssdpXmlDesc.isEmpty()) { reply->addHeader("Content-Type", "text/xml"); - reply->appendRawData(_ssdpDescription); + reply->appendRawData(_ssdpXmlDesc.toLocal8Bit()); return; } } @@ -125,10 +137,10 @@ void StaticFileServing::onRequestNeedsReply(QtHttpRequest* request, QtHttpReply* QFile file(_baseUrl % "/" % path); if (file.exists()) { - QMimeType mime = _mimeDb->mimeTypeForFile(file.fileName()); + QString mimeName = getMimeName(file.fileName()); if (file.open(QFile::ReadOnly)) { QByteArray data = file.readAll(); - reply->addHeader("Content-Type", mime.name().toLocal8Bit()); + reply->addHeader("Content-Type", mimeName.toLocal8Bit()); reply->addHeader(QtHttpHeader::AccessControlAllow, "*"); reply->appendRawData(data); file.close(); diff --git a/sources/webserver/StaticFileServing.h b/sources/webserver/StaticFileServing.h index 8619c338f..1d218aebb 100644 --- a/sources/webserver/StaticFileServing.h +++ b/sources/webserver/StaticFileServing.h @@ -1,15 +1,10 @@ -#ifndef STATICFILESERVING_H -#define STATICFILESERVING_H +#pragma once -#include - -//#include "QtHttpServer.h" -#include "QtHttpRequest.h" #include "QtHttpReply.h" -#include "QtHttpHeader.h" -#include "CgiHandler.h" -#include +class CgiHandler; +class QtHttpRequest; + class StaticFileServing : public QObject { @@ -19,28 +14,19 @@ class StaticFileServing : public QObject explicit StaticFileServing(QObject* parent = nullptr); ~StaticFileServing() override; - /// - /// @brief Overwrite current base url - /// void setBaseUrl(const QString& url); - /// - /// @brief Set a new SSDP description, if empty the description will be unset and clients will get a NotFound - /// @param The description - /// - void setSSDPDescription(const QString& desc); + void setSsdpXmlDesc(const QString& desc); public slots: void onRequestNeedsReply(QtHttpRequest* request, QtHttpReply* reply); private: QString _baseUrl; - QMimeDatabase* _mimeDb; - CgiHandler _cgi; + QHash _mimeDb; + CgiHandler* _cgi; Logger* _log; - QByteArray _ssdpDescription; + QString _ssdpXmlDesc; void printErrorToReply(QtHttpReply* reply, QtHttpReply::StatusCode code, QString errorMessage); - + QString getMimeName(QString filename); }; - -#endif // STATICFILESERVING_H diff --git a/sources/webserver/WebJsonRpc.cpp b/sources/webserver/WebJsonRpc.cpp index 828f3f966..538b73a8d 100644 --- a/sources/webserver/WebJsonRpc.cpp +++ b/sources/webserver/WebJsonRpc.cpp @@ -1,10 +1,16 @@ +#include +#include +#include +#include +#include + #include "WebJsonRpc.h" #include "QtHttpReply.h" #include "QtHttpRequest.h" #include "QtHttpServer.h" #include "QtHttpClientWrapper.h" -#include +#include #define MULTI_REQ "&request=" @@ -15,10 +21,24 @@ WebJsonRpc::WebJsonRpc(QtHttpRequest* request, QtHttpServer* server, bool localC , _log(Logger::getInstance("HTTPJSONRPC")) { const QString client = request->getClientInfo().clientAddress.toString(); - _jsonAPI = new JsonAPI(client, _log, localConnection, this, true); - connect(_jsonAPI, &JsonAPI::callbackMessage, this, &WebJsonRpc::handleCallback); - connect(_jsonAPI, &JsonAPI::forceClose, [&]() { _wrapper->closeConnection(); _stopHandle = true; }); - _jsonAPI->initialize(); + _hyperAPI = new HyperAPI(client, _log, localConnection, this, true); + connect(_hyperAPI, &HyperAPI::SignalCallbackJsonMessage, this, &WebJsonRpc::handleCallback); + connect(_hyperAPI, &HyperAPI::SignalPerformClientDisconnection, [&]() { _wrapper->closeConnection(); _stopHandle = true; }); + _hyperAPI->initialize(); +} + + +void WebJsonRpc::handleCallback(QJsonObject obj) +{ + // guard against wrong callbacks; TODO: Remove when JSONAPI is more solid + if (!_unlocked) return; + _unlocked = false; + // construct reply with headers timestamp and server name + QtHttpReply reply(_server); + QJsonDocument doc(obj); + reply.addHeader("Content-Type", "application/json"); + reply.appendRawData(doc.toJson()); + _wrapper->sendToClientWithReply(&reply); } void WebJsonRpc::handleMessage(QtHttpRequest* request, QString query) @@ -30,14 +50,14 @@ void WebJsonRpc::handleMessage(QtHttpRequest* request, QString query) if (nextQueryIndex < 0) { - QByteArray header = request->getHeader("Authorization"); - QByteArray data = (query.length() > 0) ? query.toUtf8() : request->getRawData(); + const QByteArray& header = request->getHeader("Authorization"); + const QByteArray& data = (query.length() > 0) ? query.toUtf8() : request->getRawData(); _unlocked = true; - _jsonAPI->handleMessage(data, header); + _hyperAPI->handleMessage(data, header); } else { - QByteArray header = request->getHeader("Authorization"); + const QByteArray& header = request->getHeader("Authorization"); _unlocked = true; query = QString(MULTI_REQ).append(query); @@ -49,22 +69,10 @@ void WebJsonRpc::handleMessage(QtHttpRequest* request, QString query) QString leftQuery = (nextQueryIndex >= 0) ? query.left(nextQueryIndex) : query; query = query.right(query.length() - leftQuery.length()); - _jsonAPI->handleMessage(leftQuery.toUtf8(), header); + _hyperAPI->handleMessage(leftQuery.toUtf8(), header); } while (nextQueryIndex >= 0 && query.length() > QString(MULTI_REQ).length()); } } } -void WebJsonRpc::handleCallback(QJsonObject obj) -{ - // guard against wrong callbacks; TODO: Remove when JSONAPI is more solid - if (!_unlocked) return; - _unlocked = false; - // construct reply with headers timestamp and server name - QtHttpReply reply(_server); - QJsonDocument doc(obj); - reply.addHeader("Content-Type", "application/json"); - reply.appendRawData(doc.toJson()); - _wrapper->sendToClientWithReply(&reply); -} diff --git a/sources/webserver/WebJsonRpc.h b/sources/webserver/WebJsonRpc.h index 2da6ad64d..a05363869 100644 --- a/sources/webserver/WebJsonRpc.h +++ b/sources/webserver/WebJsonRpc.h @@ -1,13 +1,16 @@ #pragma once -#include +#ifndef PCH_ENABLED + #include -#include + #include +#endif class QtHttpServer; class QtHttpRequest; class QtHttpClientWrapper; -class JsonAPI; +class HyperAPI; +class HyperHdrManager; class WebJsonRpc : public QObject { Q_OBJECT @@ -20,8 +23,8 @@ class WebJsonRpc : public QObject { private: QtHttpServer* _server; QtHttpClientWrapper* _wrapper; - Logger* _log; - JsonAPI* _jsonAPI; + Logger* _log; + HyperAPI* _hyperAPI; bool _stopHandle = false; bool _unlocked = false; diff --git a/sources/webserver/WebServer.cpp b/sources/webserver/WebServer.cpp index 8a603b5c8..b0550836f 100644 --- a/sources/webserver/WebServer.cpp +++ b/sources/webserver/WebServer.cpp @@ -1,41 +1,63 @@ -#include "webserver/WebServer.h" -#include "HyperhdrConfig.h" -#include "StaticFileServing.h" -#include "QtHttpServer.h" - +#include +#include +#include +#include +#include #include #include #include #include +#include "QtHttpRequest.h" +#include "webserver/WebServer.h" +#include "HyperhdrConfig.h" +#include "StaticFileServing.h" +#include "QtHttpServer.h" + // bonjour #ifdef ENABLE_BONJOUR - #include + #include #endif // netUtil #include -WebServer::WebServer(const QJsonDocument& config, bool useSsl, QObject* parent) +WebServer::WebServer(std::shared_ptr netOrigin, const QJsonDocument& config, bool useSsl, QObject* parent) : QObject(parent) , _port(0) , _config(config) , _useSsl(useSsl) , _log(Logger::getInstance("WEBSERVER")) - , _server() + , _staticFileServing(nullptr) + , _server(nullptr) + , _netOrigin(netOrigin) { } WebServer::~WebServer() { + Debug(_log, "Prepare to shutdown"); stop(); + Debug(_log, "Webserver instance is closed"); +} + +void WebServer::start() +{ + if (_server != nullptr) + _server->start(_port); +} + +void WebServer::stop() +{ + if (_server != nullptr) + _server->stop(); } void WebServer::initServer() { Info(_log, "Initialize Webserver"); - _server = new QtHttpServer(this); + _server = new QtHttpServer(_netOrigin, this); _server->setServerName(QString("HyperHDR WebServer %1").arg((!_useSsl) ? "(HTTP)" : "(HTTPS)")); if (_useSsl) @@ -58,8 +80,6 @@ void WebServer::initServer() void WebServer::onServerStarted(quint16 port) { - _inited = true; - Info(_log, "Started: '%s' on port: %d", QSTRING_CSTR(_server->getServerName()), port); #ifdef ENABLE_BONJOUR @@ -202,32 +222,6 @@ void WebServer::handleSettingsUpdate(settings::type type, QJsonDocument config) } } -void WebServer::start() -{ - _server->start(_port); -} - -void WebServer::stop() -{ - _server->stop(); -} - -void WebServer::setSSDPDescription(const QString& desc) -{ - _staticFileServing->setSSDPDescription(desc); -} - -void WebServer::onServerError(QString msg) -{ - Error(_log, "%s", QSTRING_CSTR(msg)); -} - -void WebServer::onServerStopped() -{ - Info(_log, "Stopped: %s", QSTRING_CSTR(_server->getServerName())); - emit stateChange(false); -} - bool WebServer::portAvailable(quint16& port, Logger* log) { const quint16 prevPort = port; @@ -245,3 +239,20 @@ bool WebServer::portAvailable(quint16& port, Logger* log) } return true; } + +void WebServer::setSsdpXmlDesc(const QString& desc) +{ + _staticFileServing->setSsdpXmlDesc(desc); +} + +void WebServer::onServerError(QString msg) +{ + Error(_log, "%s", QSTRING_CSTR(msg)); +} + +void WebServer::onServerStopped() +{ + Info(_log, "Stopped: %s", QSTRING_CSTR(_server->getServerName())); + emit stateChange(false); +} + diff --git a/sources/webserver/WebSocketClient.cpp b/sources/webserver/WebSocketClient.cpp index 0e5d6f634..074ca72fd 100644 --- a/sources/webserver/WebSocketClient.cpp +++ b/sources/webserver/WebSocketClient.cpp @@ -1,16 +1,24 @@ -#include "WebSocketClient.h" -#include "QtHttpRequest.h" -#include "QtHttpHeader.h" - -#include -#include - #include +#include +#include +#include +#include #include #include #include -WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool localConnection, QObject* parent) +#include +#include +#include + +#include "WebSocketClient.h" +#include "QtHttpRequest.h" +#include "QtHttpHeader.h" +#include "WebSocketUtils.h" + + +WebSocketClient::WebSocketClient( + QtHttpRequest* request, QTcpSocket* sock, bool localConnection, QObject* parent) : QObject(parent) , _socket(sock) , _log(Logger::getInstance("WEBSOCKET")) @@ -23,9 +31,10 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool const QString client = request->getClientInfo().clientAddress.toString(); // Json processor - _jsonAPI = new JsonAPI(client, _log, localConnection, this); - connect(_jsonAPI, &JsonAPI::callbackMessage, this, &WebSocketClient::sendMessage); - connect(_jsonAPI, &JsonAPI::forceClose, this, [this]() { this->sendClose(CLOSECODE::NORMAL); }); + _hyperAPI = new HyperAPI(client, _log, localConnection, this); + connect(_hyperAPI, &HyperAPI::SignalCallbackJsonMessage, this, &WebSocketClient::sendMessage); + connect(_hyperAPI, &HyperAPI::SignalCallbackBinaryImageMessage, this, &WebSocketClient::signalCallbackBinaryImageMessageHandler); + connect(_hyperAPI, &HyperAPI::SignalPerformClientDisconnection, this, [this]() { this->sendClose(CLOSECODE::NORMAL); }); Debug(_log, "New connection from %s", QSTRING_CSTR(client)); @@ -42,7 +51,25 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool _socket->flush(); // Init JsonAPI - _jsonAPI->initialize(); + _hyperAPI->initialize(); +} + +void WebSocketClient::handleBinaryMessage(QByteArray& data) +{ + unsigned imgSize = data.size() - 4; + unsigned width = ((data.at(2) << 8) & 0xFF00) | (data.at(3) & 0xFF); + unsigned height = imgSize / width; + + if (imgSize % width > 0) + { + Error(_log, "data size is not multiple of width"); + return; + } + + Image image; + image.resize(width, height); + + memcpy(image.rawMem(), data.data() + 4, imgSize); } void WebSocketClient::handleWebSocketFrame() @@ -117,7 +144,7 @@ void WebSocketClient::handleWebSocketFrame() if (_wsh.opCode == OPCODE::TEXT) { - _jsonAPI->handleMessage(QString(_wsReceiveBuffer)); + _hyperAPI->handleMessage(QString(_wsReceiveBuffer)); } else { @@ -156,46 +183,6 @@ void WebSocketClient::handleWebSocketFrame() } } -void WebSocketClient::getWsFrameHeader(WebSocketHeader* header) -{ - char fin_rsv_opcode, mask_length; - _socket->getChar(&fin_rsv_opcode); - _socket->getChar(&mask_length); - - header->fin = (fin_rsv_opcode & BHB0_FIN) == BHB0_FIN; - header->opCode = fin_rsv_opcode & BHB0_OPCODE; - header->masked = (mask_length & BHB1_MASK) == BHB1_MASK; - header->payloadLength = mask_length & BHB1_PAYLOAD; - - // get size of payload - switch (header->payloadLength) - { - case payload_size_code_16bit: - { - QByteArray buf = _socket->read(2); - header->payloadLength = ((buf.at(0) << 8) & 0xFF00) | (buf.at(1) & 0xFF); - } - break; - - case payload_size_code_64bit: - { - QByteArray buf = _socket->read(8); - header->payloadLength = 0; - for (uint i = 0; i < 8; i++) - { - header->payloadLength |= ((quint64)(buf.at(i) & 0xFF)) << (8 * (7 - i)); - } - } - break; - } - - // if the data is masked we need to get the key for unmasking - if (header->masked) - { - _socket->read(header->key, 4); - } -} - void WebSocketClient::sendClose(int status, QString reason) { Debug(_log, "send close: %d %s", status, QSTRING_CSTR(reason)); @@ -227,40 +214,20 @@ void WebSocketClient::sendClose(int status, QString reason) _socket->close(); } - -void WebSocketClient::handleBinaryMessage(QByteArray& data) -{ - unsigned imgSize = data.size() - 4; - unsigned width = ((data.at(2) << 8) & 0xFF00) | (data.at(3) & 0xFF); - unsigned height = imgSize / width; - - if (imgSize % width > 0) - { - Error(_log, "data size is not multiple of width"); - return; - } - - Image image; - image.resize(width, height); - - memcpy(image.rawMem(), data.data() + 4, imgSize); -} - - -qint64 WebSocketClient::sendMessage(QJsonObject obj) +qint64 WebSocketClient::sendMessage(const QJsonObject& obj) { QJsonDocument writer(obj); QByteArray data = writer.toJson(QJsonDocument::Compact) + "\n"; if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) return 0; - + qint64 payloadWritten = 0; quint32 payloadSize = data.size(); const char* payload = data.data(); qint32 numFrames = payloadSize / FRAME_SIZE_IN_BYTES + ((quint64(payloadSize) % FRAME_SIZE_IN_BYTES) > 0 ? 1 : 0); - + for (int i = 0; i < numFrames; i++) { const bool isLastFrame = (i == (numFrames - 1)); @@ -268,7 +235,7 @@ qint64 WebSocketClient::sendMessage(QJsonObject obj) quint64 position = i * FRAME_SIZE_IN_BYTES; quint32 frameSize = (payloadSize - position >= FRAME_SIZE_IN_BYTES) ? FRAME_SIZE_IN_BYTES : (payloadSize - position); quint8 headerType = (i) ? OPCODE::CONTINUATION : OPCODE::TEXT; - QByteArray buf = makeFrameHeader(headerType, frameSize, isLastFrame); + QByteArray buf = makeFrameHeader(headerType, frameSize, isLastFrame); sendMessage_Raw(buf); qint64 written = sendMessage_Raw(payload + position, frameSize); @@ -290,9 +257,90 @@ qint64 WebSocketClient::sendMessage(QJsonObject obj) return -1; } - if (obj.contains("isImage")) + return payloadWritten; +} + +void WebSocketClient::getWsFrameHeader(WebSocketHeader* header) +{ + char fin_rsv_opcode, mask_length; + _socket->getChar(&fin_rsv_opcode); + _socket->getChar(&mask_length); + + header->fin = (fin_rsv_opcode & BHB0_FIN) == BHB0_FIN; + header->opCode = fin_rsv_opcode & BHB0_OPCODE; + header->masked = (mask_length & BHB1_MASK) == BHB1_MASK; + header->payloadLength = mask_length & BHB1_PAYLOAD; + + // get size of payload + switch (header->payloadLength) { - QUEUE_CALL_0(_jsonAPI, releaseLock); + case payload_size_code_16bit: + { + QByteArray buf = _socket->read(2); + header->payloadLength = ((buf.at(0) << 8) & 0xFF00) | (buf.at(1) & 0xFF); + } + break; + + case payload_size_code_64bit: + { + QByteArray buf = _socket->read(8); + header->payloadLength = 0; + for (uint i = 0; i < 8; i++) + { + header->payloadLength |= ((quint64)(buf.at(i) & 0xFF)) << (8 * (7 - i)); + } + } + break; + } + + // if the data is masked we need to get the key for unmasking + if (header->masked) + { + _socket->read(header->key, 4); + } +} + +qint64 WebSocketClient::signalCallbackBinaryImageMessageHandler(Image image) +{ + if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) + return 0; + + MemoryBuffer mb; + FrameDecoder::encodeJpeg(mb, image, (image.width() > 800)); + + qint64 payloadWritten = 0; + quint32 payloadSize = static_cast(mb.size()); + char* payload = reinterpret_cast(mb.data()); + + qint32 numFrames = payloadSize / FRAME_SIZE_IN_BYTES + ((quint64(payloadSize) % FRAME_SIZE_IN_BYTES) > 0 ? 1 : 0); + + for (int i = 0; i < numFrames; i++) + { + const bool isLastFrame = (i == (numFrames - 1)); + + quint64 position = i * FRAME_SIZE_IN_BYTES; + quint32 frameSize = (payloadSize - position >= FRAME_SIZE_IN_BYTES) ? FRAME_SIZE_IN_BYTES : (payloadSize - position); + quint8 headerType = (i) ? OPCODE::CONTINUATION : OPCODE::BINARY; + QByteArray buf = makeFrameHeader(headerType, frameSize, isLastFrame); + sendMessage_Raw(buf); + + qint64 written = sendMessage_Raw(payload + position, frameSize); + if (written > 0) + { + payloadWritten += written; + } + else + { + _socket->flush(); + Error(_log, "Error writing bytes to socket: %s", QSTRING_CSTR(_socket->errorString())); + break; + } + } + + if (payloadSize != payloadWritten) + { + Error(_log, "Error writing bytes to socket %d bytes from %d written", payloadWritten, payloadSize); + return -1; } return payloadWritten; diff --git a/sources/webserver/WebSocketClient.h b/sources/webserver/WebSocketClient.h index a5cf4844e..067772600 100644 --- a/sources/webserver/WebSocketClient.h +++ b/sources/webserver/WebSocketClient.h @@ -1,12 +1,14 @@ #pragma once -#include -#include "WebSocketUtils.h" +#ifndef PCH_ENABLED + #include + #include + #include +#endif class QTcpSocket; - class QtHttpRequest; -class JsonAPI; +class HyperAPI; class WebSocketClient : public QObject { @@ -25,9 +27,9 @@ class WebSocketClient : public QObject }; private: - QTcpSocket* _socket; - Logger* _log; - JsonAPI* _jsonAPI; + QTcpSocket* _socket; + Logger* _log; + HyperAPI* _hyperAPI; void getWsFrameHeader(WebSocketHeader* header); void sendClose(int status, QString reason = ""); @@ -36,22 +38,17 @@ class WebSocketClient : public QObject qint64 sendMessage_Raw(QByteArray& data); QByteArray makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame); - /// The buffer used for reading data from the socket - QByteArray _receiveBuffer; - /// buffer for websockets multi frame receive + QByteArray _receiveBuffer; QByteArray _wsReceiveBuffer; quint8 _maskKey[4]; bool _onContinuation = false; - - // true when data is missing for parsing bool _notEnoughData = false; - // websocket header store - WebSocketHeader _wsh; + WebSocketHeader _wsh{}; + - // masks for fields in the basic header static uint8_t const BHB0_OPCODE = 0x0F; static uint8_t const BHB0_RSV3 = 0x10; static uint8_t const BHB0_RSV2 = 0x20; @@ -68,5 +65,6 @@ class WebSocketClient : public QObject private slots: void handleWebSocketFrame(); - qint64 sendMessage(QJsonObject obj); + qint64 sendMessage(const QJsonObject& obj); + qint64 signalCallbackBinaryImageMessageHandler(Image image); }; diff --git a/sources/webserver/WebSocketUtils.h b/sources/webserver/WebSocketUtils.h index d8a5c2e9a..2a25ad088 100644 --- a/sources/webserver/WebSocketUtils.h +++ b/sources/webserver/WebSocketUtils.h @@ -1,9 +1,20 @@ #pragma once -/// Constants and utility functions related to WebSocket opcodes -/** - * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2. - */ +namespace CLOSECODE +{ + enum value + { + NORMAL = 1000, + AWAY = 1001, + TERM = 1002, + INV_TYPE = 1003, + INV_DATA = 1007, + VIOLATION = 1008, + BIG_MSG = 1009, + UNEXPECTED = 1011 + }; +} + namespace OPCODE { enum value @@ -26,30 +37,8 @@ namespace OPCODE CONTROL_RSVF = 0xF }; - /// Check if an opcode is invalid - /** - * Invalid opcodes are negative or require greater than 4 bits to store. - * - * @param v The opcode to test. - * @return Whether or not the opcode is invalid. - */ inline bool invalid(value v) { return (v > 0xF || v < 0); } } - -namespace CLOSECODE -{ - enum value - { - NORMAL = 1000, - AWAY = 1001, - TERM = 1002, - INV_TYPE = 1003, - INV_DATA = 1007, - VIOLATION = 1008, - BIG_MSG = 1009, - UNEXPECTED = 1011 - }; -} diff --git a/version b/version index 58d46c29f..778bd13f6 100644 --- a/version +++ b/version @@ -1 +1 @@ -20.0.0.0beta0 +20.0.0.0beta1 diff --git a/www/content/about.html b/www/content/about.html index 9fe9f1452..a90bf1181 100644 --- a/www/content/about.html +++ b/www/content/about.html @@ -71,7 +71,6 @@