mirror of
				https://github.com/hyperion-project/hyperion.ng.git
				synced 2025-03-01 10:33:28 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into config
This commit is contained in:
		
							
								
								
									
										9
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
								
							| @@ -36,7 +36,12 @@ jobs: | ||||
|         if: ${{ matrix.language == 'cpp' }} | ||||
|         run: | | ||||
|           sudo apt-get update | ||||
|           sudo apt-get install --yes git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev | ||||
|           sudo apt-get install --yes git build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev  | ||||
|  | ||||
|       - name: Temporarily downgrade CMake to 3.28.3 # Please remove if GitHub has updated Cmake (greater than 3.30.0) | ||||
|         uses: jwlawson/actions-setup-cmake@v2 | ||||
|         with: | ||||
|           cmake-version: '3.28.3'           | ||||
|  | ||||
|       - name: 🔁 Initialize CodeQL | ||||
|         uses: github/codeql-action/init@v3 | ||||
| @@ -44,7 +49,7 @@ jobs: | ||||
|           languages: ${{ matrix.language }} | ||||
|           queries: +security-and-quality | ||||
|           config-file: ./.github/config/codeql.yml | ||||
|  | ||||
|            | ||||
|       - name: 👷 Autobuild | ||||
|         uses: github/codeql-action/autobuild@v3 | ||||
|  | ||||
|   | ||||
							
								
								
									
										33
									
								
								.github/workflows/qt5_6.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/qt5_6.yml
									
									
									
									
										vendored
									
									
								
							| @@ -117,9 +117,14 @@ jobs: | ||||
|           echo '::group::Update/Install dependencies' | ||||
|             brew untap --force homebrew/core homebrew/cask | ||||
|             brew update || true | ||||
|             brew install qt${{ inputs.qt_version }} vulkan-headers ninja || true | ||||
|             brew install qt@${{ inputs.qt_version }} vulkan-headers ninja libftdi || true | ||||
|           echo '::endgroup::' | ||||
|  | ||||
|       - name: Temporarily downgrade CMake to 3.28.3 # Please remove if GitHub has updated Cmake (greater than 3.30.0) | ||||
|         uses: jwlawson/actions-setup-cmake@v2 | ||||
|         with: | ||||
|           cmake-version: '3.28.3' | ||||
|  | ||||
|       - name: 👷 Build | ||||
|         shell: bash | ||||
|         run: ./.github/scripts/build.sh | ||||
| @@ -163,23 +168,30 @@ jobs: | ||||
|         uses: actions/cache@v4 | ||||
|         with: | ||||
|           path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey | ||||
|           key: ${{ runner.os }}${{ inputs.qt_version == '6' && '-chocolatey-qt6' || '-chocolatey' }} | ||||
|           key: ${{ runner.os }}${{ '-chocolatey' }} | ||||
|  | ||||
|       - name: 📥 Install DirectX SDK, OpenSSL, libjpeg-turbo ${{ inputs.qt_version == '6' && 'and Vulkan-SDK' || '' }} | ||||
|       - name: 📥 Install DirectX SDK, OpenSSL, libjpeg-turbo | ||||
|         shell: powershell | ||||
|         run: | | ||||
|           choco install --no-progress directx-sdk ${{env.VULKAN_SDK}} -y | ||||
|           choco install --no-progress directx-sdk -y | ||||
|           choco install --no-progress ${{env.OPENSSL}} -y | ||||
|           Invoke-WebRequest https://netcologne.dl.sourceforge.net/project/libjpeg-turbo/3.0.1/libjpeg-turbo-3.0.1-vc64.exe -OutFile libjpeg-turbo.exe -UserAgent NativeHost | ||||
|           .\libjpeg-turbo /S | ||||
|         env: | ||||
|           VULKAN_SDK: ${{ inputs.qt_version == '6' && 'vulkan-sdk' || '' }} | ||||
|           OPENSSL: ${{ inputs.qt_version == '6' && 'openssl' || 'openssl --version=1.1.1.2100' }} | ||||
|  | ||||
|       - name: 📥 Install Qt | ||||
|         uses: jurplel/install-qt-action@v3 | ||||
|       - name: Install Vulkan SDK | ||||
|         if: ${{ inputs.qt_version == '6' }} | ||||
|         uses: jakoch/install-vulkan-sdk-action@v1.0.4 | ||||
|         with: | ||||
|           version: ${{ inputs.qt_version == '6' && '6.5.2' || '5.15.2' }} | ||||
|           install_runtime: false | ||||
|           cache: true | ||||
|           stripdown: true | ||||
|  | ||||
|       - name: 📥 Install Qt | ||||
|         uses: jurplel/install-qt-action@v4 | ||||
|         with: | ||||
|           version: ${{ inputs.qt_version == '6' && '6.7' || '5.15.*' }} | ||||
|           target: 'desktop' | ||||
|           modules: ${{ inputs.qt_version == '6' && 'qtserialport' || '' }} | ||||
|           arch: 'win64_msvc2019_64' | ||||
| @@ -190,6 +202,11 @@ jobs: | ||||
|         shell: cmd | ||||
|         run: call "${{env.VCINSTALLDIR}}\Auxiliary\Build\vcvars64.bat" | ||||
|  | ||||
|       - name: Temporarily downgrade CMake to 3.28.3 # Please remove if GitHub has updated Cmake (greater than 3.30.0) | ||||
|         uses: jwlawson/actions-setup-cmake@v2 | ||||
|         with: | ||||
|           cmake-version: '3.28.3' | ||||
|  | ||||
|       - name: 👷 Build | ||||
|         shell: bash | ||||
|         run: ./.github/scripts/build.sh | ||||
|   | ||||
							
								
								
									
										10
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -13,13 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ||||
|  | ||||
| ### Added | ||||
|  | ||||
| - Support for ftdi chip based LED-devices with ws2812, sk6812 apa102 LED types (Many thanks to @nurikk) (#1746) | ||||
| - Support for Skydimo devices (being an Adalight variant) | ||||
| - Support gaps on Matrix Layout (#1696) | ||||
| - Windows: Added a new grabber that uses the DXGI DDA (Desktop Duplication API). This has much better performance than the DX grabber as it does more of its work on the GPU. | ||||
|  | ||||
| **JSON-API** | ||||
| - New subscription support for event updates, i.e. `Suspend, Resume, Idle, idleResume, Restart, Quit`. | ||||
| - Support direct or multiple instance addressing via single requests (#809) | ||||
| - Support of `serverinfo` subcommands: `getInfo, subscribe, unsubscribe, getSubscriptions, getSubscriptionCommands` | ||||
| - [Overview](https://github.com/hyperion-project/hyperion.ng/blob/API_Auth/doc/development/JSON-API%20_Commands_Overview.md) of API commands and subscription updates  | ||||
| - [Overview](https://github.com/hyperion-project/hyperion.ng/blob/API_Auth/doc/development/JSON-API%20_Commands_Overview.md) of API commands and subscription updates | ||||
|  | ||||
| ### Changed | ||||
|  | ||||
| @@ -29,12 +32,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ||||
| - Workaround to address Web UI keeps forcing browser to download the html instead (#1692) | ||||
| - Fixed: Kodi Color Calibration, Refactor Wizards (#1674) | ||||
| - Fixed: Token Dialog not closing | ||||
| - Fixed: Philip Hue APIv2 support without Entertainment group defined (#1742) | ||||
|  | ||||
| **JSON-API** | ||||
| - Refactored JSON-API to ensure consistent authorization behaviour across sessions and single requests with token authorization. | ||||
| - Provide additional error details with API responses, esp. on JSON parsing, validation or token errors. | ||||
| - Generate random TANs for every API request from the Hyperion UI | ||||
| - Fixed: Handling of IP4 addresses wrapped in IPv6 for external network connections-  | ||||
| - Fixed: Handling of IP4 addresses wrapped in IPv6 for external network connections- | ||||
|  | ||||
| ### Removed | ||||
|  | ||||
| @@ -134,7 +138,7 @@ Note: The wizard will configure an APIv2 capable bridge always with Entertainmen | ||||
| - Support streaming to individual WLED segments (requires WLED 0.13.3+). | ||||
| To allow segment streaming, enable "Realtime - Use main segment only" in WLED's Sync Interfaces setup screen | ||||
| - Allow to keep WLED powered on after streaming and restoring state | ||||
| - Allow to Disable / Enable all instances (#970) by  | ||||
| - Allow to Disable / Enable all instances (#970) by | ||||
| 	- Suspend/Resume support for Linux and Windows (#1493,#1282, #978). | ||||
| 	Suspend/Resume/Restart is supported via API, UI, Systray and hyperion-remote | ||||
| 	- Idle scenario via Screen Locking (Linux/Windows), Screensaver invokation (Linux), hyperion-remote or API | ||||
|   | ||||
| @@ -68,6 +68,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) | ||||
| set(DEFAULT_AMLOGIC                     OFF) | ||||
| set(DEFAULT_DISPMANX                    OFF) | ||||
| set(DEFAULT_DX                          OFF) | ||||
| set(DEFAULT_DDA                         OFF) | ||||
| set(DEFAULT_MF                          OFF) | ||||
| set(DEFAULT_OSX                         OFF) | ||||
| set(DEFAULT_QT                          ON ) | ||||
| @@ -93,6 +94,7 @@ set(DEFAULT_DEV_SPI                     OFF) | ||||
| set(DEFAULT_DEV_TINKERFORGE             OFF) | ||||
| set(DEFAULT_DEV_USB_HID                 OFF) | ||||
| set(DEFAULT_DEV_WS281XPWM               OFF) | ||||
| set(DEFAULT_DEV_FTDI                    ON ) | ||||
|  | ||||
| # Services | ||||
| set(DEFAULT_EFFECTENGINE                ON ) | ||||
| @@ -120,8 +122,10 @@ if(${CMAKE_SYSTEM} MATCHES "Linux") | ||||
| 	set(DEFAULT_DEV_USB_HID     ON) | ||||
| 	set(DEFAULT_CEC             ON) | ||||
| elseif (WIN32) | ||||
| 	set(DEFAULT_DX ON) | ||||
| 	set(DEFAULT_MF ON) | ||||
| 	set(DEFAULT_DX              ON ) | ||||
|   set(DEFAULT_DDA             ON ) | ||||
| 	set(DEFAULT_MF              ON ) | ||||
| 	set(DEFAULT_DEV_FTDI        OFF) | ||||
| else() | ||||
| 	set(DEFAULT_FB              OFF) | ||||
| 	set(DEFAULT_V4L2            OFF) | ||||
| @@ -227,6 +231,7 @@ if(HYPERION_LIGHT) | ||||
| 	SET ( DEFAULT_AMLOGIC                     OFF ) | ||||
| 	SET ( DEFAULT_DISPMANX                    OFF ) | ||||
| 	SET ( DEFAULT_DX                          OFF ) | ||||
| 	SET ( DEFAULT_DDA                         OFF ) | ||||
| 	SET ( DEFAULT_FB                          OFF ) | ||||
| 	SET ( DEFAULT_MF                          OFF ) | ||||
| 	SET ( DEFAULT_OSX                         OFF ) | ||||
| @@ -264,6 +269,9 @@ message(STATUS "ENABLE_DISPMANX = ${ENABLE_DISPMANX}") | ||||
| option(ENABLE_DX "Enable the DirectX grabber" ${DEFAULT_DX}) | ||||
| message(STATUS "ENABLE_DX = ${ENABLE_DX}") | ||||
|  | ||||
| option(ENABLE_DDA "Enable the DXGI DDA grabber" ${DEFAULT_DDA}) | ||||
| message(STATUS "ENABLE_DDA = ${ENABLE_DDA}") | ||||
|  | ||||
| if(ENABLE_AMLOGIC) | ||||
| 	set(ENABLE_FB ON) | ||||
| else() | ||||
| @@ -347,6 +355,9 @@ message(STATUS "ENABLE_DEV_USB_HID = ${ENABLE_DEV_USB_HID}") | ||||
| option(ENABLE_DEV_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_DEV_WS281XPWM}) | ||||
| message(STATUS "ENABLE_DEV_WS281XPWM = ${ENABLE_DEV_WS281XPWM}") | ||||
|  | ||||
| option(ENABLE_DEV_FTDI "Enable the FTDI devices" ${DEFAULT_DEV_FTDI} ) | ||||
| message(STATUS "ENABLE_DEV_FTDI = ${ENABLE_DEV_FTDI}") | ||||
|  | ||||
| removeIndent() | ||||
|  | ||||
| message(STATUS "Services options:") | ||||
|   | ||||
| @@ -9,6 +9,9 @@ | ||||
| // Define to enable the DirectX grabber | ||||
| #cmakedefine ENABLE_DX | ||||
|  | ||||
| // Define to enable the DXGI DDA (Desktop Duplication API) grabber | ||||
| #cmakedefine ENABLE_DDA | ||||
|  | ||||
| // Define to enable the framebuffer grabber | ||||
| // Define to enable the Audio grabber | ||||
| #cmakedefine ENABLE_AUDIO | ||||
|   | ||||
| @@ -159,8 +159,9 @@ | ||||
|     "conf_leds_note_layout_overwrite": "Achtung: Überschreiben erzeugt ein Standardlayout für {{plural:$1| eine LED| alle $1 LEDs}} gemäß der gegebenen Hardware LED-Anzahl", | ||||
|     "conf_leds_optgroup_RPiGPIO": "RPi GPIO", | ||||
|     "conf_leds_optgroup_RPiPWM": "RPi PWM", | ||||
|     "conf_leds_optgroup_RPiSPI": "RPi SPI", | ||||
|     "conf_leds_optgroup_SPI": "SPI", | ||||
|     "conf_leds_optgroup_debug": "Debug", | ||||
|     "conf_leds_optgroup_ftdi": "USB/Ftdi", | ||||
|     "conf_leds_optgroup_network": "Netzwerk", | ||||
|     "conf_leds_optgroup_other": "Andere", | ||||
|     "conf_leds_optgroup_usb": "USB/Seriell", | ||||
| @@ -191,6 +192,7 @@ | ||||
|     "conf_network_tok_diaTitle": "Neues Token erstellt!", | ||||
|     "conf_network_tok_grantMsg": "Eine App fordert Zugriff auf die Hyperion API durch ein Token. Möchtest du dies zulassen? Bitte überprüfe die angegebenen Informationen!", | ||||
|     "conf_network_tok_grantT": "App Token angefordert", | ||||
|     "conf_network_tok_idhead": "ID", | ||||
|     "conf_network_tok_intro": "Hier kannst du Token zur API-Authentifizierung erstellen oder löschen. Neu erstellte Token werden einmalig angezeigt.", | ||||
|     "conf_network_tok_lastuse": "Zuletzt genutzt", | ||||
|     "conf_network_tok_title": "Token Management", | ||||
| @@ -611,6 +613,11 @@ | ||||
|     "edt_conf_webc_sslport_title": "HTTPS Port", | ||||
|     "edt_dev_auth_key_title": "Autorisierungs-Token", | ||||
|     "edt_dev_auth_key_title_info": "Autorisierungs-Token für den Zugriff auf das Gerät erforderlich", | ||||
|     "edt_dev_enum_auto": "Auto", | ||||
|     "edt_dev_enum_auto_accurate": "Auto genau", | ||||
|     "edt_dev_enum_auto_max": "Auto maximal", | ||||
|     "edt_dev_enum_cold_white": "Kaltweiß", | ||||
|     "edt_dev_enum_neutral_white": "Neutralweiß", | ||||
|     "edt_dev_enum_sub_min_cool_adjust": "Minimale Anpassung: cool", | ||||
|     "edt_dev_enum_sub_min_warm_adjust": "Minimale Anpassung: warm", | ||||
|     "edt_dev_enum_subtract_minimum": "Subtrahiere Minimum", | ||||
| @@ -735,7 +742,7 @@ | ||||
|     "edt_dev_spec_username_title": "Benutzername", | ||||
|     "edt_dev_spec_verbose_title": "Protokollierung der HUE-Kommandos", | ||||
|     "edt_dev_spec_vid_title": "VID", | ||||
|     "edt_dev_spec_whiteLedAlgor_title": "Weiß Algorithmus", | ||||
|     "edt_dev_spec_whiteLedAlgor_title": "Weißabgleich Algorithmus", | ||||
|     "edt_dev_spec_whitepoint_title": "Weißpunkt", | ||||
|     "edt_eff_alarmcolor": "Alarm Farbe", | ||||
|     "edt_eff_backgroundColor": "Hintergrundfarbe", | ||||
| @@ -962,6 +969,7 @@ | ||||
|     "general_country_us": "Amerika", | ||||
|     "general_disabled": "deaktiviert", | ||||
|     "general_enabled": "aktiviert", | ||||
|     "general_speech_bg": "Bulgarisch", | ||||
|     "general_speech_ca": "Katalanisch", | ||||
|     "general_speech_cs": "Tschechisch", | ||||
|     "general_speech_da": "Dänisch", | ||||
|   | ||||
| @@ -161,11 +161,12 @@ | ||||
|     "conf_leds_note_layout_overwrite": "Note: Overwrite creates a default layout for {{plural:$1| one LED| all $1 LEDs}} given by the hardware LED count", | ||||
|     "conf_leds_optgroup_RPiGPIO": "RPi GPIO", | ||||
|     "conf_leds_optgroup_RPiPWM": "RPi PWM", | ||||
|     "conf_leds_optgroup_RPiSPI": "RPi SPI", | ||||
|     "conf_leds_optgroup_SPI": "SPI", | ||||
|     "conf_leds_optgroup_debug": "Debug", | ||||
|     "conf_leds_optgroup_network": "Network", | ||||
|     "conf_leds_optgroup_other": "Other", | ||||
|     "conf_leds_optgroup_usb": "USB/Serial", | ||||
|     "conf_leds_optgroup_ftdi": "USB/Ftdi", | ||||
|     "conf_logging_btn_autoscroll": "Auto scrolling", | ||||
|     "conf_logging_btn_clipboard": "Copy Log to Clipboard", | ||||
|     "conf_logging_btn_pbupload": "Upload a report for support requests", | ||||
| @@ -619,6 +620,11 @@ | ||||
|     "edt_dev_enum_sub_min_cool_adjust": "Subtract cool white", | ||||
|     "edt_dev_enum_sub_min_warm_adjust": "Subtract warm white", | ||||
|     "edt_dev_enum_subtract_minimum": "Subtract minimum", | ||||
|     "edt_dev_enum_cold_white": "Cold white", | ||||
|     "edt_dev_enum_neutral_white": "Neutral white", | ||||
|     "edt_dev_enum_auto": "Auto", | ||||
|     "edt_dev_enum_auto_max": "Auto max", | ||||
|     "edt_dev_enum_auto_accurate": "Auto accurate", | ||||
|     "edt_dev_enum_white_off": "White off", | ||||
|     "edt_dev_general_autostart_title": "Autostart", | ||||
|     "edt_dev_general_autostart_title_info": "The LED device is switched-on during startup or not", | ||||
| @@ -700,6 +706,7 @@ | ||||
|     "edt_dev_spec_port_expl": "Service Port [1-65535]", | ||||
|     "edt_dev_spec_port_title": "Port", | ||||
|     "edt_dev_spec_printTimeStamp_title": "Add timestamp", | ||||
|     "edt_dev_spec_skydimo_mode_title": "Skydimo Mode", | ||||
|     "edt_dev_spec_stream_protocol_title": "Streaming protocol", | ||||
|     "edt_dev_spec_pwmChannel_title": "PWM channel", | ||||
|     "edt_dev_spec_razer_device_title": "Razer Chroma Device", | ||||
|   | ||||
| @@ -159,8 +159,9 @@ | ||||
|     "conf_leds_note_layout_overwrite": "Varning: Åsidosättande skapar en standardlayout för {{plural:$1| en LED| varje $1 lysdioder}} enligt det givna antalet lysdioder för hårdvara", | ||||
|     "conf_leds_optgroup_RPiGPIO": "RPi GPIO", | ||||
|     "conf_leds_optgroup_RPiPWM": "RPi PWM", | ||||
|     "conf_leds_optgroup_RPiSPI": "RPi SPI", | ||||
|     "conf_leds_optgroup_SPI": "SPI", | ||||
|     "conf_leds_optgroup_debug": "Felsöka", | ||||
|     "conf_leds_optgroup_ftdi": "USB/Ftdi", | ||||
|     "conf_leds_optgroup_network": "Nätverk", | ||||
|     "conf_leds_optgroup_other": "Annat", | ||||
|     "conf_leds_optgroup_usb": "USB/Seriell", | ||||
| @@ -612,6 +613,11 @@ | ||||
|     "edt_conf_webc_sslport_title": "HTTPS-Port", | ||||
|     "edt_dev_auth_key_title": "Auktorisationsnyckel", | ||||
|     "edt_dev_auth_key_title_info": "Auktorisationsnyckel krävs för att få åtkomst till enheten", | ||||
|     "edt_dev_enum_auto": "Auto", | ||||
|     "edt_dev_enum_auto_accurate": "Auto noggrann", | ||||
|     "edt_dev_enum_auto_max": "Auto max", | ||||
|     "edt_dev_enum_cold_white": "Kallvitt", | ||||
|     "edt_dev_enum_neutral_white": "Neutralvitt", | ||||
|     "edt_dev_enum_sub_min_cool_adjust": "Minsta justering: kall", | ||||
|     "edt_dev_enum_sub_min_warm_adjust": "Minsta justering: varm", | ||||
|     "edt_dev_enum_subtract_minimum": "Subtrahera minimum", | ||||
| @@ -963,6 +969,7 @@ | ||||
|     "general_country_us": "USA", | ||||
|     "general_disabled": "Inaktiverad", | ||||
|     "general_enabled": "Aktiverad", | ||||
|     "general_speech_bg": "Bulgariska", | ||||
|     "general_speech_ca": "Katalanska", | ||||
|     "general_speech_cs": "Tjeckiska", | ||||
|     "general_speech_da": "Danska", | ||||
|   | ||||
| @@ -18,7 +18,8 @@ var bottomRight2bottomLeft = null; | ||||
| var bottomLeft2topLeft = null; | ||||
| var toggleKeystoneCorrectionArea = false; | ||||
|  | ||||
| var devRPiSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2812spi']; | ||||
| var devSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2812spi']; | ||||
| var devFTDI = ['apa102_ftdi', 'sk6812_ftdi', 'ws2812_ftdi']; | ||||
| var devRPiPWM = ['ws281x']; | ||||
| var devRPiGPIO = ['piblaster']; | ||||
| var devNET = ['atmoorb', 'cololight', 'fadecandy', 'philipshue', 'nanoleaf', 'razer', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udpddp', 'udph801', 'udpraw', 'wled', 'yeelight']; | ||||
| @@ -1121,6 +1122,12 @@ $(document).ready(function () { | ||||
|         case "karate": | ||||
|         case "sedu": | ||||
|         case "tpm2": | ||||
|  | ||||
|         //FTDI devices | ||||
|         case "apa102_ftdi": | ||||
|         case "sk6812_ftdi": | ||||
|         case "ws2812_ftdi": | ||||
|  | ||||
|           if (storedAccess === 'expert') { | ||||
|             filter.discoverAll = true; | ||||
|           } | ||||
| @@ -1139,6 +1146,7 @@ $(document).ready(function () { | ||||
|             .catch(error => { | ||||
|               showNotification('danger', "Device discovery for " + ledType + " failed with error:" + error); | ||||
|             }); | ||||
|  | ||||
|           break; | ||||
|  | ||||
|         case "philipshue": { | ||||
| @@ -1441,6 +1449,9 @@ $(document).ready(function () { | ||||
|           case "sk9822": | ||||
|           case "ws2812spi": | ||||
|           case "piblaster": | ||||
|           case "apa102_ftdi": | ||||
|           case "sk6812_ftdi": | ||||
|           case "ws2812_ftdi": | ||||
|           default: | ||||
|         } | ||||
|  | ||||
| @@ -1657,9 +1668,10 @@ $(document).ready(function () { | ||||
|   optArr[3] = []; | ||||
|   optArr[4] = []; | ||||
|   optArr[5] = []; | ||||
|   optArr[6] = []; | ||||
|  | ||||
|   for (var idx = 0; idx < ledDevices.length; idx++) { | ||||
|     if ($.inArray(ledDevices[idx], devRPiSPI) != -1) | ||||
|     if ($.inArray(ledDevices[idx], devSPI) != -1) | ||||
|       optArr[0].push(ledDevices[idx]); | ||||
|     else if ($.inArray(ledDevices[idx], devRPiPWM) != -1) | ||||
|       optArr[1].push(ledDevices[idx]); | ||||
| @@ -1671,8 +1683,12 @@ $(document).ready(function () { | ||||
|       optArr[4].push(ledDevices[idx]); | ||||
|     else if ($.inArray(ledDevices[idx], devHID) != -1) | ||||
|       optArr[4].push(ledDevices[idx]); | ||||
|     else if (ledDevices[idx].endsWith("_ftdi")) { | ||||
|       var title = ledDevices[idx].replace('_ftdi',''); | ||||
|       optArr[5].push(ledDevices[idx] + ":" + title); | ||||
|     } | ||||
|     else | ||||
|       optArr[5].push(ledDevices[idx]); | ||||
|       optArr[6].push(ledDevices[idx]); | ||||
|   } | ||||
|  | ||||
|   $("#leddevices").append(createSel(optArr[0], $.i18n('conf_leds_optgroup_RPiSPI'))); | ||||
| @@ -1680,9 +1696,10 @@ $(document).ready(function () { | ||||
|   $("#leddevices").append(createSel(optArr[2], $.i18n('conf_leds_optgroup_RPiGPIO'))); | ||||
|   $("#leddevices").append(createSel(optArr[3], $.i18n('conf_leds_optgroup_network'))); | ||||
|   $("#leddevices").append(createSel(optArr[4], $.i18n('conf_leds_optgroup_usb'))); | ||||
|   $("#leddevices").append(createSel(optArr[5], $.i18n('conf_leds_optgroup_ftdi'), true)); | ||||
|  | ||||
|   if (storedAccess === 'expert' || window.serverConfig.device.type === "file") { | ||||
|     $("#leddevices").append(createSel(optArr[5], $.i18n('conf_leds_optgroup_other'))); | ||||
|     $("#leddevices").append(createSel(optArr[6], $.i18n('conf_leds_optgroup_other'))); | ||||
|   } | ||||
|  | ||||
|   $("#leddevices").val(window.serverConfig.device.type); | ||||
| @@ -1886,6 +1903,9 @@ function saveLedConfig(genDefLayout = false) { | ||||
|     case "sk9822": | ||||
|     case "ws2812spi": | ||||
|     case "piblaster": | ||||
|     case "apa102_ftdi": | ||||
|     case "sk6812_ftdi": | ||||
|     case "ws2812_ftdi": | ||||
|     default: | ||||
|       if (genDefLayout === true) { | ||||
|         ledConfig = { | ||||
| @@ -1938,8 +1958,10 @@ var updateOutputSelectList = function (ledType, discoveryInfo) { | ||||
|     ledTypeGroup = "devNET"; | ||||
|   } else if ($.inArray(ledType, devSerial) != -1) { | ||||
|     ledTypeGroup = "devSerial"; | ||||
|   } else if ($.inArray(ledType, devRPiSPI) != -1) { | ||||
|     ledTypeGroup = "devRPiSPI"; | ||||
|   } else if ($.inArray(ledType, devSPI) != -1) { | ||||
|     ledTypeGroup = "devSPI"; | ||||
|   } else if ($.inArray(ledType, devFTDI) != -1) { | ||||
|     ledTypeGroup = "devFTDI"; | ||||
|   } else if ($.inArray(ledType, devRPiGPIO) != -1) { | ||||
|     ledTypeGroup = "devRPiGPIO"; | ||||
|   } else if ($.inArray(ledType, devRPiPWM) != -1) { | ||||
| @@ -2062,7 +2084,63 @@ var updateOutputSelectList = function (ledType, discoveryInfo) { | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
|     case "devRPiSPI": | ||||
|  | ||||
|     case "devFTDI": | ||||
|       key = "output"; | ||||
|  | ||||
|       if (discoveryInfo.devices.length == 0) { | ||||
|         enumVals.push("NONE"); | ||||
|         enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none')); | ||||
|         $('#btn_submit_controller').prop('disabled', true); | ||||
|         showAllDeviceInputOptions(key, false); | ||||
|       } | ||||
|       else { | ||||
|         switch (ledType) { | ||||
|           case "ws2812_ftdi": | ||||
|           case "sk6812_ftdi": | ||||
|           case "apa102_ftdi": | ||||
|             for (const device of discoveryInfo.devices) { | ||||
|               enumVals.push(device.ftdiOpenString); | ||||
|  | ||||
|               var title = "FTDI"; | ||||
|               if (device.manufacturer) { | ||||
|                 title = device.manufacturer; | ||||
|               } | ||||
|  | ||||
|               if (device.serialNumber) { | ||||
|                 title += " - " + device.serialNumber; | ||||
|               } | ||||
|               title += " (" + device.vendorIdentifier + "|" + device.productIdentifier + ")"; | ||||
|  | ||||
|               if (device.description) { | ||||
|                 title += " " + device.description; | ||||
|               } | ||||
|  | ||||
|               enumTitleVals.push(title); | ||||
|             } | ||||
|  | ||||
|             // Select configured device | ||||
|             var configuredDeviceType = window.serverConfig.device.type; | ||||
|             var configuredOutput = window.serverConfig.device.output; | ||||
|             if (ledType === configuredDeviceType) { | ||||
|               if ($.inArray(configuredOutput, enumVals) != -1) { | ||||
|                 enumDefaultVal = configuredOutput; | ||||
|               } else { | ||||
|                 enumVals.push(window.serverConfig.device.output); | ||||
|                 enumDefaultVal = configuredOutput; | ||||
|               } | ||||
|             } | ||||
|             else { | ||||
|               addSelect = true; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|           default: | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
|  | ||||
|     case "devSPI": | ||||
|     case "devRPiGPIO": | ||||
|       key = "output"; | ||||
|  | ||||
| @@ -2128,7 +2206,6 @@ var updateOutputSelectList = function (ledType, discoveryInfo) { | ||||
| async function discover_device(ledType, params) { | ||||
|  | ||||
|   const result = await requestLedDeviceDiscovery(ledType, params); | ||||
|  | ||||
|   var discoveryResult = {}; | ||||
|   if (result) { | ||||
|     if (result.error) { | ||||
|   | ||||
| @@ -502,7 +502,9 @@ const philipshueWizard = (() => { | ||||
|  | ||||
|           let serviceID; | ||||
|           if (isAPIv2Ready) { | ||||
|             serviceID = lightLocation.service.rid; | ||||
|             if (lightLocation) { | ||||
|               serviceID = lightLocation.service.rid; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           if (position.startsWith("entertainment")) { | ||||
| @@ -531,7 +533,7 @@ const philipshueWizard = (() => { | ||||
|             // Layout per manual settings | ||||
|             let maxSegments = 1; | ||||
|  | ||||
|             if (isAPIv2Ready) { | ||||
|             if (isAPIv2Ready && serviceID) { | ||||
|               const service = hueEntertainmentServices.find(service => service.id === serviceID); | ||||
|               maxSegments = service.segments.max_segments; | ||||
|             } | ||||
| @@ -593,10 +595,10 @@ const philipshueWizard = (() => { | ||||
|       d.enableAttempts = parseInt(conf_editor.getEditor("root.generalOptions.enableAttempts").getValue()); | ||||
|       d.enableAttemptsInterval = parseInt(conf_editor.getEditor("root.generalOptions.enableAttemptsInterval").getValue()); | ||||
|  | ||||
|       d.useEntertainmentAPI = isEntertainmentReady; | ||||
|       d.useEntertainmentAPI = isEntertainmentReady && (d.groupId !== ""); | ||||
|       d.useAPIv2 = isAPIv2Ready; | ||||
|  | ||||
|       if (isEntertainmentReady) { | ||||
|       if (d.useEntertainmentAPI) { | ||||
|         d.hardwareLedCount = channelNumber; | ||||
|         if (window.serverConfig.device.type !== d.type) { | ||||
|           //smoothing on, if new device | ||||
| @@ -803,12 +805,18 @@ const philipshueWizard = (() => { | ||||
|           "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" | ||||
|         ]; | ||||
|  | ||||
|         if (isEntertainmentReady) { | ||||
|         if (isEntertainmentReady && hueEntertainmentConfigs.length > 0) { | ||||
|           lightOptions.unshift("entertainment_center"); | ||||
|           lightOptions.unshift("entertainment"); | ||||
|         } else { | ||||
|           lightOptions.unshift("disabled"); | ||||
|           groupLights = Object.keys(hueLights); | ||||
|           if (isAPIv2Ready) { | ||||
|             for (const light in hueLights) { | ||||
|               groupLights.push(hueLights[light].id); | ||||
|             } | ||||
|           } else { | ||||
|             groupLights = Object.keys(hueLights); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         $('.lidsb').html(""); | ||||
|   | ||||
							
								
								
									
										42
									
								
								dependencies/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								dependencies/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -24,36 +24,19 @@ if (ENABLE_MDNS) | ||||
| 	if(USE_SYSTEM_QMDNS_LIBS) | ||||
| 		find_package(qmdnsengine REQUIRED) | ||||
| 	else() | ||||
| 		include(ExternalProject) | ||||
| 		ExternalProject_Add(qmdns | ||||
| 			PREFIX				${CMAKE_CURRENT_BINARY_DIR}/external/qmdnsengine | ||||
| 			BUILD_ALWAYS		OFF | ||||
| 			DOWNLOAD_COMMAND	"" | ||||
| 			SOURCE_DIR			${CMAKE_CURRENT_SOURCE_DIR}/external/qmdnsengine | ||||
| 			BINARY_DIR			${CMAKE_CURRENT_BINARY_DIR}/external/qmdnsengine/bin | ||||
| 			CMAKE_ARGS			-DBUILD_SHARED_LIBS:BOOL=OFF | ||||
| 								-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR} | ||||
| 								-DBIN_INSTALL_DIR:STRING=lib | ||||
| 								-DLIB_INSTALL_DIR:STRING=lib | ||||
| 								-DINCLUDE_INSTALL_DIR:STRING=include | ||||
| 								-DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH} | ||||
| 								-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} | ||||
| 								-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} | ||||
| 								-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} | ||||
| 								-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} | ||||
| 								-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} | ||||
| 								-Wno-dev # We don't want to be warned over unused variables | ||||
| 			INSTALL_DIR			${CMAKE_BINARY_DIR} | ||||
| 			BUILD_BYPRODUCTS    <INSTALL_DIR>/lib/${CMAKE_STATIC_LIBRARY_PREFIX}qmdnsengine${CMAKE_STATIC_LIBRARY_SUFFIX} | ||||
| 		) | ||||
|  		# Build QMdnsEngine as static library | ||||
| 		set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build statically version of QMdnsEngine") | ||||
|  | ||||
| 		add_library(qmdnsengine STATIC IMPORTED GLOBAL) | ||||
| 		add_dependencies(qmdnsengine qmdns) | ||||
| 		ExternalProject_Get_Property(qmdns INSTALL_DIR) | ||||
| 		set_target_properties(qmdnsengine PROPERTIES | ||||
| 			IMPORTED_LOCATION "${INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}qmdnsengine${CMAKE_STATIC_LIBRARY_SUFFIX}" | ||||
| 			INTERFACE_INCLUDE_DIRECTORIES "${INSTALL_DIR}/include" | ||||
| 		) | ||||
| 		# Suppress warnings about "Compatibility with CMake < 3.5 will be removed from a future version of CMake" | ||||
| 		set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) | ||||
|  | ||||
| 		# Add QMdnsEngine directory to the build | ||||
| 		add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external/qmdnsengine") | ||||
| 	endif() | ||||
|  | ||||
| 	if(TARGET qmdnsengine AND NOT TARGET qmdns) | ||||
| 		add_library(qmdns INTERFACE IMPORTED GLOBAL) | ||||
| 		target_link_libraries(qmdns INTERFACE qmdnsengine) | ||||
| 	endif() | ||||
| endif() | ||||
|  | ||||
| @@ -149,6 +132,7 @@ if(ENABLE_PROTOBUF_SERVER) | ||||
| 		set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf with tests") | ||||
| 		set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "Build protobuf shared") | ||||
| 		set(protobuf_WITH_ZLIB OFF CACHE BOOL "Build protobuf with zlib support") | ||||
| 		set(protobuf_BUILD_LIBUPB OFF CACHE BOOL "Build libupb") | ||||
|  | ||||
| 		if (WIN32) | ||||
| 			set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Build protobuf static") | ||||
|   | ||||
							
								
								
									
										2
									
								
								dependencies/external/flatbuffers
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								dependencies/external/flatbuffers
									
									
									
									
										vendored
									
									
								
							 Submodule dependencies/external/flatbuffers updated: 0100f6a577...595bf0007a
									
								
							
							
								
								
									
										2
									
								
								dependencies/external/mbedtls
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								dependencies/external/mbedtls
									
									
									
									
										vendored
									
									
								
							 Submodule dependencies/external/mbedtls updated: edb8fec988...2ca6c285a0
									
								
							
							
								
								
									
										2
									
								
								dependencies/external/protobuf
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								dependencies/external/protobuf
									
									
									
									
										vendored
									
									
								
							 Submodule dependencies/external/protobuf updated: 7f94235e55...3d9f7c430a
									
								
							| @@ -61,14 +61,14 @@ cd $HYPERION_HOME | ||||
|  | ||||
| ```console | ||||
| sudo apt-get update | ||||
| sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev | ||||
| sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev | ||||
| ``` | ||||
|  | ||||
| **Ubuntu (22.04+) - Qt6 based** | ||||
|  | ||||
| ```console | ||||
| sudo apt-get update | ||||
| sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libxkbcommon-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config | ||||
| sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libxkbcommon-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config libftdi1-dev | ||||
| ``` | ||||
|  | ||||
| **For Linux X11/XCB grabber support** | ||||
| @@ -110,16 +110,21 @@ See [AUR](https://aur.archlinux.org/packages/?O=0&SeB=nd&K=hyperion&outdated=&SB | ||||
| The following dependencies are needed to build hyperion.ng on fedora. | ||||
| ```console | ||||
| sudo dnf -y groupinstall "Development Tools" | ||||
| sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel | ||||
| sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel libftdi1-dev | ||||
| ``` | ||||
| After installing the dependencies, you can continue with the compile instructions later on this page (the more detailed way..). | ||||
|  | ||||
| ## OSX | ||||
| ## macOS | ||||
| To install on OS X you either need [Homebrew](https://brew.sh/) or [Macport](https://www.macports.org/) but Homebrew is the recommended way to install the packages. To use Homebrew, XCode is required as well, use `brew doctor` to check your install. | ||||
|  | ||||
| First you need to install the dependencies: | ||||
| First you need to install the dependencies for either the QT5 or QT6 build: | ||||
| ####QT5 | ||||
| ```console | ||||
| brew install git qt@5 python3 cmake libusb openssl@1.1 | ||||
| brew install git qt@5 python3 cmake libusb openssl@1.1 libftdi pkg-config | ||||
| ``` | ||||
| ####QT6 | ||||
| ```console | ||||
| brew install git qt python3 cmake libusb openssl@1.1 libftdi pkg-config | ||||
| ``` | ||||
|  | ||||
| ## Windows | ||||
| @@ -128,7 +133,7 @@ We assume a 64bit Windows 10. Install the following; | ||||
| - [CMake (Windows win64-x64 installer)](https://cmake.org/download/) (Check: Add to PATH) | ||||
| - [Visual Studio 2022 Community Edition](https://visualstudio.microsoft.com/downloads/#visual-studio-community-2022) | ||||
|   - Select 'Desktop development with C++' | ||||
|   - On the right, just select `MSVC v143 VS 2022 C++ x64/x86-Buildtools` and latest `Windows 10 SDK`. Everything else is not needed. | ||||
|   - On the right, just select `MSVC v143 VS 2022 C++ x64/x86-Buildtools`, `C++ ATL for latest v143 build tools (x86 & x64)` and latest `Windows 10 SDK`. Everything else is not needed. | ||||
| - [Win64 OpenSSL v1.1.1w](https://slproweb.com/products/Win32OpenSSL.html) ([direct link](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe)) | ||||
| - [Python 3 (Windows x86-64 executable installer)](https://www.python.org/downloads/windows/) (Check: Add to PATH and Debug Symbols) | ||||
|   - Open a console window and execute `pip install aqtinstall`. | ||||
| @@ -147,7 +152,7 @@ We assume a 64bit Windows 10. Install the following; | ||||
|  | ||||
| ## The general quick way (without big comments) | ||||
|  | ||||
| **complete automated process for Mac/Linux:** | ||||
| **complete automated process (Linux only):** | ||||
| ```console | ||||
| wget -qO- https://raw.githubusercontent.com/hyperion-project/hyperion.ng/master/bin/compile.sh | sh | ||||
| ``` | ||||
|   | ||||
| @@ -27,6 +27,10 @@ | ||||
| #include <grabber/directx/directXGrabber.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef ENABLE_DDA | ||||
| #include <grabber/dda/DDAGrabber.h> | ||||
| #endif | ||||
|  | ||||
| #if defined(ENABLE_X11) | ||||
| #include <grabber/x11/X11Grabber.h> | ||||
| #endif | ||||
| @@ -35,10 +39,6 @@ | ||||
| #include <grabber/xcb/XcbGrabber.h> | ||||
| #endif | ||||
|  | ||||
| #if defined(ENABLE_DX) | ||||
| #include <grabber/directx/DirectXGrabber.h> | ||||
| #endif | ||||
|  | ||||
| #if defined(ENABLE_FB) | ||||
| #include <grabber/framebuffer/FramebufferFrameGrabber.h> | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										75
									
								
								include/grabber/dda/DDAGrabber.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								include/grabber/dda/DDAGrabber.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QObject> | ||||
| #include <QJsonObject> | ||||
|  | ||||
| #include <hyperion/Grabber.h> | ||||
| #include <utils/ColorRgb.h> | ||||
|  | ||||
| class DDAGrabberImpl; | ||||
|  | ||||
| class DDAGrabber : public Grabber | ||||
| { | ||||
| public: | ||||
| 	DDAGrabber(int display = 0, int cropLeft = 0, int cropRight = 0, int cropTop = 0, int cropBottom = 0); | ||||
|  | ||||
| 	virtual ~DDAGrabber(); | ||||
|  | ||||
| 	/// | ||||
| 	/// Captures a single snapshot of the display and writes the data to the given image. The | ||||
| 	/// provided image should have the same dimensions as the configured values (_width and _height) | ||||
| 	/// | ||||
| 	/// @param[out] image  The snapped screenshot | ||||
| 	/// | ||||
| 	int grabFrame(Image<ColorRgb> &image); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Set a new video mode | ||||
| 	/// | ||||
| 	void setVideoMode(VideoMode mode) override; | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply new width/height values, overwrite Grabber.h implementation | ||||
| 	/// | ||||
| 	bool setWidthHeight(int /* width */, int /*height*/) override | ||||
| 	{ | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply new pixelDecimation | ||||
| 	/// | ||||
| 	bool setPixelDecimation(int pixelDecimation) override; | ||||
|  | ||||
| 	/// | ||||
| 	/// Set the crop values | ||||
| 	/// @param  cropLeft    Left pixel crop | ||||
| 	/// @param  cropRight   Right pixel crop | ||||
| 	/// @param  cropTop     Top pixel crop | ||||
| 	/// @param  cropBottom  Bottom pixel crop | ||||
| 	/// | ||||
| 	void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply display index | ||||
| 	/// | ||||
| 	bool setDisplayIndex(int index) override; | ||||
|  | ||||
| 	/// @brief Discover QT screens 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 ¶ms); | ||||
|  | ||||
| private: | ||||
| 	/// | ||||
| 	/// @brief Setup a new capture display, will free the previous one | ||||
| 	/// @return True on success, false if no display is found | ||||
| 	/// | ||||
| 	bool restartCapture(); | ||||
|  | ||||
| private: | ||||
| 	std::unique_ptr<DDAGrabberImpl> d; | ||||
| }; | ||||
							
								
								
									
										24
									
								
								include/grabber/dda/DDAWrapper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								include/grabber/dda/DDAWrapper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <grabber/dda/DDAGrabber.h> | ||||
| #include <hyperion/GrabberWrapper.h> | ||||
|  | ||||
| class DDAWrapper : public GrabberWrapper | ||||
| { | ||||
| public: | ||||
| 	static constexpr const char *GRABBERTYPE = "DDA"; | ||||
|  | ||||
| 	DDAWrapper(int updateRate_Hz = GrabberWrapper::DEFAULT_RATE_HZ, int display = 0, | ||||
| 	           int pixelDecimation = GrabberWrapper::DEFAULT_PIXELDECIMATION, int cropLeft = 0, int cropRight = 0, | ||||
| 	           int cropTop = 0, int cropBottom = 0); | ||||
|  | ||||
| 	DDAWrapper(const QJsonDocument &grabberConfig = QJsonDocument()); | ||||
|  | ||||
| 	virtual ~DDAWrapper(){}; | ||||
|  | ||||
| public slots: | ||||
| 	virtual void action(); | ||||
|  | ||||
| private: | ||||
| 	DDAGrabber _grabber; | ||||
| }; | ||||
| @@ -46,6 +46,7 @@ public: | ||||
| 		int numerator	= 0; | ||||
| 		int denominator = 0; | ||||
| 		PixelFormat pf	= PixelFormat::NO_CHANGE; | ||||
| 		long defstride  = 0; | ||||
| 		GUID guid		= GUID_NULL; | ||||
| 	}; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,11 @@ | ||||
| #pragma once | ||||
|  | ||||
| #define NOFRAME_BENCH | ||||
|  | ||||
| #ifdef FRAME_BENCH | ||||
| 	#include <QElapsedTimer> | ||||
| #endif | ||||
|  | ||||
| // stl includes | ||||
| #include <vector> | ||||
| #include <map> | ||||
| @@ -166,6 +172,9 @@ private: | ||||
| 	double   _x_frac_max; | ||||
| 	double   _y_frac_max; | ||||
|  | ||||
| #ifdef FRAME_BENCH | ||||
| 	QElapsedTimer _frameTimer; | ||||
| #endif | ||||
| 	QSocketNotifier *_streamNotifier; | ||||
|  | ||||
| 	bool _initialized, _reload; | ||||
|   | ||||
| @@ -10,6 +10,7 @@ enum class PixelFormat { | ||||
| 	YUYV, | ||||
| 	UYVY, | ||||
| 	BGR16, | ||||
| 	RGB24, | ||||
| 	BGR24, | ||||
| 	RGB32, | ||||
| 	BGR32, | ||||
| @@ -36,6 +37,10 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat) | ||||
| 	{ | ||||
| 		return PixelFormat::BGR16; | ||||
| 	} | ||||
| 	else if (format.compare("rgb24")  == 0) | ||||
| 	{ | ||||
| 		return PixelFormat::RGB24; | ||||
| 	} | ||||
| 	else if (format.compare("bgr24")  == 0) | ||||
| 	{ | ||||
| 		return PixelFormat::BGR24; | ||||
| @@ -80,6 +85,10 @@ inline QString pixelFormatToString(const PixelFormat& pixelFormat) | ||||
| 	{ | ||||
| 		return "BGR16"; | ||||
| 	} | ||||
| 	else if (pixelFormat == PixelFormat::RGB24) | ||||
| 	{ | ||||
| 		return "RGB24"; | ||||
| 	} | ||||
| 	else if (pixelFormat == PixelFormat::BGR24) | ||||
| 	{ | ||||
| 		return "BGR24"; | ||||
| @@ -115,10 +124,10 @@ inline QString pixelFormatToString(const PixelFormat& pixelFormat) | ||||
|  | ||||
| enum class FlipMode | ||||
| { | ||||
| 	NO_CHANGE, | ||||
| 	HORIZONTAL, | ||||
| 	VERTICAL, | ||||
| 	BOTH, | ||||
| 	NO_CHANGE | ||||
| 	BOTH | ||||
| }; | ||||
|  | ||||
| inline FlipMode parseFlipMode(const QString& flipMode) | ||||
|   | ||||
| @@ -11,7 +11,12 @@ namespace RGBW { | ||||
| 		SUBTRACT_MINIMUM, | ||||
| 		SUB_MIN_WARM_ADJUST, | ||||
| 		SUB_MIN_COOL_ADJUST, | ||||
| 		WHITE_OFF | ||||
| 		WHITE_OFF, | ||||
|         COLD_WHITE, | ||||
|         NEUTRAL_WHITE, | ||||
|         AUTO, | ||||
|         AUTO_MAX, | ||||
|         AUTO_ACCURATE | ||||
| 	}; | ||||
|  | ||||
| 	WhiteAlgorithm stringToWhiteAlgorithm(const QString& str); | ||||
|   | ||||
| @@ -593,6 +593,10 @@ QJsonArray JsonInfo::discoverScreenInputs(const QJsonObject& params) const | ||||
| 	discoverGrabber<DirectXGrabber>(screenInputs, params); | ||||
| #endif | ||||
|  | ||||
| #ifdef ENABLE_DDA | ||||
| 	discoverGrabber<DDAGrabber>(screenInputs, params); | ||||
| #endif | ||||
|  | ||||
| #ifdef ENABLE_X11 | ||||
| 	discoverGrabber<X11Grabber>(screenInputs, params); | ||||
| #endif | ||||
|   | ||||
| @@ -34,6 +34,10 @@ if(ENABLE_DX) | ||||
| 	add_subdirectory(directx) | ||||
| endif(ENABLE_DX) | ||||
|  | ||||
| if(ENABLE_DDA) | ||||
| 	add_subdirectory(dda) | ||||
| endif(ENABLE_DDA) | ||||
|  | ||||
| if(ENABLE_AUDIO) | ||||
| 	add_subdirectory(audio) | ||||
| endif() | ||||
|   | ||||
							
								
								
									
										12
									
								
								libsrc/grabber/dda/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								libsrc/grabber/dda/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| add_library(dda-grabber | ||||
| 	${CMAKE_SOURCE_DIR}/include/grabber/dda/DDAGrabber.h | ||||
| 	${CMAKE_SOURCE_DIR}/include/grabber/dda/DDAWrapper.h | ||||
| 	${CMAKE_SOURCE_DIR}/libsrc/grabber/dda/DDAGrabber.cpp | ||||
| 	${CMAKE_SOURCE_DIR}/libsrc/grabber/dda/DDAWrapper.cpp | ||||
| ) | ||||
|  | ||||
| target_link_libraries(dda-grabber | ||||
| 	hyperion | ||||
| 	d3d11.lib | ||||
| 	dxgi.lib | ||||
| ) | ||||
							
								
								
									
										357
									
								
								libsrc/grabber/dda/DDAGrabber.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								libsrc/grabber/dda/DDAGrabber.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,357 @@ | ||||
| #include "grabber/dda/DDAGrabber.h" | ||||
|  | ||||
| #include <atlbase.h> | ||||
| #include <d3d11.h> | ||||
| #include <dxgi1_2.h> | ||||
| #include <physicalmonitorenumerationapi.h> | ||||
| #include <windows.h> | ||||
|  | ||||
| #pragma comment(lib, "d3d9.lib") | ||||
| #pragma comment(lib, "dxva2.lib") | ||||
|  | ||||
| namespace | ||||
| { | ||||
| // Driver types supported. | ||||
| constexpr D3D_DRIVER_TYPE kDriverTypes[] = { | ||||
|     D3D_DRIVER_TYPE_HARDWARE, | ||||
|     D3D_DRIVER_TYPE_WARP, | ||||
|     D3D_DRIVER_TYPE_REFERENCE, | ||||
| }; | ||||
|  | ||||
| // Feature levels supported. | ||||
| D3D_FEATURE_LEVEL kFeatureLevels[] = {D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, | ||||
|                                       D3D_FEATURE_LEVEL_9_1}; | ||||
|  | ||||
| // Returns true if the two texture descriptors are compatible for copying. | ||||
| bool areTextureDescriptionsCompatible(D3D11_TEXTURE2D_DESC a, D3D11_TEXTURE2D_DESC b) | ||||
| { | ||||
| 	return a.Width == b.Width && a.Height == b.Height && a.MipLevels == b.MipLevels && a.ArraySize == b.ArraySize && | ||||
| 	       a.Format == b.Format; | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
|  | ||||
| // Logs a message along with the hex error HRESULT. | ||||
| #define LOG_ERROR(hr, msg) Error(_log, msg ": 0x%x", hr) | ||||
|  | ||||
| // Checks if the HRESULT is an error, and if so, logs it and returns from the | ||||
| // current function. | ||||
| #define RETURN_IF_ERROR(hr, msg, returnValue)                                                                          \ | ||||
| 	if (FAILED(hr))                                                                                                    \ | ||||
| 	{                                                                                                                  \ | ||||
| 		LOG_ERROR(hr, msg);                                                                                            \ | ||||
| 		return returnValue;                                                                                            \ | ||||
| 	} | ||||
|  | ||||
| // Checks if the condition is false, and if so, logs an error and returns from | ||||
| // the current function. | ||||
| #define RET_CHECK(cond, returnValue)                                                                                   \ | ||||
| 	if (!(cond))                                                                                                       \ | ||||
| 	{                                                                                                                  \ | ||||
| 		Error(_log, "Assertion failed: " #cond);                                                                       \ | ||||
| 		return returnValue;                                                                                            \ | ||||
| 	} | ||||
|  | ||||
| // Private implementation. These member variables are here and not in the .h | ||||
| // so we don't have to include <atlbase.h> in the header and pollute everything | ||||
| // else that includes it. | ||||
| class DDAGrabberImpl | ||||
| { | ||||
| public: | ||||
| 	int display = 0; | ||||
| 	int desktopWidth = 0; | ||||
| 	int desktopHeight = 0; | ||||
|  | ||||
| 	// Created in the constructor. | ||||
| 	CComPtr<ID3D11Device> device; | ||||
| 	CComPtr<ID3D11DeviceContext> deviceContext; | ||||
| 	CComPtr<IDXGIDevice> dxgiDevice; | ||||
| 	CComPtr<IDXGIAdapter> dxgiAdapter; | ||||
|  | ||||
| 	// Created in restartCapture - only valid while desktop capture is in | ||||
| 	// progress. | ||||
| 	CComPtr<IDXGIOutputDuplication> desktopDuplication; | ||||
| 	CComPtr<ID3D11Texture2D> intermediateTexture; | ||||
| 	D3D11_TEXTURE2D_DESC intermediateTextureDesc; | ||||
| }; | ||||
|  | ||||
| DDAGrabber::DDAGrabber(int display, int cropLeft, int cropRight, int cropTop, int cropBottom) | ||||
|     : Grabber("GRABBER-DDA", cropLeft, cropRight, cropTop, cropBottom), d(new DDAGrabberImpl) | ||||
| { | ||||
| 	d->display = display; | ||||
|  | ||||
| 	HRESULT hr = S_OK; | ||||
|  | ||||
| 	// Iterate through driver types until we find one that succeeds. | ||||
| 	D3D_FEATURE_LEVEL featureLevel; | ||||
| 	for (D3D_DRIVER_TYPE driverType : kDriverTypes) | ||||
| 	{ | ||||
| 		hr = D3D11CreateDevice(nullptr, driverType, nullptr, 0, kFeatureLevels, std::size(kFeatureLevels), | ||||
| 		                       D3D11_SDK_VERSION, &d->device, &featureLevel, &d->deviceContext); | ||||
| 		if (SUCCEEDED(hr)) | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	RETURN_IF_ERROR(hr, "CreateDevice failed", ); | ||||
|  | ||||
| 	// Get the DXGI factory. | ||||
| 	hr = d->device.QueryInterface(&d->dxgiDevice); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to get DXGI device", ); | ||||
|  | ||||
| 	// Get the factory's adapter. | ||||
| 	hr = d->dxgiDevice->GetAdapter(&d->dxgiAdapter); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to get DXGI Adapter", ); | ||||
| } | ||||
|  | ||||
| DDAGrabber::~DDAGrabber() | ||||
| { | ||||
| } | ||||
|  | ||||
| bool DDAGrabber::restartCapture() | ||||
| { | ||||
| 	if (!d->dxgiAdapter) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	HRESULT hr = S_OK; | ||||
|  | ||||
| 	d->desktopDuplication.Release(); | ||||
|  | ||||
| 	// Get the output that was selected. | ||||
| 	CComPtr<IDXGIOutput> output; | ||||
| 	hr = d->dxgiAdapter->EnumOutputs(d->display, &output); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to get output", false); | ||||
|  | ||||
| 	// Get the descriptor which has the size of the display. | ||||
| 	DXGI_OUTPUT_DESC desc; | ||||
| 	hr = output->GetDesc(&desc); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to get output description", false); | ||||
|  | ||||
| 	d->desktopWidth = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; | ||||
| 	d->desktopHeight = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; | ||||
| 	_width = (d->desktopWidth - _cropLeft - _cropRight) / _pixelDecimation; | ||||
| 	_height = (d->desktopHeight - _cropTop - _cropBottom) / _pixelDecimation; | ||||
| 	Info(_log, "Desktop size: %dx%d, cropping=%d,%d,%d,%d, decimation=%d, final image size=%dx%d", d->desktopWidth, | ||||
| 	     d->desktopHeight, _cropLeft, _cropTop, _cropRight, _cropBottom, _pixelDecimation, _width, _height); | ||||
|  | ||||
| 	// Get the DXGIOutput1 interface. | ||||
| 	CComPtr<IDXGIOutput1> output1; | ||||
| 	hr = output.QueryInterface(&output1); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to get output1", false); | ||||
|  | ||||
| 	// Create the desktop duplication interface. | ||||
| 	hr = output1->DuplicateOutput(d->device, &d->desktopDuplication); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to create desktop duplication interface", false); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| int DDAGrabber::grabFrame(Image<ColorRgb> &image) | ||||
| { | ||||
| 	// Do nothing if we're disabled. | ||||
| 	if (!_isEnabled) | ||||
| 	{ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	// Start the capture if it's not already running. | ||||
| 	if (!d->desktopDuplication && !restartCapture()) | ||||
| 	{ | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	HRESULT hr = S_OK; | ||||
|  | ||||
| 	// Release the last frame, if any. | ||||
| 	hr = d->desktopDuplication->ReleaseFrame(); | ||||
| 	if (FAILED(hr) && hr != DXGI_ERROR_INVALID_CALL) | ||||
| 	{ | ||||
| 		LOG_ERROR(hr, "Failed to release frame"); | ||||
| 	} | ||||
|  | ||||
| 	// Acquire the next frame. | ||||
| 	CComPtr<IDXGIResource> desktopResource; | ||||
| 	DXGI_OUTDUPL_FRAME_INFO frameInfo; | ||||
| 	hr = d->desktopDuplication->AcquireNextFrame(500, &frameInfo, &desktopResource); | ||||
| 	if (hr == DXGI_ERROR_ACCESS_LOST || hr == DXGI_ERROR_INVALID_CALL) | ||||
| 	{ | ||||
| 		if (!restartCapture()) | ||||
| 		{ | ||||
| 			return -1; | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (hr == DXGI_ERROR_WAIT_TIMEOUT) | ||||
| 	{ | ||||
| 		// Nothing changed on the screen in the 500ms we waited. | ||||
| 		return 0; | ||||
| 	} | ||||
| 	RETURN_IF_ERROR(hr, "Failed to acquire next frame", 0); | ||||
|  | ||||
| 	// Get the 2D texture. | ||||
| 	CComPtr<ID3D11Texture2D> texture; | ||||
| 	hr = desktopResource.QueryInterface(&texture); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to get 2D texture", 0); | ||||
|  | ||||
| 	// The texture we acquired is on the GPU and can't be accessed from the CPU, | ||||
| 	// so we have to copy it into another texture that can. | ||||
| 	D3D11_TEXTURE2D_DESC textureDesc; | ||||
| 	texture->GetDesc(&textureDesc); | ||||
|  | ||||
| 	// Create a new intermediate texture if we haven't done so already, or the | ||||
| 	// existing one is incompatible with the acquired texture (i.e. it has | ||||
| 	// different dimensions). | ||||
| 	if (!d->intermediateTexture || !areTextureDescriptionsCompatible(d->intermediateTextureDesc, textureDesc)) | ||||
| 	{ | ||||
| 		Info(_log, "Creating intermediate texture"); | ||||
| 		d->intermediateTexture.Release(); | ||||
|  | ||||
| 		d->intermediateTextureDesc = textureDesc; | ||||
| 		d->intermediateTextureDesc.Usage = D3D11_USAGE_STAGING; | ||||
| 		d->intermediateTextureDesc.BindFlags = 0; | ||||
| 		d->intermediateTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; | ||||
| 		d->intermediateTextureDesc.MiscFlags = 0; | ||||
|  | ||||
| 		hr = d->device->CreateTexture2D(&d->intermediateTextureDesc, nullptr, &d->intermediateTexture); | ||||
| 		RETURN_IF_ERROR(hr, "Failed to create intermediate texture", 0); | ||||
| 	} | ||||
|  | ||||
| 	// Copy the texture to the intermediate texture. | ||||
| 	d->deviceContext->CopyResource(d->intermediateTexture, texture); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to copy texture", 0); | ||||
|  | ||||
| 	// Map the texture so we can access its pixels. | ||||
| 	D3D11_MAPPED_SUBRESOURCE resource; | ||||
| 	hr = d->deviceContext->Map(d->intermediateTexture, 0, D3D11_MAP_READ, 0, &resource); | ||||
| 	RETURN_IF_ERROR(hr, "Failed to map texture", 0); | ||||
|  | ||||
| 	// Copy the texture to the output image. | ||||
| 	RET_CHECK(textureDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM, 0); | ||||
|  | ||||
| 	ColorRgb *dest = image.memptr(); | ||||
| 	for (size_t destY = 0, srcY = _cropTop; destY < image.height(); destY++, srcY += _pixelDecimation) | ||||
| 	{ | ||||
| 		uint32_t *src = | ||||
| 		    reinterpret_cast<uint32_t *>(reinterpret_cast<unsigned char *>(resource.pData) + srcY * resource.RowPitch) + | ||||
| 		    _cropLeft; | ||||
| 		for (size_t destX = 0; destX < image.width(); destX++, src += _pixelDecimation, dest++) | ||||
| 		{ | ||||
| 			*dest = ColorRgb{static_cast<uint8_t>(((*src) >> 16) & 0xff), static_cast<uint8_t>(((*src) >> 8) & 0xff), | ||||
| 			                 static_cast<uint8_t>(((*src) >> 0) & 0xff)}; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void DDAGrabber::setVideoMode(VideoMode mode) | ||||
| { | ||||
| 	Grabber::setVideoMode(mode); | ||||
| 	restartCapture(); | ||||
| } | ||||
|  | ||||
| bool DDAGrabber::setPixelDecimation(int pixelDecimation) | ||||
| { | ||||
| 	if (Grabber::setPixelDecimation(pixelDecimation)) | ||||
| 		return restartCapture(); | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void DDAGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) | ||||
| { | ||||
| 	// Grabber::setCropping rejects the cropped size if it is larger than _width | ||||
| 	// and _height, so temporarily set those back to the original pre-cropped full | ||||
| 	// desktop sizes first. They'll be set back to the cropped sizes by | ||||
| 	// restartCapture. | ||||
| 	_width = d->desktopWidth; | ||||
| 	_height = d->desktopHeight; | ||||
| 	Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); | ||||
| 	restartCapture(); | ||||
| } | ||||
|  | ||||
| bool DDAGrabber::setDisplayIndex(int index) | ||||
| { | ||||
| 	bool rc = true; | ||||
| 	if (d->display != index) | ||||
| 	{ | ||||
| 		d->display = index; | ||||
| 		rc = restartCapture(); | ||||
| 	} | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| QJsonObject DDAGrabber::discover(const QJsonObject ¶ms) | ||||
| { | ||||
| 	QJsonObject ret; | ||||
| 	if (!d->dxgiAdapter) | ||||
| 	{ | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	HRESULT hr = S_OK; | ||||
|  | ||||
| 	// Enumerate through the outputs. | ||||
| 	QJsonArray videoInputs; | ||||
| 	for (int i = 0;; ++i) | ||||
| 	{ | ||||
| 		CComPtr<IDXGIOutput> output; | ||||
| 		hr = d->dxgiAdapter->EnumOutputs(i, &output); | ||||
| 		if (!output || !SUCCEEDED(hr)) | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		// Get the output description. | ||||
| 		DXGI_OUTPUT_DESC desc; | ||||
| 		hr = output->GetDesc(&desc); | ||||
| 		if (FAILED(hr)) | ||||
| 		{ | ||||
| 			Error(_log, "Failed to get output description"); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		// Add it to the JSON. | ||||
| 		const int width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; | ||||
| 		const int height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; | ||||
| 		videoInputs.append(QJsonObject{ | ||||
| 		    {"inputIdx", i}, | ||||
| 		    {"name", QString::fromWCharArray(desc.DeviceName)}, | ||||
| 		    {"formats", | ||||
| 		     QJsonArray{ | ||||
| 		         QJsonObject{ | ||||
| 		             {"resolutions", | ||||
| 		              QJsonArray{ | ||||
| 		                  QJsonObject{ | ||||
| 		                      {"width", width}, | ||||
| 		                      {"height", height}, | ||||
| 		                      {"fps", QJsonArray{1, 5, 10, 15, 20, 25, 30, 40, 50, 60, 120, 144}}, | ||||
| 		                  }, | ||||
| 		              }}, | ||||
| 		         }, | ||||
| 		     }}, | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	ret["video_inputs"] = videoInputs; | ||||
| 	if (!videoInputs.isEmpty()) | ||||
| 	{ | ||||
| 		ret["device"] = "dda"; | ||||
| 		ret["device_name"] = "DXGI DDA"; | ||||
| 		ret["type"] = "screen"; | ||||
| 		ret["default"] = QJsonObject{ | ||||
| 		    {"video_input", | ||||
| 		     QJsonObject{ | ||||
| 		         {"inputIdx", 0}, | ||||
| 		         {"resolution", | ||||
| 		          QJsonObject{ | ||||
| 		              {"fps", 60}, | ||||
| 		          }}, | ||||
| 		     }}, | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
							
								
								
									
										20
									
								
								libsrc/grabber/dda/DDAWrapper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								libsrc/grabber/dda/DDAWrapper.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| #include "grabber/dda/DDAWrapper.h" | ||||
|  | ||||
| DDAWrapper::DDAWrapper(int updateRate_Hz, int display, int pixelDecimation, int cropLeft, int cropRight, int cropTop, | ||||
|                        int cropBottom) | ||||
|     : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz), _grabber(display, cropLeft, cropRight, cropTop, cropBottom) | ||||
|  | ||||
| { | ||||
| 	_grabber.setPixelDecimation(pixelDecimation); | ||||
| } | ||||
|  | ||||
| DDAWrapper::DDAWrapper(const QJsonDocument &grabberConfig) | ||||
|     : DDAWrapper(GrabberWrapper::DEFAULT_RATE_HZ, 0, GrabberWrapper::DEFAULT_PIXELDECIMATION, 0, 0, 0, 0) | ||||
| { | ||||
| 	this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); | ||||
| } | ||||
|  | ||||
| void DDAWrapper::action() | ||||
| { | ||||
| 	transferFrame(_grabber); | ||||
| } | ||||
| @@ -122,18 +122,6 @@ void EncoderThread::process() | ||||
| 		else | ||||
| #endif | ||||
| 		{ | ||||
| 			if (_pixelFormat == PixelFormat::BGR24) | ||||
| 			{ | ||||
| 				if (_flipMode == FlipMode::NO_CHANGE) | ||||
| 					_imageResampler.setFlipMode(FlipMode::HORIZONTAL); | ||||
| 				else if (_flipMode == FlipMode::HORIZONTAL) | ||||
| 					_imageResampler.setFlipMode(FlipMode::NO_CHANGE); | ||||
| 				else if (_flipMode == FlipMode::VERTICAL) | ||||
| 					_imageResampler.setFlipMode(FlipMode::BOTH); | ||||
| 				else if (_flipMode == FlipMode::BOTH) | ||||
| 					_imageResampler.setFlipMode(FlipMode::VERTICAL); | ||||
| 			} | ||||
|  | ||||
| 			Image<ColorRgb> image = Image<ColorRgb>(); | ||||
| 			_imageResampler.processImage( | ||||
| 				_localData, | ||||
| @@ -143,7 +131,7 @@ void EncoderThread::process() | ||||
| #if defined(ENABLE_V4L2) | ||||
| 				_pixelFormat, | ||||
| #else | ||||
| 				PixelFormat::BGR24, | ||||
| 				PixelFormat::BGR24,  // MF-Grabber always sends RGB24, but memory layout is RGBTRIPLE (b,g,r) -> process as BGR24 | ||||
| #endif | ||||
| 				image | ||||
| 			); | ||||
|   | ||||
| @@ -363,6 +363,18 @@ done: | ||||
| 		_height = props.height; | ||||
| 		_frameByteSize = _width * _height * 3; | ||||
| 		_lineLength = _width * 3; | ||||
| 		// adjust flipMode for bottom-up images | ||||
| 		if (props.defstride < 0) | ||||
| 		{ | ||||
| 			if (_flipMode == FlipMode::NO_CHANGE) | ||||
| 				_flipMode = FlipMode::HORIZONTAL; | ||||
| 			else if (_flipMode == FlipMode::HORIZONTAL) | ||||
| 				_flipMode = FlipMode::NO_CHANGE; | ||||
| 			else if (_flipMode == FlipMode::VERTICAL) | ||||
| 				_flipMode = FlipMode::BOTH; | ||||
| 			else if (_flipMode == FlipMode::BOTH) | ||||
| 				_flipMode = FlipMode::VERTICAL; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Cleanup | ||||
| @@ -436,6 +448,14 @@ void MFGrabber::enumVideoCaptureDevices() | ||||
| 												properties.denominator = denominator; | ||||
| 												properties.pf = pixelformat; | ||||
| 												properties.guid = format; | ||||
|  | ||||
| 												HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&properties.defstride); | ||||
| 												if (FAILED(hr)) | ||||
| 												{ | ||||
| 													hr = MFGetStrideForBitmapInfoHeader(format.Data1, width, &properties.defstride); | ||||
| 													if (FAILED(hr)) | ||||
| 														DebugIf (verbose, _log, "failed to get default stride"); | ||||
| 												} | ||||
| 												devicePropertyList.append(properties); | ||||
|  | ||||
| 												DebugIf (verbose, _log, "%s %d x %d @ %d fps (%s)", QSTRING_CSTR(dev), properties.width, properties.height, properties.fps, QSTRING_CSTR(pixelFormatToString(properties.pf))); | ||||
| @@ -797,7 +817,7 @@ QJsonArray MFGrabber::discover(const QJsonObject& params) | ||||
| 		resolution_default["width"] = 640; | ||||
| 		resolution_default["height"] = 480; | ||||
| 		resolution_default["fps"] = 25; | ||||
| 		format_default["format"] = "bgr24"; | ||||
| 		format_default["format"] = "rgb24"; | ||||
| 		format_default["resolution"] = resolution_default; | ||||
| 		video_inputs_default["inputIdx"] = 0; | ||||
| 		video_inputs_default["standards"] = "PAL"; | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
| static PixelFormat GetPixelFormatForGuid(const GUID guid) | ||||
| { | ||||
| 	if (IsEqualGUID(guid, MFVideoFormat_RGB32)) return PixelFormat::RGB32; | ||||
| 	if (IsEqualGUID(guid, MFVideoFormat_RGB24)) return PixelFormat::BGR24; | ||||
| 	if (IsEqualGUID(guid, MFVideoFormat_RGB24)) return PixelFormat::RGB24; | ||||
| 	if (IsEqualGUID(guid, MFVideoFormat_YUY2)) return PixelFormat::YUYV; | ||||
| 	if (IsEqualGUID(guid, MFVideoFormat_UYVY)) return PixelFormat::UYVY; | ||||
| #ifdef HAVE_TURBO_JPEG | ||||
| @@ -145,11 +145,11 @@ public: | ||||
| 		} | ||||
|  | ||||
| #ifdef HAVE_TURBO_JPEG | ||||
| 		if (_pixelformat != PixelFormat::MJPEG && _pixelformat != PixelFormat::BGR24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| 		if (_pixelformat != PixelFormat::MJPEG && _pixelformat != PixelFormat::RGB24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| #else | ||||
| 		if (_pixelformat != PixelFormat::BGR24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| 		if (_pixelformat != PixelFormat::RGB24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| #endif | ||||
| 			pSample = TransformSample(_transform, pSample); | ||||
| 			pSample = TransformSample(_transform, pSample); // forced conversion to RGB24, but memory layout is RGBTRIPLE (b,g,r) -> process as BGR24 | ||||
|  | ||||
| 		_hrStatus = pSample->ConvertToContiguousBuffer(&buffer); | ||||
| 		if (FAILED(_hrStatus)) | ||||
| @@ -181,9 +181,9 @@ public: | ||||
| 			_bEOS = TRUE; // Reached the end of the stream. | ||||
|  | ||||
| #ifdef HAVE_TURBO_JPEG | ||||
| 		if (_pixelformat != PixelFormat::MJPEG && _pixelformat != PixelFormat::BGR24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| 		if (_pixelformat != PixelFormat::MJPEG && _pixelformat != PixelFormat::RGB24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| #else | ||||
| 		if (_pixelformat != PixelFormat::BGR24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| 		if (_pixelformat != PixelFormat::RGB24 && _pixelformat != PixelFormat::NO_CHANGE) | ||||
| #endif | ||||
| 			SAFE_RELEASE(pSample); | ||||
|  | ||||
| @@ -196,9 +196,9 @@ public: | ||||
| 	{ | ||||
| 		_pixelformat = format; | ||||
| #ifdef HAVE_TURBO_JPEG | ||||
| 		if (format == PixelFormat::MJPEG || format == PixelFormat::BGR24 || format == PixelFormat::NO_CHANGE) | ||||
| 		if (format == PixelFormat::MJPEG || format == PixelFormat::RGB24 || format == PixelFormat::NO_CHANGE) | ||||
| #else | ||||
| 		if (format == PixelFormat::BGR24 || format == PixelFormat::NO_CHANGE) | ||||
| 		if (format == PixelFormat::RGB24 || format == PixelFormat::NO_CHANGE) | ||||
| #endif | ||||
| 			return S_OK; | ||||
|  | ||||
| @@ -392,10 +392,10 @@ private: | ||||
| private: | ||||
| 	long				_nRefCount; | ||||
| 	CRITICAL_SECTION	_critsec; | ||||
| 	MFGrabber*			_grabber; | ||||
| 	MFGrabber* _grabber; | ||||
| 	BOOL				_bEOS; | ||||
| 	HRESULT				_hrStatus; | ||||
| 	IMFTransform*		_transform; | ||||
| 	IMFTransform* _transform; | ||||
| 	PixelFormat			_pixelformat; | ||||
| 	std::atomic<bool>	_isBusy; | ||||
| }; | ||||
|   | ||||
| @@ -54,7 +54,9 @@ Q_GLOBAL_STATIC_WITH_ARGS(ControlIDPropertyMap, _controlIDPropertyMap, (initCont | ||||
| static PixelFormat GetPixelFormat(const unsigned int format) | ||||
| { | ||||
| 	if (format == V4L2_PIX_FMT_RGB32) return PixelFormat::RGB32; | ||||
| 	if (format == V4L2_PIX_FMT_RGB24) return PixelFormat::BGR24; | ||||
| 	if (format == V4L2_PIX_FMT_BGR32) return PixelFormat::BGR32; | ||||
| 	if (format == V4L2_PIX_FMT_RGB24) return PixelFormat::RGB24; | ||||
| 	if (format == V4L2_PIX_FMT_BGR24) return PixelFormat::BGR24; | ||||
| 	if (format == V4L2_PIX_FMT_YUYV) return PixelFormat::YUYV; | ||||
| 	if (format == V4L2_PIX_FMT_UYVY) return PixelFormat::UYVY; | ||||
| 	if (format == V4L2_PIX_FMT_NV12) return  PixelFormat::NV12; | ||||
| @@ -557,10 +559,18 @@ void V4L2Grabber::init_device(VideoStandard videoStandard) | ||||
| 			fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; | ||||
| 		break; | ||||
|  | ||||
| 		case PixelFormat::BGR24: | ||||
| 		case PixelFormat::BGR32: | ||||
| 			fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR32; | ||||
| 			break; | ||||
|  | ||||
| 		case PixelFormat::RGB24: | ||||
| 			fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; | ||||
| 		break; | ||||
|  | ||||
| 		case PixelFormat::BGR24: | ||||
| 			fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; | ||||
| 		break; | ||||
|  | ||||
| 		case PixelFormat::YUYV: | ||||
| 			fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; | ||||
| 		break; | ||||
| @@ -691,7 +701,23 @@ void V4L2Grabber::init_device(VideoStandard videoStandard) | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 		case V4L2_PIX_FMT_BGR32: | ||||
| 		{ | ||||
| 			_pixelFormat = PixelFormat::BGR32; | ||||
| 			_frameByteSize = _width * _height * 4; | ||||
| 			Debug(_log, "Pixel format=BGR32"); | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 		case V4L2_PIX_FMT_RGB24: | ||||
| 		{ | ||||
| 			_pixelFormat = PixelFormat::RGB24; | ||||
| 			_frameByteSize = _width * _height * 3; | ||||
| 			Debug(_log, "Pixel format=RGB24"); | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 		case V4L2_PIX_FMT_BGR24: | ||||
| 		{ | ||||
| 			_pixelFormat = PixelFormat::BGR24; | ||||
| 			_frameByteSize = _width * _height * 3; | ||||
| @@ -699,7 +725,6 @@ void V4L2Grabber::init_device(VideoStandard videoStandard) | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
|  | ||||
| 		case V4L2_PIX_FMT_YUYV: | ||||
| 		{ | ||||
| 			_pixelFormat = PixelFormat::YUYV; | ||||
| @@ -743,9 +768,9 @@ void V4L2Grabber::init_device(VideoStandard videoStandard) | ||||
|  | ||||
| 		default: | ||||
| #ifdef HAVE_TURBO_JPEG | ||||
| 			throw_exception("Only pixel formats RGB32, BGR24, YUYV, UYVY, NV12, I420 and MJPEG are supported"); | ||||
| 			throw_exception("Only pixel formats RGB32, BGR32, RGB24, BGR24, YUYV, UYVY, NV12, I420 and MJPEG are supported"); | ||||
| #else | ||||
| 			throw_exception("Only pixel formats RGB32, BGR24, YUYV, UYVY, NV12 and I420 are supported"); | ||||
| 			throw_exception("Only pixel formats RGB32, BGR32, RGB24, BGR24, YUYV, UYVY, NV12 and I420 are supported"); | ||||
| #endif | ||||
| 		return; | ||||
| 	} | ||||
| @@ -1079,6 +1104,22 @@ void V4L2Grabber::newThreadFrame(Image<ColorRgb> image) | ||||
| 	} | ||||
| 	else | ||||
| 		emit newFrame(image); | ||||
|  | ||||
| #ifdef FRAME_BENCH | ||||
| 	// calculate average frametime | ||||
| 	if (_currentFrame > 1) | ||||
| 	{ | ||||
| 		if (_currentFrame % 100 == 0) | ||||
| 		{ | ||||
| 			Debug(_log, "%d: avg. frametime=%.02fms / %.02fms", int(_currentFrame), _frameTimer.restart()/100.0, 1000.0/_fps); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		Debug(_log, "%d: frametimer started", int(_currentFrame)); | ||||
| 		_frameTimer.start(); | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int V4L2Grabber::xioctl(int request, void *arg) | ||||
|   | ||||
| @@ -173,6 +173,10 @@ QStringList GrabberWrapper::availableGrabbers(GrabberTypeFilter type) | ||||
| 		#ifdef ENABLE_DX | ||||
| 				grabbers << "dx"; | ||||
| 		#endif | ||||
|  | ||||
| 		#ifdef ENABLE_DDA | ||||
| 				grabbers << "dda"; | ||||
| 		#endif | ||||
| 	} | ||||
|  | ||||
| 	if (type == GrabberTypeFilter::VIDEO || type == GrabberTypeFilter::ALL) | ||||
|   | ||||
| @@ -19,6 +19,7 @@ include_directories( | ||||
| 	dev_spi | ||||
| 	dev_rpi_pwm | ||||
| 	dev_tinker | ||||
| 	dev_ftdi | ||||
| ) | ||||
|  | ||||
| file (GLOB Leddevice_SOURCES | ||||
| @@ -63,7 +64,11 @@ if(ENABLE_DEV_WS281XPWM) | ||||
| 	file (GLOB Leddevice_PWM_SOURCES "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.h" "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.cpp") | ||||
| endif() | ||||
|  | ||||
| set(LedDevice_RESOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSchemas.qrc) | ||||
| if (ENABLE_DEV_FTDI) | ||||
| 	FILE ( GLOB Leddevice_FTDI_SOURCES "${CURRENT_SOURCE_DIR}/dev_ftdi/*.h" "${CURRENT_SOURCE_DIR}/dev_ftdi/*.cpp") | ||||
| endif() | ||||
|  | ||||
| set(LedDevice_RESOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSchemas.qrc ) | ||||
|  | ||||
| set(Leddevice_SOURCES | ||||
| 	${Leddevice_SOURCES} | ||||
| @@ -74,6 +79,7 @@ set(Leddevice_SOURCES | ||||
| 	${Leddevice_SPI_SOURCES} | ||||
| 	${Leddevice_TINKER_SOURCES} | ||||
| 	${Leddevice_USB_HID_SOURCES} | ||||
| 	${Leddevice_FTDI_SOURCES} | ||||
| ) | ||||
|  | ||||
| # auto generate header file that include all available leddevice headers | ||||
| @@ -165,3 +171,10 @@ if(ENABLE_MDNS) | ||||
|  	target_link_libraries(leddevice mdns) | ||||
| endif() | ||||
|  | ||||
| if( ENABLE_DEV_FTDI ) | ||||
| 	find_package(PkgConfig REQUIRED) | ||||
| 	pkg_check_modules(LIB_FTDI REQUIRED IMPORTED_TARGET libftdi1 ) | ||||
| 	target_include_directories(leddevice PRIVATE PkgConfig::LIB_FTDI) | ||||
| 	target_link_libraries(leddevice PkgConfig::LIB_FTDI) | ||||
| endif() | ||||
|  | ||||
|   | ||||
| @@ -38,5 +38,8 @@ | ||||
| 		<file alias="schema-yeelight">schemas/schema-yeelight.json</file> | ||||
| 		<file alias="schema-razer">schemas/schema-razer.json</file>		 | ||||
| 		<file alias="schema-cololight">schemas/schema-cololight.json</file> | ||||
| 		<file alias="schema-ws2812_ftdi">schemas/schema-ws2812_ftdi.json</file> | ||||
| 		<file alias="schema-apa102_ftdi">schemas/schema-apa102_ftdi.json</file> | ||||
| 		<file alias="schema-sk6812_ftdi">schemas/schema-sk6812_ftdi.json</file> | ||||
| 	</qresource> | ||||
| </RCC> | ||||
|   | ||||
| @@ -65,7 +65,7 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config) | ||||
| 	connect(thread, &QThread::started, _ledDevice, &LedDevice::start); | ||||
|  | ||||
| 	// further signals | ||||
| 	connect(this, &LedDeviceWrapper::updateLeds, _ledDevice, &LedDevice::updateLeds, Qt::QueuedConnection); | ||||
| 	connect(this, &LedDeviceWrapper::updateLeds, _ledDevice, &LedDevice::updateLeds, Qt::BlockingQueuedConnection); | ||||
|  | ||||
| 	connect(this, &LedDeviceWrapper::switchOn, _ledDevice, &LedDevice::switchOn, Qt::BlockingQueuedConnection); | ||||
| 	connect(this, &LedDeviceWrapper::switchOff, _ledDevice, &LedDevice::switchOff, Qt::BlockingQueuedConnection); | ||||
|   | ||||
							
								
								
									
										52
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceAPA102_ftdi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceAPA102_ftdi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| #include "LedDeviceAPA102_ftdi.h" | ||||
|  | ||||
| #define LED_HEADER 0b11100000 | ||||
| #define LED_BRIGHTNESS_FULL 31 | ||||
|  | ||||
| LedDeviceAPA102_ftdi::LedDeviceAPA102_ftdi(const QJsonObject &deviceConfig) : ProviderFtdi(deviceConfig) | ||||
| { | ||||
| } | ||||
|  | ||||
| LedDevice *LedDeviceAPA102_ftdi::construct(const QJsonObject &deviceConfig) | ||||
| { | ||||
| 	return new LedDeviceAPA102_ftdi(deviceConfig); | ||||
| } | ||||
|  | ||||
| bool LedDeviceAPA102_ftdi::init(const QJsonObject &deviceConfig) | ||||
| { | ||||
| 	bool isInitOK = false; | ||||
| 	// Initialise sub-class | ||||
| 	if (ProviderFtdi::init(deviceConfig)) | ||||
| 	{ | ||||
|         _brightnessControlMaxLevel = deviceConfig["brightnessControlMaxLevel"].toInt(LED_BRIGHTNESS_FULL); | ||||
|         Info(_log, "[%s] Setting maximum brightness to [%d] = %d%%", QSTRING_CSTR(_activeDeviceType), _brightnessControlMaxLevel, _brightnessControlMaxLevel * 100 / LED_BRIGHTNESS_FULL); | ||||
|  | ||||
|         CreateHeader(); | ||||
| 		isInitOK = true; | ||||
| 	} | ||||
| 	return isInitOK; | ||||
| } | ||||
|  | ||||
| void LedDeviceAPA102_ftdi::CreateHeader() | ||||
| { | ||||
| 	const unsigned int startFrameSize = 4; | ||||
| 	// Endframe, add additional 4 bytes to cover SK9922 Reset frame (in case SK9922 were sold as AP102) -  has no effect on APA102 | ||||
| 	const unsigned int endFrameSize = (_ledCount / 32) * 4 + 4; | ||||
| 	const unsigned int APAbufferSize = (_ledCount * 4) + startFrameSize + endFrameSize; | ||||
| 	_ledBuffer.resize(APAbufferSize, 0); | ||||
| 	Debug(_log, "APA102 buffer created for %d LEDs", _ledCount); | ||||
| } | ||||
|  | ||||
| int LedDeviceAPA102_ftdi::write(const std::vector<ColorRgb> &ledValues) | ||||
| { | ||||
| 	for (signed iLed = 0; iLed < static_cast<int>(_ledCount); ++iLed) | ||||
| 	{ | ||||
| 		const ColorRgb &rgb = ledValues[iLed]; | ||||
| 		_ledBuffer[4 + iLed * 4 + 0] = LED_HEADER | _brightnessControlMaxLevel; | ||||
| 		_ledBuffer[4 + iLed * 4 + 1] = rgb.red; | ||||
| 		_ledBuffer[4 + iLed * 4 + 2] = rgb.green; | ||||
| 		_ledBuffer[4 + iLed * 4 + 3] = rgb.blue; | ||||
| 	} | ||||
|  | ||||
| 	return writeBytes(_ledBuffer.size(), _ledBuffer.data()); | ||||
| } | ||||
							
								
								
									
										50
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceAPA102_ftdi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceAPA102_ftdi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #ifndef LEDEVICET_APA102_H | ||||
| #define LEDEVICET_APA102_H | ||||
| #include "ProviderFtdi.h" | ||||
|  | ||||
| class LedDeviceAPA102_ftdi : public ProviderFtdi | ||||
| { | ||||
| 	Q_OBJECT | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Constructs an APA102 LED-device | ||||
| 	/// | ||||
| 	/// @param deviceConfig Device's configuration as JSON-Object | ||||
| 	/// | ||||
| 	explicit LedDeviceAPA102_ftdi(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<ColorRgb>& ledValues) override; | ||||
|  | ||||
| 	/// The brighness level. Possibile values 1 .. 31. | ||||
| 	int _brightnessControlMaxLevel; | ||||
|  | ||||
| }; | ||||
|  | ||||
| #endif // LEDEVICET_APA102_H | ||||
							
								
								
									
										96
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceSk6812_ftdi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceSk6812_ftdi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| #include "LedDeviceSk6812_ftdi.h" | ||||
|  | ||||
| LedDeviceSk6812_ftdi::LedDeviceSk6812_ftdi(const QJsonObject &deviceConfig) | ||||
| 	: ProviderFtdi(deviceConfig), | ||||
| 	  _whiteAlgorithm(RGBW::WhiteAlgorithm::INVALID), | ||||
| 	  SPI_BYTES_PER_COLOUR(4), | ||||
| 	  bitpair_to_byte{ | ||||
| 		  0b10001000, | ||||
| 		  0b10001100, | ||||
| 		  0b11001000, | ||||
| 		  0b11001100} | ||||
| { | ||||
| } | ||||
|  | ||||
| LedDevice *LedDeviceSk6812_ftdi::construct(const QJsonObject &deviceConfig) | ||||
| { | ||||
| 	return new LedDeviceSk6812_ftdi(deviceConfig); | ||||
| } | ||||
|  | ||||
| bool LedDeviceSk6812_ftdi::init(const QJsonObject &deviceConfig) | ||||
| { | ||||
|  | ||||
| 	bool isInitOK = false; | ||||
|  | ||||
| 	// Initialise sub-class | ||||
| 	if (ProviderFtdi::init(deviceConfig)) | ||||
| 	{ | ||||
| 		_brightnessControlMaxLevel = deviceConfig["brightnessControlMaxLevel"].toInt(255); | ||||
| 		Info(_log, "[%s] Setting maximum brightness to [%d]", QSTRING_CSTR(_activeDeviceType), _brightnessControlMaxLevel); | ||||
|  | ||||
|  | ||||
| 		QString whiteAlgorithm = deviceConfig["whiteAlgorithm"].toString("white_off"); | ||||
|  | ||||
| 		_whiteAlgorithm = RGBW::stringToWhiteAlgorithm(whiteAlgorithm); | ||||
| 		if (_whiteAlgorithm == RGBW::WhiteAlgorithm::INVALID) | ||||
| 		{ | ||||
| 			QString errortext = QString ("unknown whiteAlgorithm: %1").arg(whiteAlgorithm); | ||||
| 			this->setInError(errortext); | ||||
| 			isInitOK = false; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
|  | ||||
| 			Debug(_log, "whiteAlgorithm : %s", QSTRING_CSTR(whiteAlgorithm)); | ||||
|  | ||||
| 			WarningIf((_baudRate_Hz < 2050000 || _baudRate_Hz > 3750000), _log, "Baud rate %d outside recommended range (2050000 -> 3750000)", _baudRate_Hz); | ||||
|  | ||||
| 			const int SPI_FRAME_END_LATCH_BYTES = 3; | ||||
| 			_ledBuffer.resize(_ledRGBWCount * SPI_BYTES_PER_COLOUR + SPI_FRAME_END_LATCH_BYTES, 0x00); | ||||
|  | ||||
| 			isInitOK = true; | ||||
| 		} | ||||
| 	} | ||||
| 	return isInitOK; | ||||
| } | ||||
|  | ||||
|  | ||||
| inline __attribute__((always_inline)) uint8_t LedDeviceSk6812_ftdi::scale(uint8_t i, uint8_t scale) { | ||||
| 	return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8; | ||||
| } | ||||
|  | ||||
| int LedDeviceSk6812_ftdi::write(const std::vector<ColorRgb> &ledValues) | ||||
| { | ||||
| 	unsigned spi_ptr = 0; | ||||
| 	const int SPI_BYTES_PER_LED = sizeof(ColorRgbw) * SPI_BYTES_PER_COLOUR; | ||||
|  | ||||
| 	ColorRgbw temp_rgbw; | ||||
| 	ColorRgb scaled_color; | ||||
| 	for (const ColorRgb &color : ledValues) | ||||
| 	{ | ||||
| 		scaled_color.red = scale(color.red, _brightnessControlMaxLevel); | ||||
| 		scaled_color.green = scale(color.green, _brightnessControlMaxLevel); | ||||
| 		scaled_color.blue = scale(color.blue, _brightnessControlMaxLevel); | ||||
|  | ||||
|         RGBW::Rgb_to_Rgbw(scaled_color, &temp_rgbw, _whiteAlgorithm); | ||||
|  | ||||
| 		uint32_t colorBits = | ||||
| 			((uint32_t)temp_rgbw.red << 24) + | ||||
| 			((uint32_t)temp_rgbw.green << 16) + | ||||
| 			((uint32_t)temp_rgbw.blue << 8) + | ||||
| 			temp_rgbw.white; | ||||
|  | ||||
| 		for (int j = SPI_BYTES_PER_LED - 1; j >= 0; j--) | ||||
| 		{ | ||||
| 			_ledBuffer[spi_ptr + j] = bitpair_to_byte[colorBits & 0x3]; | ||||
| 			colorBits >>= 2; | ||||
| 		} | ||||
| 		spi_ptr += SPI_BYTES_PER_LED; | ||||
| 	} | ||||
|  | ||||
| 	_ledBuffer[spi_ptr++] = 0; | ||||
| 	_ledBuffer[spi_ptr++] = 0; | ||||
| 	_ledBuffer[spi_ptr++] = 0; | ||||
|  | ||||
| 	return writeBytes(_ledBuffer.size(), _ledBuffer.data()); | ||||
| } | ||||
							
								
								
									
										52
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceSk6812_ftdi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceSk6812_ftdi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| #ifndef LEDEVICESK6812ftdi_H | ||||
| #define LEDEVICESK6812ftdi_H | ||||
|  | ||||
| #include "ProviderFtdi.h" | ||||
|  | ||||
| class LedDeviceSk6812_ftdi : public ProviderFtdi | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Constructs a Sk6801 LED-device | ||||
| 	/// | ||||
| 	/// @param deviceConfig Device's configuration as JSON-Object | ||||
| 	/// | ||||
| 	explicit LedDeviceSk6812_ftdi(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<ColorRgb>& ledValues) override; | ||||
|  | ||||
| 	inline __attribute__((always_inline)) uint8_t scale(uint8_t i, uint8_t scale); | ||||
|  | ||||
| 	RGBW::WhiteAlgorithm _whiteAlgorithm; | ||||
|  | ||||
| 	const int SPI_BYTES_PER_COLOUR; | ||||
| 	uint8_t bitpair_to_byte[4]; | ||||
|  | ||||
| 	int _brightnessControlMaxLevel; | ||||
| }; | ||||
|  | ||||
| #endif // LEDEVICESK6812ftdi_H | ||||
							
								
								
									
										93
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceWs2812_ftdi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceWs2812_ftdi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| #include "LedDeviceWs2812_ftdi.h" | ||||
|  | ||||
| /* | ||||
| From the data sheet: | ||||
|  | ||||
| (TH+TL=1.25μs±600ns) | ||||
|  | ||||
| T0H,	 0 code, high level time,	 0.40µs ±0.150ns | ||||
| T0L,	 0 code, low level time,	 0.85µs ±0.150ns | ||||
| T1H,	 1 code, high level time,	 0.80µs ±0.150ns | ||||
| T1L,	 1 code, low level time,	 0.45µs ±0.150ns | ||||
| WT,	 Wait for the processing time,	 NA | ||||
| Trst,	 Reset code,low level time,	 50µs (not anymore... need 300uS for latest revision) | ||||
|  | ||||
| To normalise the pulse times so they fit in 4 SPI bits: | ||||
|  | ||||
| On the assumption that the "low" time doesnt matter much | ||||
|  | ||||
| A SPI bit time of 0.40uS = 2.5 Mbit/sec | ||||
| T0 is sent as 1000 | ||||
| T1 is sent as 1100 | ||||
|  | ||||
| With a bit of excel testing, we can work out the maximum and minimum speeds: | ||||
| 2106000 MIN | ||||
| 2590500 AVG | ||||
| 3075000 MAX | ||||
|  | ||||
| Wait time: | ||||
| Not Applicable for WS2812 | ||||
|  | ||||
| Reset time: | ||||
| using the max of 3075000, the bit time is 0.325 | ||||
| Reset time is 300uS = 923 bits = 116 bytes | ||||
|  | ||||
| */ | ||||
|  | ||||
| LedDeviceWs2812_ftdi::LedDeviceWs2812_ftdi(const QJsonObject &deviceConfig) | ||||
| 	: ProviderFtdi(deviceConfig), | ||||
| 	  SPI_BYTES_PER_COLOUR(4), | ||||
| 	  SPI_FRAME_END_LATCH_BYTES(116), | ||||
| 	  bitpair_to_byte{ | ||||
| 		  0b10001000, | ||||
| 		  0b10001100, | ||||
| 		  0b11001000, | ||||
| 		  0b11001100, | ||||
| 	  } | ||||
| { | ||||
| } | ||||
|  | ||||
| LedDevice *LedDeviceWs2812_ftdi::construct(const QJsonObject &deviceConfig) | ||||
| { | ||||
| 	return new LedDeviceWs2812_ftdi(deviceConfig); | ||||
| } | ||||
|  | ||||
| bool LedDeviceWs2812_ftdi::init(const QJsonObject &deviceConfig) | ||||
| { | ||||
| 	bool isInitOK = false; | ||||
|  | ||||
| 	// Initialise sub-class | ||||
| 	if (ProviderFtdi::init(deviceConfig)) | ||||
| 	{ | ||||
| 		WarningIf((_baudRate_Hz < 2106000 || _baudRate_Hz > 3075000), _log, "Baud rate %d outside recommended range (2106000 -> 3075000)", _baudRate_Hz); | ||||
| 		_ledBuffer.resize(_ledRGBCount * SPI_BYTES_PER_COLOUR + SPI_FRAME_END_LATCH_BYTES, 0x00); | ||||
| 		isInitOK = true; | ||||
| 	} | ||||
|  | ||||
| 	return isInitOK; | ||||
| } | ||||
|  | ||||
| int LedDeviceWs2812_ftdi::write(const std::vector<ColorRgb> &ledValues) | ||||
| { | ||||
| 	unsigned spi_ptr = 0; | ||||
| 	const int SPI_BYTES_PER_LED = sizeof(ColorRgb) * SPI_BYTES_PER_COLOUR; | ||||
|  | ||||
| 	for (const ColorRgb &color : ledValues) | ||||
| 	{ | ||||
| 		uint32_t colorBits = ((unsigned int)color.red << 16) | ((unsigned int)color.green << 8) | color.blue; | ||||
|  | ||||
| 		for (int j = SPI_BYTES_PER_LED - 1; j >= 0; j--) | ||||
| 		{ | ||||
| 			_ledBuffer[spi_ptr + j] = bitpair_to_byte[colorBits & 0x3]; | ||||
| 			colorBits >>= 2; | ||||
| 		} | ||||
| 		spi_ptr += SPI_BYTES_PER_LED; | ||||
| 	} | ||||
|  | ||||
| 	for (int j = 0; j < SPI_FRAME_END_LATCH_BYTES; j++) | ||||
| 	{ | ||||
| 		_ledBuffer[spi_ptr++] = 0; | ||||
| 	} | ||||
|  | ||||
| 	return writeBytes(_ledBuffer.size(), _ledBuffer.data()); | ||||
| } | ||||
							
								
								
									
										49
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceWs2812_ftdi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								libsrc/leddevice/dev_ftdi/LedDeviceWs2812_ftdi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| #ifndef LEDEVICEWS2812_ftdi_H | ||||
| #define LEDEVICEWS2812_ftdi_H | ||||
|  | ||||
| #include "ProviderFtdi.h" | ||||
|  | ||||
|  | ||||
| class LedDeviceWs2812_ftdi : public ProviderFtdi | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Constructs a Ws2812 LED-device | ||||
| 	/// | ||||
| 	/// @param deviceConfig Device's configuration as JSON-Object | ||||
| 	/// | ||||
| 	explicit LedDeviceWs2812_ftdi(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<ColorRgb>& ledValues) override; | ||||
|  | ||||
| 	const int SPI_BYTES_PER_COLOUR; | ||||
| 	const int SPI_FRAME_END_LATCH_BYTES; | ||||
|  | ||||
| 	uint8_t bitpair_to_byte[4]; | ||||
| }; | ||||
|  | ||||
| #endif // LEDEVICEWS2812_ftdi_H | ||||
							
								
								
									
										208
									
								
								libsrc/leddevice/dev_ftdi/ProviderFtdi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								libsrc/leddevice/dev_ftdi/ProviderFtdi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,208 @@ | ||||
| // LedDevice includes | ||||
| #include <leddevice/LedDevice.h> | ||||
| #include "ProviderFtdi.h" | ||||
| #include <utils/WaitTime.h> | ||||
|  | ||||
| #include <ftdi.h> | ||||
| #include <libusb.h> | ||||
|  | ||||
| #define ANY_FTDI_VENDOR 0x0 | ||||
| #define ANY_FTDI_PRODUCT 0x0 | ||||
|  | ||||
| #define FTDI_CHECK_RESULT(statement) if (statement) {setInError(ftdi_get_error_string(_ftdic)); return rc;} | ||||
|  | ||||
| namespace Pin | ||||
| { | ||||
| // enumerate the AD bus for convenience. | ||||
| enum bus_t | ||||
| { | ||||
| 	SK = 0x01, // ADBUS0, SPI data clock | ||||
| 	DO = 0x02, // ADBUS1, SPI data out | ||||
| 	CS = 0x08, // ADBUS3, SPI chip select, active low | ||||
| }; | ||||
| } | ||||
|  | ||||
| const uint8_t pinInitialState = Pin::CS; | ||||
| // Use these pins as outputs | ||||
| const uint8_t pinDirection = Pin::SK | Pin::DO | Pin::CS; | ||||
|  | ||||
| const QString ProviderFtdi::AUTO_SETTING = QString("auto"); | ||||
|  | ||||
| ProviderFtdi::ProviderFtdi(const QJsonObject &deviceConfig) | ||||
| 	: LedDevice(deviceConfig), | ||||
| 	  _ftdic(nullptr), | ||||
| 	  _baudRate_Hz(1000000) | ||||
| { | ||||
| } | ||||
|  | ||||
| bool ProviderFtdi::init(const QJsonObject &deviceConfig) | ||||
| { | ||||
| 	bool isInitOK = false; | ||||
|  | ||||
| 	if (LedDevice::init(deviceConfig)) | ||||
| 	{ | ||||
| 		_baudRate_Hz = deviceConfig["rate"].toInt(_baudRate_Hz); | ||||
| 		_deviceName = deviceConfig["output"].toString(AUTO_SETTING); | ||||
|  | ||||
| 		Debug(_log, "_baudRate_Hz [%d]", _baudRate_Hz); | ||||
| 		Debug(_log, "_deviceName [%s]", QSTRING_CSTR(_deviceName)); | ||||
|  | ||||
| 		isInitOK = true; | ||||
| 	} | ||||
| 	return isInitOK; | ||||
| } | ||||
|  | ||||
| int ProviderFtdi::open() | ||||
| { | ||||
| 	int rc = 0; | ||||
|  | ||||
| 	_ftdic = ftdi_new(); | ||||
|  | ||||
| 	if (ftdi_init(_ftdic) < 0) | ||||
| 	{ | ||||
| 		_ftdic = nullptr; | ||||
| 		setInError("Could not initialize the ftdi library"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	Debug(_log, "Opening FTDI device=%s", QSTRING_CSTR(_deviceName)); | ||||
|  | ||||
| 	FTDI_CHECK_RESULT((rc = ftdi_usb_open_string(_ftdic, QSTRING_CSTR(_deviceName))) < 0); | ||||
| 	/* doing this disable resets things if they were in a bad state */ | ||||
| 	FTDI_CHECK_RESULT((rc = ftdi_disable_bitbang(_ftdic)) < 0); | ||||
| 	FTDI_CHECK_RESULT((rc = ftdi_setflowctrl(_ftdic, SIO_DISABLE_FLOW_CTRL)) < 0); | ||||
| 	FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0x00, BITMODE_RESET)) < 0); | ||||
| 	FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0xff, BITMODE_MPSSE)) < 0); | ||||
|  | ||||
| 	double reference_clock = 60e6; | ||||
| 	int divisor = (reference_clock / 2 / _baudRate_Hz) - 1; | ||||
| 	std::vector<uint8_t> buf = { | ||||
| 		DIS_DIV_5, | ||||
| 		TCK_DIVISOR, | ||||
| 		static_cast<unsigned char>(divisor), | ||||
| 		static_cast<unsigned char>(divisor >> 8), | ||||
| 		SET_BITS_LOW,    // opcode: set low bits (ADBUS[0-7] | ||||
| 		pinInitialState, // argument: inital pin state | ||||
| 		pinDirection | ||||
| 	}; | ||||
|  | ||||
| 	FTDI_CHECK_RESULT((rc = ftdi_write_data(_ftdic, buf.data(), buf.size())) != buf.size()); | ||||
|  | ||||
| 	_isDeviceReady = true; | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| int ProviderFtdi::close() | ||||
| { | ||||
| 	LedDevice::close(); | ||||
| 	if (_ftdic != nullptr) { | ||||
| 		Debug(_log, "Closing FTDI device"); | ||||
| 		// Delay to give time to push color black from writeBlack() into the led, | ||||
| 		// otherwise frame transmission will be terminated half way through | ||||
| 		wait(30); | ||||
| 		ftdi_set_bitmode(_ftdic, 0x00, BITMODE_RESET); | ||||
| 		ftdi_usb_close(_ftdic); | ||||
| 		ftdi_free(_ftdic); | ||||
| 		_ftdic = nullptr; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void ProviderFtdi::setInError(const QString &errorMsg, bool isRecoverable) | ||||
| { | ||||
| 	close(); | ||||
|  | ||||
| 	LedDevice::setInError(errorMsg, isRecoverable); | ||||
| } | ||||
|  | ||||
| int ProviderFtdi::writeBytes(const qint64 size, const uint8_t *data) | ||||
| { | ||||
| 	int rc; | ||||
| 	int count_arg = size - 1; | ||||
| 	std::vector<uint8_t> buf = { | ||||
| 		SET_BITS_LOW, | ||||
| 		pinInitialState & ~Pin::CS, | ||||
| 		pinDirection, | ||||
| 		MPSSE_DO_WRITE | MPSSE_WRITE_NEG, | ||||
| 		static_cast<unsigned char>(count_arg), | ||||
| 		static_cast<unsigned char>(count_arg >> 8), | ||||
| 		SET_BITS_LOW, | ||||
| 		pinInitialState | Pin::CS, | ||||
| 		pinDirection | ||||
| 	}; | ||||
| 	// insert before last SET_BITS_LOW command | ||||
| 	// SET_BITS_LOW takes 2 arguments, so we're inserting data in -3 position from the end | ||||
| 	buf.insert(buf.end() - 3, &data[0], &data[size]); | ||||
|  | ||||
| 	FTDI_CHECK_RESULT((rc = ftdi_write_data(_ftdic, buf.data(), buf.size())) != buf.size()); | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| QJsonObject ProviderFtdi::discover(const QJsonObject & /*params*/) | ||||
| { | ||||
| 	QJsonObject devicesDiscovered; | ||||
| 	QJsonArray deviceList; | ||||
| 	struct ftdi_device_list *devlist; | ||||
| 	struct ftdi_context *ftdic; | ||||
|  | ||||
| 	ftdic = ftdi_new(); | ||||
|  | ||||
| 	if (ftdi_usb_find_all(ftdic, &devlist, ANY_FTDI_VENDOR, ANY_FTDI_PRODUCT) > 0) | ||||
| 	{ | ||||
| 		struct ftdi_device_list *curdev = devlist; | ||||
| 		QMap<QString, uint8_t> deviceIndexes; | ||||
|  | ||||
| 		while (curdev) | ||||
| 		{ | ||||
| 			libusb_device_descriptor desc; | ||||
| 			int rc = libusb_get_device_descriptor(curdev->dev, &desc); | ||||
| 			if (rc == 0) | ||||
| 			{ | ||||
| 				QString vendorIdentifier =  QString("0x%1").arg(desc.idVendor, 4, 16, QChar{'0'}); | ||||
| 				QString productIdentifier = QString("0x%1").arg(desc.idProduct, 4, 16, QChar{'0'}); | ||||
| 				QString vendorAndProduct = QString("%1:%2") | ||||
| 										   .arg(vendorIdentifier) | ||||
| 										   .arg(productIdentifier); | ||||
| 				uint8_t deviceIndex = deviceIndexes.value(vendorAndProduct, 0); | ||||
|  | ||||
| 				char serial_string[128] = {0}; | ||||
| 				char manufacturer_string[128] = {0}; | ||||
| 				char description_string[128] = {0}; | ||||
| 				ftdi_usb_get_strings2(ftdic, curdev->dev, manufacturer_string, 128, description_string, 128, serial_string, 128); | ||||
|  | ||||
| 				QString serialNumber {serial_string}; | ||||
| 				QString ftdiOpenString; | ||||
| 				if(!serialNumber.isEmpty()) | ||||
| 				{ | ||||
| 					ftdiOpenString = QString("s:%1:%2").arg(vendorAndProduct).arg(serialNumber); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					ftdiOpenString = QString("i:%1:%2").arg(vendorAndProduct).arg(deviceIndex); | ||||
| 				} | ||||
|  | ||||
| 				deviceList.push_back(QJsonObject{ | ||||
| 										 {"ftdiOpenString", ftdiOpenString}, | ||||
| 										 {"vendorIdentifier", vendorIdentifier}, | ||||
| 										 {"productIdentifier", productIdentifier}, | ||||
| 										 {"deviceIndex", deviceIndex}, | ||||
| 										 {"serialNumber", serialNumber}, | ||||
| 										 {"manufacturer", manufacturer_string}, | ||||
| 										 {"description", description_string} | ||||
| 									 }); | ||||
| 				deviceIndexes.insert(vendorAndProduct, deviceIndex + 1); | ||||
| 			} | ||||
| 			curdev = curdev->next; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ftdi_list_free(&devlist); | ||||
| 	ftdi_free(ftdic); | ||||
|  | ||||
| 	devicesDiscovered.insert("ledDeviceType", _activeDeviceType); | ||||
| 	devicesDiscovered.insert("devices", deviceList); | ||||
|  | ||||
| 	Debug(_log, "FTDI devices discovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); | ||||
|  | ||||
| 	return devicesDiscovered; | ||||
| } | ||||
							
								
								
									
										76
									
								
								libsrc/leddevice/dev_ftdi/ProviderFtdi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								libsrc/leddevice/dev_ftdi/ProviderFtdi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| #ifndef PROVIDERFtdi_H | ||||
| #define PROVIDERFtdi_H | ||||
|  | ||||
| // LedDevice includes | ||||
| #include <leddevice/LedDevice.h> | ||||
|  | ||||
| #include <ftdi.h> | ||||
|  | ||||
| /// | ||||
| /// The ProviderFtdi implements an abstract base-class for LedDevices using a Ftdi-device. | ||||
| /// | ||||
| class ProviderFtdi : public LedDevice | ||||
| { | ||||
| 	Q_OBJECT | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Constructs a Ftdi LED-device | ||||
| 	/// | ||||
| 	ProviderFtdi(const QJsonObject& deviceConfig); | ||||
|  | ||||
| 	static const QString AUTO_SETTING; | ||||
|  | ||||
| protected: | ||||
| 	/// | ||||
| 	/// @brief Opens the output device. | ||||
| 	/// | ||||
| 	/// @return Zero on success (i.e. device is ready), else negative | ||||
| 	/// | ||||
| 	int open() override; | ||||
|  | ||||
| 	/// | ||||
| 	/// Sets configuration | ||||
| 	/// | ||||
| 	/// @param deviceConfig the json device config | ||||
| 	/// @return true if success | ||||
| 	bool init(const QJsonObject& deviceConfig) override; | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Closes the UDP device. | ||||
| 	/// | ||||
| 	/// @return Zero on success (i.e. device is closed), else negative | ||||
| 	/// | ||||
| 	int close() override; | ||||
|  | ||||
|  | ||||
| 	/// @brief Write the given bytes to the Ftdi-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); | ||||
|  | ||||
| 	 | ||||
| 	QJsonObject discover(const QJsonObject& params) override; | ||||
|  | ||||
| 	/// The Ftdi serial-device | ||||
| 	struct ftdi_context *_ftdic; | ||||
|  | ||||
| 	/// The used baud-rate of the output device | ||||
| 	qint32 _baudRate_Hz; | ||||
| 	QString _deviceName; | ||||
|  | ||||
| protected slots: | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Set device in error state | ||||
| 	/// | ||||
| 	/// @param errorMsg The error message to be logged | ||||
| 	/// | ||||
| 	void setInError(const QString& errorMsg, bool isRecoverable=true) override; | ||||
| }; | ||||
|  | ||||
| #endif // PROVIDERFtdi_H | ||||
| @@ -895,6 +895,7 @@ void LedDevicePhilipsHueBridge::setBridgeDetails(const QJsonDocument &doc, bool | ||||
| 		log( "API-Version", "%u.%u.%u", _api_major, _api_minor, _api_patch ); | ||||
| 		log( "API v2 ready", "%s", _isAPIv2Ready ? "Yes" : "No" ); | ||||
| 		log( "Entertainment ready", "%s", _isHueEntertainmentReady ? "Yes" : "No" ); | ||||
| 		log( "Use Entertainment API", "%s", _useEntertainmentAPI ? "Yes" : "No" ); | ||||
| 		log( "DIYHue", "%s", _isDiyHue ? "Yes" : "No" ); | ||||
| 	} | ||||
| } | ||||
| @@ -1799,11 +1800,11 @@ bool LedDevicePhilipsHue::init(const QJsonObject &deviceConfig) | ||||
|  | ||||
| 	if (LedDevicePhilipsHueBridge::init(_devConfig)) | ||||
| 	{ | ||||
| 		log( "Off on Black", "%s", _switchOffOnBlack ? "Yes" : "No" ); | ||||
| 		log( "Brightness Factor", "%f", _brightnessFactor ); | ||||
| 		log( "Transition Time", "%d", _transitionTime ); | ||||
| 		log( "Restore Original State", "%s", _isRestoreOrigState ? "Yes" : "No" ); | ||||
| 		log( "Use Hue Entertainment API", "%s", _useEntertainmentAPI ? "Yes" : "No" ); | ||||
| 		log("Off on Black", "%s", _switchOffOnBlack ? "Yes" : "No" ); | ||||
| 		log("Brightness Factor", "%f", _brightnessFactor ); | ||||
| 		log("Transition Time", "%d", _transitionTime ); | ||||
| 		log("Restore Original State", "%s", _isRestoreOrigState ? "Yes" : "No" ); | ||||
| 		log("Use Hue Entertainment API", "%s", _useEntertainmentAPI ? "Yes" : "No" ); | ||||
| 		log("Brightness Threshold", "%f", _blackLevel); | ||||
| 		log("CandyGamma", "%s", _candyGamma ? "Yes" : "No" ); | ||||
| 		log("Time powering off when black", "%s", _onBlackTimeToPowerOff ? "Yes" : "No" ); | ||||
| @@ -1864,7 +1865,7 @@ bool LedDevicePhilipsHue::setLights() | ||||
| 		Debug(_log, "Lights configured: %d", configuredLightsCount ); | ||||
| 		if (updateLights( getLightMap())) | ||||
| 		{ | ||||
| 			if (_useApiV2) | ||||
| 			if (_useApiV2 && _useEntertainmentAPI) | ||||
| 			{ | ||||
| 				_channelsCount = getGroupChannelsCount (_groupId); | ||||
|  | ||||
| @@ -2208,15 +2209,14 @@ int LedDevicePhilipsHue::write(const std::vector<ColorRgb> & ledValues) | ||||
| 	int rc {0}; | ||||
| 	if (_isOn) | ||||
| 	{ | ||||
| 		if (!_useApiV2) | ||||
| 		{ | ||||
| 			rc = writeSingleLights( ledValues ); | ||||
| 		} | ||||
|  | ||||
| 		if (_useEntertainmentAPI && _isInitLeds) | ||||
| 		{ | ||||
| 			rc= writeStreamData(ledValues); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			rc = writeSingleLights( ledValues ); | ||||
| 		} | ||||
| 	} | ||||
| 	return rc; | ||||
| } | ||||
| @@ -2482,7 +2482,7 @@ void LedDevicePhilipsHue::setColor(PhilipsHueLight& light, CiColor& color) | ||||
| 					QJsonObject colorXY; | ||||
| 					colorXY[API_X_COORDINATE] = color.x; | ||||
| 					colorXY[API_Y_COORDINATE] = color.y; | ||||
| 					cmd.insert(API_COLOR, QJsonObject {{API_DURATION, colorXY }}); | ||||
| 					cmd.insert(API_COLOR, QJsonObject {{API_XY_COORDINATES, colorXY }}); | ||||
| 					cmd.insert(API_DIMMING, QJsonObject {{API_BRIGHTNESS, bri }}); | ||||
| 				} | ||||
| 				else | ||||
| @@ -2556,7 +2556,7 @@ void LedDevicePhilipsHue::setState(PhilipsHueLight& light, bool on, const CiColo | ||||
| 					QJsonObject colorXY; | ||||
| 					colorXY[API_X_COORDINATE] = color.x; | ||||
| 					colorXY[API_Y_COORDINATE] = color.y; | ||||
| 					cmd.insert(API_COLOR, QJsonObject {{API_DURATION, colorXY }}); | ||||
| 					cmd.insert(API_COLOR, QJsonObject {{API_XY_COORDINATES, colorXY }}); | ||||
| 					cmd.insert(API_DIMMING, QJsonObject {{API_BRIGHTNESS, bri }}); | ||||
| 				} | ||||
| 				else | ||||
|   | ||||
| @@ -30,7 +30,8 @@ enum HttpStatusCode { | ||||
| 	BadRequest   = 400, | ||||
| 	UnAuthorized = 401, | ||||
| 	Forbidden    = 403, | ||||
| 	NotFound     = 404 | ||||
| 	NotFound     = 404, | ||||
| 	TooManyRequests = 429 | ||||
| }; | ||||
|  | ||||
| } //End of constants | ||||
| @@ -336,6 +337,15 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply) | ||||
| 				case HttpStatusCode::NotFound: | ||||
| 					advise = "Check Resource given"; | ||||
| 					break; | ||||
| 				case HttpStatusCode::TooManyRequests: | ||||
| 				{ | ||||
| 					QString retryAfterTime = response.getHeader("Retry-After"); | ||||
| 					if (!retryAfterTime.isEmpty()) | ||||
| 					{ | ||||
| 						advise = "Retry-After: " + response.getHeader("Retry-After"); | ||||
| 					} | ||||
| 				} | ||||
| 				break; | ||||
| 				default: | ||||
| 					advise = httpReason; | ||||
| 					break; | ||||
|   | ||||
| @@ -58,6 +58,11 @@ bool LedDeviceAdalight::init(const QJsonObject &deviceConfig) | ||||
| 		case Adalight::ADA: | ||||
| 			Debug( _log, "Adalight driver uses standard Adalight protocol"); | ||||
| 			break; | ||||
|  | ||||
| 		case Adalight::SKYDIMO: | ||||
| 			Debug( _log, "Adalight driver uses Skydimo protocol"); | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			Error( _log, "Adalight driver - unsupported protocol"); | ||||
| 			return false; | ||||
| @@ -71,10 +76,6 @@ bool LedDeviceAdalight::init(const QJsonObject &deviceConfig) | ||||
|  | ||||
| void LedDeviceAdalight::prepareHeader() | ||||
| { | ||||
| 	// create ledBuffer | ||||
| 	uint totalLedCount = _ledCount; | ||||
| 	_bufferLength = static_cast<qint64>(HEADER_SIZE + _ledRGBCount); | ||||
|  | ||||
| 	switch (_streamProtocol) { | ||||
| 	case Adalight::LBAPA: | ||||
| 	{ | ||||
| @@ -82,7 +83,6 @@ void LedDeviceAdalight::prepareHeader() | ||||
| 		const unsigned int bytesPerRGBLed = 4; | ||||
| 		const unsigned int endFrameSize = qMax<unsigned int>(((_ledCount + 15) / 16), bytesPerRGBLed); | ||||
| 		_bufferLength = HEADER_SIZE + (_ledCount * bytesPerRGBLed) + startFrameSize + endFrameSize; | ||||
|  | ||||
| 		_ledBuffer.resize(static_cast<size_t>(_bufferLength), 0x00); | ||||
|  | ||||
| 		// init constant data values | ||||
| @@ -91,39 +91,47 @@ void LedDeviceAdalight::prepareHeader() | ||||
| 			_ledBuffer[iLed*4+HEADER_SIZE] = 0xFF; | ||||
| 		} | ||||
| 	} | ||||
| 		break; | ||||
|  | ||||
| 	break; | ||||
| 	case Adalight::SKYDIMO: | ||||
| 	{ | ||||
| 		_bufferLength = static_cast<qint64>(HEADER_SIZE + _ledRGBCount); | ||||
| 		_ledBuffer.resize(static_cast<size_t>(_bufferLength), 0x00); | ||||
| 		_ledBuffer[0] = 'A'; | ||||
| 		_ledBuffer[1] = 'd'; | ||||
| 		_ledBuffer[2] = 'a'; | ||||
| 		_ledBuffer[3] = 0; | ||||
| 		_ledBuffer[4] = 0; | ||||
| 		_ledBuffer[5] = static_cast<quint8>(_ledCount); | ||||
| 	} | ||||
| 	break; | ||||
| 	case Adalight::AWA: | ||||
| 		_bufferLength += 8; | ||||
| 		[[fallthrough]]; | ||||
| 	{ | ||||
| 		_bufferLength = static_cast<qint64>(HEADER_SIZE + _ledRGBCount + 8); | ||||
| 		_ledBuffer.resize(static_cast<size_t>(_bufferLength), 0x00); | ||||
| 		_ledBuffer[0] = 'A'; | ||||
| 		_ledBuffer[1] = 'w'; | ||||
| 		_ledBuffer[2] = _white_channel_calibration ? 'A' : 'a'; | ||||
| 		qToBigEndian<quint16>(static_cast<quint16>(_ledCount-1), &_ledBuffer[3]); | ||||
| 		_ledBuffer[5] = _ledBuffer[3] ^ _ledBuffer[4] ^ 0x55; // Checksum | ||||
| 	} | ||||
| 	break; | ||||
| 	case Adalight::ADA: | ||||
| 		[[fallthrough]]; | ||||
| 	default: | ||||
| 		totalLedCount -= 1; | ||||
| 		_bufferLength = static_cast<qint64>(HEADER_SIZE + _ledRGBCount); | ||||
| 		_ledBuffer.resize(static_cast<size_t>(_bufferLength), 0x00); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	_ledBuffer[0] = 'A'; | ||||
| 	if (_streamProtocol == Adalight::AWA ) | ||||
| 	{ | ||||
| 		_ledBuffer[1] = 'w'; | ||||
| 		_ledBuffer[2] = _white_channel_calibration ? 'A' : 'a'; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		_ledBuffer[0] = 'A'; | ||||
| 		_ledBuffer[1] = 'd'; | ||||
| 		_ledBuffer[2] = 'a'; | ||||
| 		qToBigEndian<quint16>(static_cast<quint16>(_ledCount-1), &_ledBuffer[3]); | ||||
| 		_ledBuffer[5] = _ledBuffer[3] ^ _ledBuffer[4] ^ 0x55; // Checksum | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	qToBigEndian<quint16>(static_cast<quint16>(totalLedCount), &_ledBuffer[3]); | ||||
| 	_ledBuffer[5] = _ledBuffer[3] ^ _ledBuffer[4] ^ 0x55; // Checksum | ||||
|  | ||||
| 	Debug( _log, "Adalight header for %d leds (size: %d): %c%c%c 0x%02x 0x%02x 0x%02x", _ledCount, _ledBuffer.size(), | ||||
| 		   _ledBuffer[0], _ledBuffer[1], _ledBuffer[2], _ledBuffer[3], _ledBuffer[4], _ledBuffer[5] ); | ||||
| } | ||||
|  | ||||
|  | ||||
| int LedDeviceAdalight::write(const std::vector<ColorRgb> & ledValues) | ||||
| { | ||||
| 	if (_ledCount != ledValues.size()) | ||||
|   | ||||
| @@ -10,7 +10,8 @@ typedef enum ProtocolType | ||||
| { | ||||
| 	ADA = 0, | ||||
| 	LBAPA, | ||||
| 	AWA | ||||
| 	AWA, | ||||
| 	SKYDIMO | ||||
| } PROTOCOLTYPE; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,10 +11,10 @@ | ||||
| 		"streamProtocol": { | ||||
| 		  "type": "string", | ||||
| 		  "title": "edt_dev_spec_stream_protocol_title", | ||||
| 		  "enum": [ "0", "1", "2" ], | ||||
| 		  "enum": [ "0", "1", "2", "3" ], | ||||
| 		  "default": "0", | ||||
| 		  "options": { | ||||
| 		    "enum_titles": [ "edt_dev_spec_ada_mode_title", "edt_dev_spec_LBap102Mode_title","edt_dev_spec_awa_mode_title" ] | ||||
| 		    "enum_titles": [ "edt_dev_spec_ada_mode_title", "edt_dev_spec_LBap102Mode_title","edt_dev_spec_awa_mode_title", "edt_dev_spec_skydimo_mode_title" ] | ||||
| 		  }, | ||||
| 		  "propertyOrder": 2 | ||||
| 		}, | ||||
|   | ||||
							
								
								
									
										27
									
								
								libsrc/leddevice/schemas/schema-apa102_ftdi.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								libsrc/leddevice/schemas/schema-apa102_ftdi.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| { | ||||
| 	"type": "object", | ||||
| 	"required": true, | ||||
| 	"properties": { | ||||
| 		"output": { | ||||
| 			"type": "string", | ||||
| 			"title":"edt_dev_spec_outputPath_title", | ||||
| 			"propertyOrder": 1 | ||||
| 		}, | ||||
| 		"rate": { | ||||
| 			"type": "integer", | ||||
| 			"title": "edt_dev_spec_baudrate_title", | ||||
| 			"default": 5000000, | ||||
| 			"propertyOrder": 2 | ||||
| 		}, | ||||
| 		"brightnessControlMaxLevel": { | ||||
| 			"type": "integer", | ||||
| 			"title": "edt_conf_color_brightness_title", | ||||
| 			"default": 31, | ||||
| 			"minimum": 1, | ||||
| 			"maximum": 31, | ||||
| 			"propertyOrder": 3 | ||||
|  | ||||
| 		} | ||||
| 	}, | ||||
| 	"additionalProperties": true | ||||
| } | ||||
							
								
								
									
										60
									
								
								libsrc/leddevice/schemas/schema-sk6812_ftdi.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								libsrc/leddevice/schemas/schema-sk6812_ftdi.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| { | ||||
| 	"type": "object", | ||||
| 	"required": true, | ||||
| 	"properties": { | ||||
| 		"output": { | ||||
| 			"type": "string", | ||||
| 			"title": "edt_dev_spec_outputPath_title", | ||||
| 			"required": true, | ||||
| 			"propertyOrder": 1 | ||||
| 		}, | ||||
| 		"rate": { | ||||
| 			"type": "integer", | ||||
| 			"step": 100000, | ||||
| 			"title": "edt_dev_spec_baudrate_title", | ||||
| 			"default": 3200000, | ||||
| 			"minimum": 2050000, | ||||
| 			"maximum": 3750000, | ||||
| 			"propertyOrder": 2 | ||||
| 		}, | ||||
| 		"brightnessControlMaxLevel": { | ||||
| 			"type": "integer", | ||||
| 			"title": "edt_conf_color_brightness_title", | ||||
| 			"default": 255, | ||||
| 			"minimum": 1, | ||||
| 			"maximum": 255, | ||||
| 			"propertyOrder": 3 | ||||
| 		}, | ||||
| 		"whiteAlgorithm": { | ||||
| 			"type": "string", | ||||
| 			"title": "edt_dev_spec_whiteLedAlgor_title", | ||||
| 			"enum": [ | ||||
| 				"subtract_minimum", | ||||
| 				"sub_min_cool_adjust", | ||||
| 				"sub_min_warm_adjust", | ||||
| 				"cold_white", | ||||
| 				"neutral_white", | ||||
| 				"auto", | ||||
| 				"auto_max", | ||||
| 				"auto_accurate", | ||||
| 				"white_off"				 | ||||
| 			], | ||||
| 			"default": "white_off", | ||||
| 			"options": { | ||||
| 				"enum_titles": [ | ||||
| 					"edt_dev_enum_subtract_minimum", | ||||
| 					"edt_dev_enum_sub_min_cool_adjust", | ||||
| 					"edt_dev_enum_sub_min_warm_adjust", | ||||
| 					"edt_dev_enum_cold_white", | ||||
| 					"edt_dev_enum_neutral_white", | ||||
| 					"edt_dev_enum_auto", | ||||
| 					"edt_dev_enum_auto_max", | ||||
| 					"edt_dev_enum_auto_accurate", | ||||
| 					"edt_dev_enum_white_off" | ||||
| 				] | ||||
| 			}, | ||||
| 			"propertyOrder": 4 | ||||
| 		} | ||||
| 	}, | ||||
| 	"additionalProperties": true | ||||
| } | ||||
| @@ -22,10 +22,30 @@ | ||||
| 		"whiteAlgorithm": { | ||||
| 			"type": "string", | ||||
| 			"title":"edt_dev_spec_whiteLedAlgor_title", | ||||
| 			"enum" : ["subtract_minimum","sub_min_cool_adjust","sub_min_warm_adjust","white_off"], | ||||
| 			"enum" : [ | ||||
| 				"subtract_minimum", | ||||
| 				"sub_min_cool_adjust", | ||||
| 				"sub_min_warm_adjust", | ||||
| 				"cold_white", | ||||
| 				"neutral_white", | ||||
| 				"auto", | ||||
| 				"auto_max", | ||||
| 				"auto_accurate", | ||||
| 				"white_off" | ||||
| 			], | ||||
| 			"default": "subtract_minimum", | ||||
| 			"options" : { | ||||
| 				"enum_titles" : ["edt_dev_enum_subtract_minimum", "edt_dev_enum_sub_min_cool_adjust","edt_dev_enum_sub_min_warm_adjust", "edt_dev_enum_white_off"] | ||||
| 				"enum_titles" : [ | ||||
| 					"edt_dev_enum_subtract_minimum", | ||||
| 					"edt_dev_enum_sub_min_cool_adjust", | ||||
| 					"edt_dev_enum_sub_min_warm_adjust", | ||||
| 					"edt_dev_enum_cold_white", | ||||
| 					"edt_dev_enum_neutral_white", | ||||
| 					"edt_dev_enum_auto", | ||||
| 					"edt_dev_enum_auto_max", | ||||
| 					"edt_dev_enum_auto_accurate", | ||||
| 					"edt_dev_enum_white_off" | ||||
| 				] | ||||
| 			}, | ||||
| 			"propertyOrder" : 4 | ||||
| 		}, | ||||
|   | ||||
							
								
								
									
										20
									
								
								libsrc/leddevice/schemas/schema-ws2812_ftdi.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								libsrc/leddevice/schemas/schema-ws2812_ftdi.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| { | ||||
| 	"type": "object", | ||||
| 	"required": true, | ||||
| 	"properties": { | ||||
| 		"output": { | ||||
| 			"type": "string", | ||||
| 			"title": "edt_dev_spec_outputPath_title", | ||||
| 			"propertyOrder": 1 | ||||
| 		}, | ||||
| 		"rate": { | ||||
| 			"type": "integer", | ||||
| 			"title": "edt_dev_spec_baudrate_title", | ||||
| 			"default": 3075000, | ||||
| 			"minimum": 2106000, | ||||
| 			"maximum": 3075000, | ||||
| 			"propertyOrder": 2 | ||||
| 		} | ||||
| 	}, | ||||
| 	"additionalProperties": true | ||||
| } | ||||
| @@ -43,10 +43,30 @@ | ||||
| 		"whiteAlgorithm": { | ||||
| 			"type": "string", | ||||
| 			"title":"edt_dev_spec_whiteLedAlgor_title", | ||||
| 			"enum" : ["subtract_minimum","sub_min_cool_adjust","sub_min_warm_adjust","white_off"], | ||||
| 			"enum" : [ | ||||
| 				"subtract_minimum", | ||||
| 				"sub_min_cool_adjust", | ||||
| 				"sub_min_warm_adjust", | ||||
| 				"cold_white", | ||||
| 				"neutral_white", | ||||
| 				"auto", | ||||
| 				"auto_max", | ||||
| 				"auto_accurate", | ||||
| 				"white_off" | ||||
| 			], | ||||
| 			"default": "subtract_minimum", | ||||
| 			"options" : { | ||||
| 				"enum_titles" : ["edt_dev_enum_subtract_minimum", "edt_dev_enum_sub_min_cool_adjust","edt_dev_enum_sub_min_warm_adjust", "edt_dev_enum_white_off"] | ||||
| 				"enum_titles" : [ | ||||
| 					"edt_dev_enum_subtract_minimum", | ||||
| 					"edt_dev_enum_sub_min_cool_adjust", | ||||
| 					"edt_dev_enum_sub_min_warm_adjust", | ||||
| 					"edt_dev_enum_cold_white", | ||||
| 					"edt_dev_enum_neutral_white", | ||||
| 					"edt_dev_enum_auto", | ||||
| 					"edt_dev_enum_auto_max", | ||||
| 					"edt_dev_enum_auto_accurate", | ||||
| 					"edt_dev_enum_white_off" | ||||
| 				] | ||||
| 			}, | ||||
| 			"propertyOrder" : 7 | ||||
| 		}, | ||||
|   | ||||
| @@ -29,9 +29,6 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i | ||||
| 	int cropTop = _cropTop; | ||||
| 	int cropBottom = _cropBottom; | ||||
|  | ||||
| 	int xDestFlip = 0, yDestFlip = 0; | ||||
| 	int uOffset = 0, vOffset = 0; | ||||
|  | ||||
| 	// handle 3D mode | ||||
| 	switch (_videoMode) | ||||
| 	{ | ||||
| @@ -53,118 +50,191 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i | ||||
|  | ||||
| 	outputImage.resize(outputWidth, outputHeight); | ||||
|  | ||||
| 	for (int yDest = 0, ySource = cropTop + (_verticalDecimation >> 1); yDest < outputHeight; ySource += _verticalDecimation, ++yDest) | ||||
| 	int xDestStart, xDestEnd; | ||||
| 	int yDestStart, yDestEnd; | ||||
|  | ||||
| 	switch (_flipMode) | ||||
| 	{ | ||||
| 		int yOffset = lineLength * ySource; | ||||
| 		if (pixelFormat == PixelFormat::NV12) | ||||
| 		{ | ||||
| 			uOffset = (height + ySource / 2) * lineLength; | ||||
| 		} | ||||
| 		else if (pixelFormat == PixelFormat::I420) | ||||
| 		{ | ||||
| 			uOffset = width * height + (ySource/2) * width/2; | ||||
| 			vOffset = width * height * 1.25 + (ySource/2) * width/2; | ||||
| 		} | ||||
| 		case FlipMode::NO_CHANGE: | ||||
| 			xDestStart = 0; | ||||
| 			xDestEnd = outputWidth-1; | ||||
| 			yDestStart = 0; | ||||
| 			yDestEnd = outputHeight-1; | ||||
| 			break; | ||||
| 		case FlipMode::HORIZONTAL: | ||||
| 			xDestStart = 0; | ||||
| 			xDestEnd = outputWidth-1; | ||||
| 			yDestStart = -(outputHeight-1); | ||||
| 			yDestEnd = 0; | ||||
| 			break; | ||||
| 		case FlipMode::VERTICAL: | ||||
| 			xDestStart = -(outputWidth-1); | ||||
| 			xDestEnd = 0; | ||||
| 			yDestStart = 0; | ||||
| 			yDestEnd = outputHeight-1; | ||||
| 			break; | ||||
| 		case FlipMode::BOTH: | ||||
| 			xDestStart = -(outputWidth-1); | ||||
| 			xDestEnd = 0; | ||||
| 			yDestStart = -(outputHeight-1); | ||||
| 			yDestEnd = 0; | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 		for (int xDest = 0, xSource = cropLeft + (_horizontalDecimation >> 1); xDest < outputWidth; xSource += _horizontalDecimation, ++xDest) | ||||
| 	switch (pixelFormat) | ||||
| 	{ | ||||
| 		case PixelFormat::UYVY: | ||||
| 		{ | ||||
| 			switch (_flipMode) | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				case FlipMode::HORIZONTAL: | ||||
|  | ||||
| 					xDestFlip = xDest; | ||||
| 					yDestFlip = outputHeight-yDest-1; | ||||
| 					break; | ||||
| 				case FlipMode::VERTICAL: | ||||
| 					xDestFlip = outputWidth-xDest-1; | ||||
| 					yDestFlip = yDest; | ||||
| 					break; | ||||
| 				case FlipMode::BOTH: | ||||
| 					xDestFlip = outputWidth-xDest-1; | ||||
| 					yDestFlip = outputHeight-yDest-1; | ||||
| 					break; | ||||
| 				case FlipMode::NO_CHANGE: | ||||
| 					xDestFlip = xDest; | ||||
| 					yDestFlip = yDest; | ||||
| 					break; | ||||
| 			} | ||||
|  | ||||
| 			ColorRgb &rgb = outputImage(xDestFlip, yDestFlip); | ||||
| 			switch (pixelFormat) | ||||
| 			{ | ||||
| 				case PixelFormat::UYVY: | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					int index = yOffset + (xSource << 1); | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int index = lineLength * ySource + (xSource << 1); | ||||
| 					uint8_t y = data[index+1]; | ||||
| 					uint8_t u = ((xSource&1) == 0) ? data[index  ] : data[index-2]; | ||||
| 					uint8_t v = ((xSource&1) == 0) ? data[index+2] : data[index  ]; | ||||
| 					ColorSys::yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::YUYV: | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::YUYV: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					int index = yOffset + (xSource << 1); | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int index = lineLength * ySource + (xSource << 1); | ||||
| 					uint8_t y = data[index]; | ||||
| 					uint8_t u = ((xSource&1) == 0) ? data[index+1] : data[index-1]; | ||||
| 					uint8_t v = ((xSource&1) == 0) ? data[index+3] : data[index+1]; | ||||
| 					ColorSys::yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::BGR16: | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::BGR16: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					int index = yOffset + (xSource << 1); | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int index = lineLength * ySource + (xSource << 1); | ||||
| 					rgb.blue  = (data[index] & 0x1f) << 3; | ||||
| 					rgb.green = (((data[index+1] & 0x7) << 3) | (data[index] & 0xE0) >> 5) << 2; | ||||
| 					rgb.red   = (data[index+1] & 0xF8); | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::BGR24: | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::RGB24: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					int index = yOffset + (xSource << 1) + xSource; | ||||
| 					rgb.blue  = data[index  ]; | ||||
| 					rgb.green = data[index+1]; | ||||
| 					rgb.red   = data[index+2]; | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::RGB32: | ||||
| 				{ | ||||
| 					int index = yOffset + (xSource << 2); | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int index = lineLength * ySource + (xSource << 1) + xSource; | ||||
| 					rgb.red   = data[index  ]; | ||||
| 					rgb.green = data[index+1]; | ||||
| 					rgb.blue  = data[index+2]; | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::BGR32: | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::BGR24: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					int index = yOffset + (xSource << 2); | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int index = lineLength * ySource + (xSource << 1) + xSource; | ||||
| 					rgb.blue  = data[index  ]; | ||||
| 					rgb.green = data[index+1]; | ||||
| 					rgb.red   = data[index+2]; | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::NV12: | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::RGB32: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					uint8_t y = data[yOffset + xSource]; | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int index = lineLength * ySource + (xSource << 2); | ||||
| 					rgb.red   = data[index  ]; | ||||
| 					rgb.green = data[index+1]; | ||||
| 					rgb.blue  = data[index+2]; | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::BGR32: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int index = lineLength * ySource + (xSource << 2); | ||||
| 					rgb.blue  = data[index  ]; | ||||
| 					rgb.green = data[index+1]; | ||||
| 					rgb.red   = data[index+2]; | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::NV12: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				int uOffset = (height + ySource / 2) * lineLength; | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					uint8_t y = data[lineLength * ySource + xSource]; | ||||
| 					uint8_t u = data[uOffset + ((xSource >> 1) << 1)]; | ||||
| 					uint8_t v = data[uOffset + ((xSource >> 1) << 1) + 1]; | ||||
| 					ColorSys::yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::I420: | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		case PixelFormat::I420: | ||||
| 		{ | ||||
| 			for (int yDest = yDestStart, ySource = cropTop + (_verticalDecimation >> 1); yDest <= yDestEnd; ySource += _verticalDecimation, ++yDest) | ||||
| 			{ | ||||
| 				int uOffset = width * height + (ySource/2) * width/2; | ||||
| 				int vOffset = width * height * 1.25 + (ySource/2) * width/2; | ||||
| 				for (int xDest = xDestStart, xSource = cropLeft + (_horizontalDecimation >> 1); xDest <= xDestEnd; xSource += _horizontalDecimation, ++xDest) | ||||
| 				{ | ||||
| 					int y = data[yOffset + xSource]; | ||||
| 					ColorRgb & rgb = outputImage(abs(xDest), abs(yDest)); | ||||
| 					int y = data[lineLength * ySource + xSource]; | ||||
| 					int u = data[uOffset + (xSource >> 1)]; | ||||
| 					int v = data[vOffset + (xSource >> 1)]; | ||||
| 					ColorSys::yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); | ||||
| 					break; | ||||
| 				} | ||||
| 				break; | ||||
| 				case PixelFormat::MJPEG: | ||||
| 				break; | ||||
| 				case PixelFormat::NO_CHANGE: | ||||
| 					Error(Logger::getInstance("ImageResampler"), "Invalid pixel format given"); | ||||
| 				break; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		case PixelFormat::MJPEG: | ||||
| 		break; | ||||
| 		case PixelFormat::NO_CHANGE: | ||||
| 			Error(Logger::getInstance("ImageResampler"), "Invalid pixel format given"); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
| #include <utils/RgbToRgbw.h> | ||||
| #include <utils/Logger.h> | ||||
|  | ||||
| #define ROUND_DIVIDE(number, denom) (((number) + (denom) / 2) / (denom)) | ||||
|  | ||||
| namespace RGBW { | ||||
|  | ||||
| WhiteAlgorithm stringToWhiteAlgorithm(const QString& str) | ||||
| @@ -19,7 +21,27 @@ WhiteAlgorithm stringToWhiteAlgorithm(const QString& str) | ||||
| 	{ | ||||
| 		return WhiteAlgorithm::SUB_MIN_COOL_ADJUST; | ||||
| 	} | ||||
| 	if (str.isEmpty() || str == "white_off") | ||||
|     if (str == "cold_white") | ||||
|     { | ||||
|         return WhiteAlgorithm::COLD_WHITE; | ||||
|     } | ||||
|     if (str == "neutral_white") | ||||
|     { | ||||
|         return WhiteAlgorithm::NEUTRAL_WHITE; | ||||
|     } | ||||
|     if (str == "auto") | ||||
|     { | ||||
|         return WhiteAlgorithm::AUTO; | ||||
|     } | ||||
|     if (str == "auto_max") | ||||
|     { | ||||
|         return WhiteAlgorithm::AUTO_MAX; | ||||
|     } | ||||
|     if (str == "auto_accurate") | ||||
|     { | ||||
|         return WhiteAlgorithm::AUTO_ACCURATE; | ||||
|     } | ||||
|     if (str.isEmpty() || str == "white_off") | ||||
| 	{ | ||||
| 		return WhiteAlgorithm::WHITE_OFF; | ||||
| 	} | ||||
| @@ -77,6 +99,63 @@ void Rgb_to_Rgbw(ColorRgb input, ColorRgbw * output, WhiteAlgorithm algorithm) | ||||
| 			output->white = 0; | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
|         case WhiteAlgorithm::AUTO_MAX: | ||||
|         { | ||||
|             output->red = input.red; | ||||
|             output->green = input.green; | ||||
|             output->blue = input.blue; | ||||
|             output->white = input.red > input.green ? (input.red > input.blue ? input.red : input.blue) : (input.green > input.blue ? input.green : input.blue); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case WhiteAlgorithm::AUTO_ACCURATE: | ||||
|         { | ||||
|             output->white = input.red < input.green ? (input.red < input.blue ? input.red : input.blue) : (input.green < input.blue ? input.green : input.blue); | ||||
|             output->red = input.red - output->white; | ||||
|             output->green = input.green - output->white; | ||||
|             output->blue = input.blue - output->white; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case WhiteAlgorithm::AUTO: | ||||
|         { | ||||
|  | ||||
|             output->red = input.red; | ||||
|             output->green = input.green; | ||||
|             output->blue = input.blue; | ||||
|             output->white = input.red < input.green ? (input.red < input.blue ? input.red : input.blue) : (input.green < input.blue ? input.green : input.blue); | ||||
|             break; | ||||
|         } | ||||
|         case WhiteAlgorithm::NEUTRAL_WHITE: | ||||
|         case WhiteAlgorithm::COLD_WHITE: | ||||
|         { | ||||
|             //cold white config | ||||
|             uint8_t gain = 0xFF; | ||||
|             uint8_t red = 0xA0; | ||||
|             uint8_t green = 0xA0; | ||||
|             uint8_t blue = 0xA0; | ||||
|  | ||||
|             if (algorithm == WhiteAlgorithm::NEUTRAL_WHITE) { | ||||
|                 gain = 0xFF; | ||||
|                 red = 0xB0; | ||||
|                 green = 0xB0; | ||||
|                 blue = 0x70; | ||||
|             } | ||||
|  | ||||
|             uint8_t _r = qMin((uint32_t)(ROUND_DIVIDE(red * input.red,  0xFF)), (uint32_t)0xFF); | ||||
|             uint8_t _g = qMin((uint32_t)(ROUND_DIVIDE(green * input.green,  0xFF)), (uint32_t)0xFF); | ||||
|             uint8_t _b = qMin((uint32_t)(ROUND_DIVIDE(blue * input.blue,  0xFF)), (uint32_t)0xFF); | ||||
|  | ||||
|             output->white = qMin(_r, qMin(_g, _b)); | ||||
|             output->red = input.red - _r; | ||||
|             output->green = input.green - _g; | ||||
|             output->blue = input.blue - _b; | ||||
|  | ||||
|             uint8_t _w = qMin((uint32_t)(ROUND_DIVIDE(gain * output->white,  0xFF)), (uint32_t)0xFF); | ||||
|             output->white = _w; | ||||
|             break; | ||||
|         } | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
|   | ||||
| @@ -118,6 +118,10 @@ if(ENABLE_DX) | ||||
| 	target_link_libraries(${PROJECT_NAME} directx-grabber) | ||||
| endif (ENABLE_DX) | ||||
|  | ||||
| if(ENABLE_DDA) | ||||
| 	target_link_libraries(${PROJECT_NAME} dda-grabber) | ||||
| endif (ENABLE_DDA) | ||||
|  | ||||
| if(ENABLE_CEC) | ||||
| 	target_link_libraries(${PROJECT_NAME} cechandler) | ||||
| endif (ENABLE_CEC) | ||||
|   | ||||
| @@ -425,7 +425,7 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs | ||||
|  | ||||
| void HyperionDaemon::updateScreenGrabbers(const QJsonDocument& grabberConfig) | ||||
| { | ||||
| #if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_XCB) && !defined(ENABLE_AMLOGIC) && !defined(ENABLE_QT) && !defined(ENABLE_DX) | ||||
| #if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_XCB) && !defined(ENABLE_AMLOGIC) && !defined(ENABLE_QT) && !defined(ENABLE_DX) && !defined(ENABLE_DDA) | ||||
| 	Info(_log, "No screen capture supported on this platform"); | ||||
| 	return; | ||||
| #endif | ||||
| @@ -469,6 +469,12 @@ void HyperionDaemon::updateScreenGrabbers(const QJsonDocument& grabberConfig) | ||||
| 			startGrabber<DirectXWrapper>(_screenGrabber, grabberConfig); | ||||
| 		} | ||||
| #endif | ||||
| #ifdef ENABLE_DDA | ||||
| 		else if (type == "dda") | ||||
| 		{ | ||||
| 			startGrabber<DDAWrapper>(_screenGrabber, grabberConfig); | ||||
| 		} | ||||
| #endif | ||||
| #ifdef ENABLE_FB | ||||
| 		else if (type == "framebuffer") | ||||
| 		{ | ||||
|   | ||||
| @@ -64,6 +64,12 @@ | ||||
| 	typedef QObject DirectXWrapper; | ||||
| #endif | ||||
|  | ||||
| #ifdef ENABLE_DDA | ||||
| 	#include <grabber/dda/DDAWrapper.h> | ||||
| #else | ||||
| 	typedef QObject DDAWrapper; | ||||
| #endif | ||||
|  | ||||
| #include <hyperion/GrabberWrapper.h> | ||||
| #ifdef ENABLE_AUDIO | ||||
| 	#include <grabber/audio/AudioWrapper.h> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user