diff --git a/.github/actions/download-pre-built-deps/action.yml b/.github/actions/download-pre-built-deps/action.yml index 0295eb36c..f1cb80682 100644 --- a/.github/actions/download-pre-built-deps/action.yml +++ b/.github/actions/download-pre-built-deps/action.yml @@ -10,11 +10,6 @@ inputs: description: Linux architecture required: false default: 'amd64' - qt_version: - type: string - description: Qt version - required: false - default: '6' build_type: type: string description: Build type @@ -22,8 +17,11 @@ inputs: default: 'release' outputs: cmakeArgs: - description: "CMake arguments" + description: "Resulting CMake arguments" value: ${{ steps.download-pre-built-deps.outputs.cmake-args }} + qtVersion: + description: "Determined Qt version" + value: ${{ steps.download-pre-built-deps.outputs.qt-version }} runs: using: composite steps: @@ -35,8 +33,9 @@ runs: preBuiltDeps='buildspec.json' baseURL=$(jq -r '.baseUrl' $preBuiltDeps) version=$(jq -r '.version' $preBuiltDeps) - downloadHash=$(jq -r '.hashes .${{ inputs.os }} .${{ inputs.architecture }} .qt${{ inputs.qt_version }} .${{ inputs.build_type }}' $preBuiltDeps) - downloadFilename=${{ inputs.os }}-${{ inputs.architecture }}-qt${{ inputs.qt_version }}-${{ inputs.build_type }}-$version.tar.gz + downloadHash=$(jq -r '.hashes .${{ inputs.os }} .${{ inputs.architecture }} .${{ inputs.build_type }}' $preBuiltDeps) + qtVersion=$(jq -r '.hashes .${{ inputs.os }} .${{ inputs.architecture }} .qt_version' $preBuiltDeps) + downloadFilename=${{ inputs.os }}-${{ inputs.architecture }}-qt$qtVersion-${{ inputs.build_type }}-$version.tar.gz echo 💾 Download "$downloadFilename"... curl -OL "$baseURL"/"$version"/"$downloadFilename" if [[ ${{ inputs.os }} == 'windows' ]]; then @@ -55,7 +54,9 @@ runs: fi echo ✅ Hash check passed. echo "cmake-args="-DUSE_PRE_BUILT_DEPS=ON -DPRE_BUILT_DEPS_DIR=$destDir"" >> $GITHUB_OUTPUT + echo "qt-version=$qtVersion" >> $GITHUB_OUTPUT else echo ❌ Hash check failed. echo "cmake-args=" >> $GITHUB_OUTPUT + echo "qt-version=" >> $GITHUB_OUTPUT fi diff --git a/.github/actions/download-pre-built-deps/buildspec.json b/.github/actions/download-pre-built-deps/buildspec.json index f27e8af34..4a12444aa 100644 --- a/.github/actions/download-pre-built-deps/buildspec.json +++ b/.github/actions/download-pre-built-deps/buildspec.json @@ -1,111 +1,95 @@ { - "version": "2025-10-18", + "version": "2025-11-01", "baseUrl": "https://github.com/hyperion-project/hyperion-deps/releases/download", "hashes": { "debian_bookworm": { "amd64": { - "qt6": { - "debug": "a905db1cb0ec7f2099f9703e2b97f15d850b7aac69657416d6f4c0bda4256a6b", - "release": "a7216c688c5a5ff4b6e1c534c9c939e422a675803a5831aaffbe7fc7ba173894" - } + "qt_version": "6.4.2", + "debug": "06c425db6251dcd6109bc07bcb2aa1cfeb19139676651552b2ebaf28a33c854b", + "release": "22fdfa2338b18883fb047954c895fdaae094ff5ff309da22cf9317912d425d8a" }, "arm64": { - "qt6": { - "debug": "083ed521e20894138c751dc3406363167af39f846e080c0d8b12329f4468e59f", - "release": "7ba77697a85496ae467249046db65f628aee3c13fbe16f1b17878eb59de42f80" - } + "qt_version": "6.4.2", + "debug": "51b4e1f2bdb426f517e9fb620c1b4fb199a59f999009dd096ca1a5a3c75438fd", + "release": "393bbc164584842ee463256f13a1cd49388d3d2ee29f00f667ac0c51339d0890" }, "armv6": { - "qt6": { - "debug": "6a89a75d6c6bae815fa9357cf0c342f0ae0fa78be64d8d99d95d95b69e803bf7", - "release": "ad756f9154b8b1a5bae7dc47f9a8a86264f46bf52e3d1457b90038d6640fd5d6" - } + "qt_version": "6.4.2", + "debug": "6666953af031134e64c2a1e3424a34c356c7bfc67deee1f3b71474cb4c0ea71b", + "release": "db3c3163fb478377e3510f9d6b30bbbfab4af5439b35b8e78bdd7997002f4357" }, "armv7": { - "qt6": { - "debug": "acc3ea2126f84741eb0a803344f4b626d44bc739cb556416f2594616064fcbcb", - "release": "75f343a6f015b58463e15762e8af79309656b648de1e8550b2e41aeea71c3a5e" - } + "qt_version": "6.4.2", + "debug": "21011415966aa71a70ef5f4b6c4f9c8fbe6657bbe1270981e6bafe0bfe176644", + "release": "864d173d7d5777852402eef92263f3dcf6628fc640ded86c415ea38d8b451197" } }, "debian_bullseye": { "amd64": { - "qt6": { - "debug": "434603d57ffee0c4b6640dfcd1b06bb8c745c199d8a81d50c1cc304244bcf7ff", - "release": "bc6540399f8fb198e194fc6080f0056267bce827511ade8f13843eb92f987bca" - } + "qt_version": "6.4.2", + "debug": "f0bc24c51b535152416fe3e0e26380f52398014d14b545d7405165aab57cf4c7", + "release": "7f77f48490b15998c5e0bd521f5171ff2e84da48f8a64a352e326d6251bca326" }, "arm64": { - "qt6": { - "debug": "01e2b25c191dac140fe9c24f3d3e22bf1020ed8ee44867d25e021b0eded8699e", - "release": "25d25009e563899fd1abfd2ca079b3e396f20774a680a991c086d5f5430dd5a8" - } + "qt_version": "6.4.2", + "debug": "fedf553b11146ffb78b21edf9a4c2baa4a7762c8ff48a1dbd17df8eb68dcac07", + "release": "1da73a58405df370e93a63e1e543edd77e4c17a36b9a418160e681023767663e" }, "armv6": { - "qt5": { - "debug": "8b253cac7614e5debda8b8749d963b1d944f54198fd12b3f63e31bfd4a4e57e6", - "release": "8c3f23457e5f033f391986fa860b0f7835053fcfdc34ba4c8e4f345349ea5610" - } + "qt_version": "5.15.2", + "debug": "ded600345ddf13a720dea9526851c3c9a0c3c6f2d8a09562522c68359172f087", + "release": "154ef53cceb244a38017ebfb93df8648759d6fa136fcc1f1ed56b3e52e1b2e11" }, "armv7": { - "qt6": { - "debug": "8099bef7e66a14b83736dea63f3489b1387533bd02107ef41b259323a0379086", - "release": "9231e3dff694d4cb2f513ec087574b01fae8f8d48ca30f97a5d30d194e8c5b71" - } + "qt_version": "6.4.2", + "debug": "d718231973b00d074c180efa9c8e67e889de13e285c9cc4636b2438a6c2cbce5", + "release": "2aa94250e99f348202d87bfb668053b503ed1a0bb96430b27764080506c89367" } }, "debian_trixie": { "amd64": { - "qt6": { - "debug": "38d9ebb0e97120ec815977f855eb5c44681f8a312f70a0fb0e3e0d7fc1760ea4", - "release": "6b8513dd42036428b704d775c5e4e39bd324485bd9d54ff49ad6056259e53425" - } + "qt_version": "6.8.2", + "debug": "6c7a63ff73ab6ec215d18a3aa1a61d75adc70d38f8b8cf31bbb7552d64848ce3", + "release": "a0546be2ce99d482ecce7e9bd72444784717f841ab9678049337fc0ff4f3fc67" }, "arm64": { - "qt6": { - "debug": "042a822aac4c0567b4074fe7a665f1fc1a61e6f3eb9a035c113f585ed2ee11c7", - "release": "322cd767e3b4e7d18166af091cb83bac8c7b7001552da0a379f397c680da278f" - } + "qt_version": "6.8.2", + "debug": "b82a0e8a12ae5171a9e2d410558d9d2ea41b1347abb9584b401cf0afcf3a816d", + "release": "a05ae0959a59bf2f6027a3e6df57c66e5cd0ff4b528cbc8fea62fda12949719d" }, "armv6": { - "qt6": { - "debug": "7b72cf3b1f98000d60789ec19c33afa05c368198717bf9e4609c0c1a0a721ede", - "release": "8bffeb651aa7c3c0337567c949a7587720e130c4da64078408d0fd9e8edcac42" - } + "qt_version": "6.8.2", + "debug": "7dacde55ac25973658e94eb873529800812a534113d3bc2e65af14f781b7cceb", + "release": "9aec7657d52171259bc50319c2079a34136b1f96a4cf23892cb14d016121cde3" }, "armv7": { - "qt6": { - "debug": "6f8df2ce86dd0cd3006ddb9502ac383f764c6bee2f7ec7229b0b4b66fa0abe4a", - "release": "dbb31da97e047bfb77fb872d54f8bf38909cfa369d279942f9de9c4ca807189f" - } + "qt_version": "6.8.2", + "debug": "6f0df0bb7671a38746f0af1897b1c306c51c34d94f453beba912430c7e63234a", + "release": "349e2bcba3b2361840f03110807489b25e5b1de186483a713ae990f825358736" } }, "macos": { "arm64": { - "qt6": { - "debug": "15c33416e706ebb9c32b9785e9b47e05c2eed484a729e2eb672966586bf248da", - "release": "8fcbc9ac2f753d312a62f50ad39632d5befde3397b53d00de6e6803b8a0cf889" - } + "qt_version": "6.9.3", + "debug": "9b4566c573535edeb02fbf76925d8a0c6881b29b32ae85ef1af6dfd7ada1fa39", + "release": "76923c8d06f2aa7519367a85a117bfd58183848a9368d3c244b248f8691b4d81" }, "x64": { - "qt6": { - "debug": "5873403e647af0d199cd79857058030894d4175ee6d1df237b0212f69fb2cc98", - "release": "bd6c5704423f1f782771ae68695960554b9b56ec43f83dff3ce938f067fb2a36" - } + "qt_version": "6.9.3", + "debug": "6ae7fec1f607577a004f29125cad6c2d0877dc88b1589509625bfa139c8d7e2b", + "release": "d8eaf0cb1694e3b0ee0608fe2d70351e7aa6c43074a373573d6411c93daf4b98" } }, "windows": { "arm64": { - "qt6": { - "release": "0f5a324b1d852dd7d2093e3c275bcd3209e368fda32dc8c760f3e26931c687e8", - "relwithdebinfo": "68e6e468b0cc9ff47a54454b120cbf47ef84a575d2a1da9bd7912132799fda1c" - } + "qt_version": "6.9.3", + "release": "100d981764e5cd761bba0edf25dc46a0975be5f56766d34a5fbdb66053929a91", + "relwithdebinfo": "d7c51b3f8d1903e7babb379e1b30210ffbcd3a7b18bbafbc5d7b8030653dfee6" }, "x64": { - "qt6": { - "release": "de989913f047aff4d5f8bf5ee65ebf7b14aaa9478daabfb361e52883c100a181", - "relwithdebinfo": "5741b925ec912e1348f4e0b549aa0bb33b24e31d303236bb6f78399646aed3a2" - } + "qt_version": "6.9.3", + "release": "9925c722758f09b7174b3a861a754c100a03e55f9fae132bd4c428d9744ca2f3", + "relwithdebinfo": "7b693d95f221dd678c70660f41f1f9cf27b146b1ab6f5732096c6641b5b8a5bf" } } } diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 0370ce54c..86205495a 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -71,7 +71,6 @@ jobs: with: os: debian_${{ inputs.codename }} architecture: ${{ matrix.os.architecture[0] }} - qt_version: ${{ inputs.codename == 'bullseye' && matrix.os.architecture[0] == 'armv6' && '5' || '6' }} build_type: ${{ inputs.event_name == 'pull_request' && 'debug' || 'release' }} - name: 👷 Build ${{ env.HINT }} @@ -87,7 +86,7 @@ jobs: -e LC_ALL="C.UTF-8" \ ghcr.io/hyperion-project/debian:${{ env.DOCKER_TAG }} \ /bin/bash -c " - git config --global --add safe.directory /source && + git config --global --add safe.directory /source && cmake --preset linux-${{ env.BUILD_TYPE }} ${{ steps.dependencies.outputs.cmakeArgs }} -DPLATFORM=${{ matrix.os.platform }} ${{ env.CPACK_SYSTEM_PROCESSOR }} && cmake --build --preset linux-${{ env.BUILD_TYPE }} --target package && cp /source/build/Hyperion-* /deploy/ 2>/dev/null" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 0813ea7dc..b90496f6e 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -42,7 +42,7 @@ jobs: - name: ⬇ Checkout uses: actions/checkout@v5 with: - submodules: recursive + submodules: recursive fetch-depth: 0 # Ensures all tags are fetched - name: 🔧 Prepare @@ -55,12 +55,6 @@ jobs: echo -n "+nightly$(date '+%Y%m%d')" >> .version fi - - name: 📥 Install dependencies - uses: tecolicom/actions-use-homebrew-tools@v1 - with: - tools: qt@6 coreutils - key: ${{ runner.os }}-${{ matrix.os.architecture }}-homebrew-packages - - name: 💾 Download Pre-Build Dependencies id: dependencies uses: ./.github/actions/download-pre-built-deps @@ -69,10 +63,20 @@ jobs: architecture: ${{ matrix.os.architecture }} build_type: ${{ inputs.event_name == 'pull_request' && 'debug' || 'release' }} + - name: 📥 Install Qt ${{ env.QT_VERSION }} + uses: jurplel/install-qt-action@v4 + with: + version: ${{ env.QT_VERSION }} + target: 'desktop' + modules: 'qtserialport qtwebsockets' + cache: 'true' + cache-key-prefix: 'cache-qt-macos' + env: + QT_VERSION: ${{ steps.dependencies.outputs.qtVersion != '' && steps.dependencies.outputs.qtVersion || '' }} + - name: 👷 Build ${{ env.HINT }} shell: bash run: | - # Build cmake --preset macos-${{ env.BUILD_TYPE }} ${{ steps.dependencies.outputs.cmakeArgs }} cmake --build --preset macos-${{ env.BUILD_TYPE }} --target package env: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 2cfb87de5..4ebe32f12 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -43,7 +43,7 @@ jobs: uses: actions/checkout@v5 with: submodules: recursive - fetch-depth: 0 # Ensures all tags are fetched + fetch-depth: 0 # Ensures all tags are fetched - name: 🔧 Prepare shell: bash @@ -55,21 +55,31 @@ jobs: echo -n "+nightly$(date '+%Y%m%d')" >> .version fi + - name: 💾 Download Pre-Build Dependencies + id: dependencies + uses: ./.github/actions/download-pre-built-deps + with: + os: 'windows' + architecture: ${{ matrix.os.architecture }} + build_type: ${{ inputs.event_name == 'pull_request' && 'relwithdebinfo' || 'release' }} + - name: 📥 Install Python uses: actions/setup-python@v6 with: python-version: '3.13.7' architecture: ${{ matrix.os.architecture }} - - name: 📥 Install Qt + - name: 📥 Install Qt ${{ env.QT_VERSION }} uses: jurplel/install-qt-action@v4 with: - version: '6.8.*' + version: ${{ env.QT_VERSION }} target: 'desktop' modules: 'qtserialport qtwebsockets' cache: 'true' cache-key-prefix: 'cache-qt-windows' setup-python: 'false' + env: + QT_VERSION: ${{ steps.dependencies.outputs.qtVersion != '' && steps.dependencies.outputs.qtVersion || '' }} - name: 📥 Install latest CMake and Ninja uses: lukka/get-cmake@latest @@ -85,39 +95,31 @@ jobs: - name: Download/Install Inno Setup run: | echo "Checking pre-installation..." - + set "ISCC_PATH=%programfiles(x86)%\Inno Setup 6\ISCC.exe" - + if exist "%ISCC_PATH%" ( echo ::warning::Inno Setup is already installed exit /b 0 ) - + echo "Downloading Inno Setup..." - + set "_url_=https://jrsoftware.org/download.php/is.exe?site=1" set "_file_=innosetup.exe" - + curl -L -o "%tmp%\%_file_%" "%_url_%" - + echo "Installing Inno Setup..." "%tmp%\%_file_%" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- - + if not exist "%ISCC_PATH%" ( echo "::error::Inno Setup installation failed" exit 1 ) - + echo "Inno Setup installed successfully!" shell: cmd - - - name: 💾 Download Pre-Build Dependencies - id: dependencies - uses: ./.github/actions/download-pre-built-deps - with: - os: 'windows' - architecture: ${{ matrix.os.architecture }} - build_type: ${{ inputs.event_name == 'pull_request' && 'relwithdebinfo' || 'release' }} - name: 👷 Build ${{ env.HINT }} shell: cmd diff --git a/CHANGELOG.md b/CHANGELOG.md index b23bc061d..ca6a6c0ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - HTTPS support for homeassistant LED devices (#1886) - Hue Bridge - Use https and certificates for all API calls, support Bridge Pro (V3) - Hue Bridge - Alternate certificate support +- Linux: New DRM/KMS screen grabber with plane-based capture - not feature complete yet +- Logging/Tracing: Introduced qlogging categories to enable dynamic tracing --- @@ -24,9 +26,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Hue Bridge - Wizard updates to support bridge-ids, overall code refactoring - USB Grabber - Default hardware control properties are now applied when a new USB grabber is selected (avoids black images) +- Amlogic grabber - Support to switch between DRM & FB-DEV for CoreElec New Order version +- Web UI: Update panel title uses "Hyperion - "; skip showing the "nightly" tag in releases list +- Screen grabbers: Commonized base with getDeviceName/getInputDeviceDetails; explicit constructors; improved error handling +- Framebuffer grabber: Internal cleanup, consistent device naming, safer mmap usage +- Logger internals: use smart pointers and clean-ups - **Fixes:** - UI - Language is not selectable (#1877) + - UI - Release were not shown on Update page + - UI - Fixes for input/format selection - CEC-Handler is not stopped properly - Qt-Grabber (Windows) does not apply pixel ratio (#1882) - _Thanks to @SolberLight_ - LED-devices are not retrying to establish connectivity, if supported by the device diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f23211c3..3e7463049 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,8 +103,9 @@ message(STATUS " - Build: ${HYPERION_BUILD_ID}") # Grabber set(DEFAULT_AMLOGIC OFF) set(DEFAULT_DISPMANX OFF) -set(DEFAULT_DX OFF) set(DEFAULT_DDA OFF) +set(DEFAULT_DRM OFF) +set(DEFAULT_DX OFF) set(DEFAULT_MF OFF) set(DEFAULT_OSX OFF) set(DEFAULT_QT ON) @@ -164,6 +165,7 @@ set(DEFAULT_HYPERION_LIGHT OFF) set(DEFAULT_PRE_BUILD_DEPS OFF) if(${CMAKE_SYSTEM} MATCHES "Linux") + set(DEFAULT_DRM ON) set(DEFAULT_FB ON) set(DEFAULT_V4L2 ON) set(DEFAULT_DEV_SPI ON) @@ -288,9 +290,10 @@ if(HYPERION_LIGHT) # Disable Screen/Video Grabbers set(DEFAULT_AMLOGIC OFF) + set(DEFAULT_DDA OFF) set(DEFAULT_DISPMANX OFF) + set(DEFAULT_DRM OFF) set(DEFAULT_DX OFF) - set(DEFAULT_DDA OFF) set(DEFAULT_FB OFF) set(DEFAULT_MF OFF) set(DEFAULT_OSX OFF) @@ -331,12 +334,15 @@ message(STATUS "ENABLE_AMLOGIC = ${ENABLE_AMLOGIC}") option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ${DEFAULT_DISPMANX}) 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}") +option(ENABLE_DRM "Enable the DRM grabber" ${DEFAULT_DRM}) +message(STATUS "ENABLE_DRM = ${ENABLE_DRM}") + +option(ENABLE_DX "Enable the DirectX grabber" ${DEFAULT_DX}) +message(STATUS "ENABLE_DX = ${ENABLE_DX}") + if(ENABLE_AMLOGIC) set(ENABLE_FB ON) else() diff --git a/CMakePresets.json b/CMakePresets.json index 0a16af660..85c480c47 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -54,6 +54,7 @@ "ENABLE_AMLOGIC": "OFF", "ENABLE_DDA": "OFF", "ENABLE_DISPMANX": "OFF", + "ENABLE_DRM": "OFF", "ENABLE_DX": "OFF", "ENABLE_FB": "OFF", "ENABLE_MF": "OFF", diff --git a/HyperionConfig.h.in b/HyperionConfig.h.in index 086c4ef07..8e2269f35 100644 --- a/HyperionConfig.h.in +++ b/HyperionConfig.h.in @@ -43,6 +43,9 @@ // Define to enable CEC #cmakedefine ENABLE_CEC +// Define to enable the DRM grabber +#cmakedefine ENABLE_DRM + // Define to enable flatbuffer server #cmakedefine ENABLE_FLATBUF_SERVER diff --git a/assets/webconfig/content/update.html b/assets/webconfig/content/update.html index 3a638bd7a..5db879296 100644 --- a/assets/webconfig/content/update.html +++ b/assets/webconfig/content/update.html @@ -54,7 +54,7 @@

} matches++; - $('#versionlist').append('
Hyperion V' + window.gitHubVersionList[key].tag_name + '

' + $.i18n('update_label_type') + ' ' + type + '

' + $.i18n('update_label_description') + ' ' + DOMPurify.sanitize(marked.parse(window.gitHubVersionList[key].body)) + '


' + $.i18n('update_button_changelog') + '
'); + $('#versionlist').append('
Hyperion - ' + window.gitHubVersionList[key].tag_name + '

' + $.i18n('update_label_type') + ' ' + type + '

' + $.i18n('update_label_description') + ' ' + DOMPurify.sanitize(marked.parse(window.gitHubVersionList[key].body)) + '


' + $.i18n('update_button_changelog') + '
'); } $('#update_currver').append($.i18n('update_versreminder', currentVersion)); diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index 43f85bcaf..b02e39154 100755 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -207,7 +207,7 @@ $(document).ready(function () { var deviceSelected = conf_editor_screen.getEditor("root.framegrabber.available_devices").getValue(); var videoInputSelected = conf_editor_screen.getEditor("root.framegrabber.device_inputs").getValue(); - //Update hidden input element + //Update hidden input element conf_editor_screen.getEditor("root.framegrabber.input").setValue(parseInt(videoInputSelected)); var addSchemaElements = {}; @@ -216,8 +216,9 @@ $(document).ready(function () { var enumDefaultVal = ""; var deviceProperties = getPropertiesOfDevice("screen", deviceSelected); - - var formats = deviceProperties.video_inputs[videoInputSelected].formats; + + const videoInput = deviceProperties.video_inputs.find(input => input.inputIdx === parseInt(videoInputSelected)); + const formats = videoInput.formats; var formatIdx = 0; var resolutions = formats[formatIdx].resolutions; @@ -262,7 +263,8 @@ $(document).ready(function () { var deviceProperties = getPropertiesOfDevice("screen", deviceSelected); - var formats = deviceProperties.video_inputs[videoInputSelected].formats; + const videoInput = deviceProperties.video_inputs.find(input => input.inputIdx == videoInputSelected); + const formats = videoInput.formats; var formatIdx = 0; //Update hidden resolution related elements diff --git a/assets/webconfig/js/ui_utils.js b/assets/webconfig/js/ui_utils.js index cd28cdcb2..896445ca0 100644 --- a/assets/webconfig/js/ui_utils.js +++ b/assets/webconfig/js/ui_utils.js @@ -1343,6 +1343,8 @@ function getReleases(callback) { // Iterate through releases releases.forEach((release) => { + + if (release.tag_name === "nightly") return; if (release.draft) return; if (release.tag_name.includes('alpha') && semverLite.gt(release.tag_name, highestAlphaRelease.tag_name)) { diff --git a/cmake/FindLibDRM.cmake b/cmake/FindLibDRM.cmake new file mode 100644 index 000000000..5d5f105e5 --- /dev/null +++ b/cmake/FindLibDRM.cmake @@ -0,0 +1,63 @@ +#.rst: +# FindLibDRM +# ---------- +# Finds the LibDRM library +# +# This will define the following variables:: +# +# LIBDRM_FOUND - system has LibDRM +# LIBDRM_INCLUDE_DIRS - the LibDRM include directory +# LIBDRM_LIBRARIES - the LibDRM libraries +# LIBDRM_DEFINITIONS - the LibDRM definitions +# +# and the following imported targets:: +# +# LibDRM::LibDRM - The LibDRM library + +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_LIBDRM libdrm>=2.4.95 QUIET) +endif() + +find_path(LIBDRM_INCLUDE_DIR NAMES drm.h + PATH_SUFFIXES libdrm drm + PATHS ${PC_LIBDRM_INCLUDEDIR}) +find_library(LIBDRM_LIBRARY NAMES drm + PATHS ${PC_LIBDRM_LIBDIR}) + +set(LIBDRM_VERSION ${PC_LIBDRM_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibDRM + REQUIRED_VARS + LIBDRM_LIBRARY + LIBDRM_INCLUDE_DIR + VERSION_VAR + LIBDRM_VERSION +) + +include(CheckCSourceCompiles) +set(CMAKE_REQUIRED_INCLUDES ${LIBDRM_INCLUDE_DIR}) +check_c_source_compiles("#include + int main() + { + struct hdr_output_metadata test; + return test.metadata_type; + } + " LIBDRM_HAS_HDR_OUTPUT_METADATA) + +if(LIBDRM_FOUND) + set(LIBDRM_LIBRARIES ${LIBDRM_LIBRARY}) + set(LIBDRM_INCLUDE_DIRS ${LIBDRM_INCLUDE_DIR}) + if(LIBDRM_HAS_HDR_OUTPUT_METADATA) + set(LIBDRM_DEFINITIONS -DHAVE_HDR_OUTPUT_METADATA=1) + endif() + + if(NOT TARGET LIBDRM::LIBDRM) + add_library(LIBDRM::LIBDRM UNKNOWN IMPORTED) + set_target_properties(LIBDRM::LIBDRM PROPERTIES + IMPORTED_LOCATION "${LIBDRM_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBDRM_INCLUDE_DIR}") + endif() +endif() + +mark_as_advanced(LIBDRM_INCLUDE_DIR LIBDRM_LIBRARY) diff --git a/cmake/macos/BundleMacOS.cmake.in b/cmake/macos/BundleMacOS.cmake.in index 507dab63b..208870e01 100644 --- a/cmake/macos/BundleMacOS.cmake.in +++ b/cmake/macos/BundleMacOS.cmake.in @@ -49,7 +49,7 @@ if(EXISTS ${MAC_BUNDLE_INSTALL_BIN_DIR}/@MAC_BUNDLE_NAME@) foreach(plugin "platforms" "sqldrivers" "imageformats" "tls") if(EXISTS ${QT_INSTALL_PLUGINS}/${plugin}) file(GLOB files "${QT_INSTALL_PLUGINS}/${plugin}/*.dylib") - list(FILTER files EXCLUDE REGEX ".*(sqlodbc|sqlpsql)\\.(dylib)$") + list(FILTER files EXCLUDE REGEX ".*(sqlodbc|sqlpsql|sqlmimer)\\.(dylib)$") file(GET_RUNTIME_DEPENDENCIES LIBRARIES @@ -77,6 +77,7 @@ if(EXISTS ${MAC_BUNDLE_INSTALL_BIN_DIR}/@MAC_BUNDLE_NAME@) ${MAC_BUNDLE_INSTALL_PLUGIN_DIR}/${plugin} TYPE SHARED_LIBRARY + FOLLOW_SYMLINK_CHAIN ) foreach(dependency ${DEPS_UNRESOLVED}) @@ -114,19 +115,34 @@ if(EXISTS ${MAC_BUNDLE_INSTALL_BIN_DIR}/@MAC_BUNDLE_NAME@) ) endif() - file(GLOB_RECURSE LIBS FOLLOW_SYMLINKS "${MAC_BUNDLE_INSTALL_DIR}/*.dylib") - file(GLOB FRAMEWORKS FOLLOW_SYMLINKS LIST_DIRECTORIES ON "${MAC_BUNDLE_INSTALL_LIB_DIR}/*") - foreach(item ${LIBS} ${FRAMEWORKS} ${PYTHON_FRAMEWORK} ${MAC_BUNDLE_INSTALL_DIR}) - set(cmd codesign --deep --force --sign - "${item}") + if("@CMAKE_SYSTEM_PROCESSOR@" STREQUAL "arm64") + message("Running ad hoc code signing...") + file(GLOB_RECURSE LIBS FOLLOW_SYMLINKS "${MAC_BUNDLE_INSTALL_DIR}/*.dylib") + file(GLOB FRAMEWORKS FOLLOW_SYMLINKS LIST_DIRECTORIES ON "${MAC_BUNDLE_INSTALL_LIB_DIR}/*") + foreach(ITEM ${LIBS} ${FRAMEWORKS} ${PYTHON_FRAMEWORK} ${MAC_BUNDLE_INSTALL_DIR}) + execute_process( + COMMAND codesign --deep --force --sign - "${ITEM}" + RESULT_VARIABLE SIGN_RESULT + ERROR_VARIABLE SIGN_ERROR + ) + if(NOT SIGN_RESULT EQUAL 0) + message(WARNING "Failed to sign ${ITEM}: ${SIGN_ERROR}") + endif() + endforeach() + + message("Verify the signature...") execute_process( - COMMAND ${cmd} - RESULT_VARIABLE codesign_result + COMMAND codesign --verify --deep --verbose=2 ${MAC_BUNDLE_INSTALL_DIR} + RESULT_VARIABLE VERIFY_RESULT + ERROR_VARIABLE VERIFY_ERROR ) - if(NOT codesign_result EQUAL 0) - message(WARNING "macOS signing failed; ${cmd} returned ${codesign_result}") + if(NOT VERIFY_RESULT EQUAL 0) + message(WARNING "Code signature verification failed: ${VERIFY_ERROR}") + else() + message("Code signing completed successfully") endif() - endforeach() + endif() elseif(EXISTS ${MAC_BUNDLE_INSTALL_DIR}) file(REMOVE_RECURSE ${MAC_BUNDLE_INSTALL_DIR}) endif() diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 48e59ff20..f1847cca8 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -385,7 +385,7 @@ if(ENABLE_FLATBUF_SERVER OR ENABLE_FLATBUF_CONNECT) COMMAND "$" ARGS -c --no-includes --gen-mutable --gen-object-api -o "${CMAKE_CURRENT_BINARY_DIR}" "${ABS_FIL}" DEPENDS ${ABS_FIL} flatc - COMMENT "Running flatbuffers compiler on ${FIL}" + COMMENT "Running FlatBuffers Compiler '$' on '${FIL}'" VERBATIM ) set_property(SOURCE ${OUT_FILE} PROPERTY SKIP_AUTOMOC ON) @@ -424,10 +424,10 @@ if(ENABLE_PROTOBUF_SERVER) endif() if(NOT USE_SYSTEM_PROTO_LIBS) - + # Enable ProtoBuf verbose mode set(protobuf_VERBOSE OFF CACHE BOOL "Build protobuf verbose") - + # Build Protobuf as static library set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "Build protobuf shared") @@ -439,9 +439,6 @@ if(ENABLE_PROTOBUF_SERVER) # Enable build of libupb set(protobuf_BUILD_LIBUPB ON CACHE BOOL "Build libupb") - - # Force all dependencies to be downloaded from GitHub - set(protobuf_FORCE_FETCH_DEPENDENCIES ON CACHE INTERNAL "Force all dependencies to be downloaded from GitHub") # Build abeil (3rd party sub-module) with C++ version requirements set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") @@ -465,15 +462,14 @@ if(ENABLE_PROTOBUF_SERVER) set(protobuf_BUILD_PROTOC_BINARIES OFF CACHE BOOL "Build protobuf libraries and protoc compiler") endif() - include(FetchContent) + # Apply patches to Protobuf + execute_process( + COMMAND git apply -q "${CMAKE_CURRENT_SOURCE_DIR}/patch/protobuf/atomic.diff" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf" + ) # Add Protobuf directory to the build - FetchContent_Declare( - protobuf - SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf" - PATCH_COMMAND git apply "${CMAKE_CURRENT_SOURCE_DIR}/patch/protobuf/atomic.diff" - ) - FetchContent_MakeAvailable(protobuf) + add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf") if(CMAKE_CROSSCOMPILING) if(IMPORT_PROTOC) @@ -486,7 +482,6 @@ if(ENABLE_PROTOBUF_SERVER) PREFIX ${CMAKE_BINARY_DIR}/dependencies/external/protoc-host BUILD_ALWAYS OFF DOWNLOAD_COMMAND "" - PATCH_COMMAND git apply "${CMAKE_CURRENT_SOURCE_DIR}/patch/protobuf/atomic.diff" INSTALL_COMMAND "" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf CMAKE_ARGS -Dprotobuf_BUILD_LIBPROTOC:BOOL=OFF @@ -575,7 +570,7 @@ if(ENABLE_PROTOBUF_SERVER) COMMAND "$" ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL} DEPENDS ${ABS_FIL} protoc - COMMENT "Running C++ protocol buffer compiler on ${FIL}" + COMMENT "Running C++ Protocol Buffers Compiler '$' on '${FIL}'" VERBATIM ) set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc" PROPERTY SKIP_AUTOMOC ON) diff --git a/include/api/API.h b/include/api/API.h index 0ea331c69..ae66a43c9 100644 --- a/include/api/API.h +++ b/include/api/API.h @@ -52,7 +52,7 @@ class API : public QObject /// @param localConnection Is this a local network connection? Use utils/NetOrigin to check that /// @param parent Parent QObject /// - API(Logger *log, bool localConnection, QObject *parent); + API(QSharedPointer log, bool localConnection, QObject *parent); protected: /// @@ -379,7 +379,7 @@ class API : public QObject AuthManager *_authManager; HyperionIManager *_instanceManager; - Logger *_log; + QSharedPointer _log; // current instance index quint8 _currInstanceIndex; diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index b388e93b0..8dc3bb8c5 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -36,7 +36,8 @@ class JsonAPI : public API /// @param localConnection True when the sender has origin home network /// @param noListener if true, this instance won't listen for hyperion push events /// - JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject *parent, bool noListener = false); + JsonAPI(QString peerAddress, QSharedPointerlog, bool localConnection, QObject *parent, bool noListener = false); + ~JsonAPI() override; /// /// Handle an incoming JSON message diff --git a/include/api/JsonCallbacks.h b/include/api/JsonCallbacks.h index 84d10a0db..38d8fa754 100644 --- a/include/api/JsonCallbacks.h +++ b/include/api/JsonCallbacks.h @@ -25,7 +25,8 @@ class JsonCallbacks : public QObject Q_OBJECT public: - JsonCallbacks(Logger* log, const QString& peerAddress, QObject* parent); + JsonCallbacks(QSharedPointer log, const QString& peerAddress, QObject* parent); + ~JsonCallbacks() override; /// /// @brief Subscribe to future data updates given by cmd @@ -194,7 +195,7 @@ private slots: void doCallback(Subscription::Type cmd, const QJsonArray& data); void doCallback(Subscription::Type cmd, const QJsonObject& data); - Logger *_log; + QSharedPointer _log; quint8 _instanceID; QWeakPointer _hyperionWeak; diff --git a/include/api/JsonInfo.h b/include/api/JsonInfo.h index 2a3cbca30..f595e2fd8 100644 --- a/include/api/JsonInfo.h +++ b/include/api/JsonInfo.h @@ -14,8 +14,8 @@ class JsonInfo { public: - static QJsonObject getInfo(const QSharedPointer& hyperionInstance, Logger* log); - static QJsonArray getAdjustmentInfo(const QSharedPointer& hyperionInstance, Logger* log); + static QJsonObject getInfo(const QSharedPointer& hyperionInstance, QSharedPointer log); + static QJsonArray getAdjustmentInfo(const QSharedPointer& hyperionInstance, QSharedPointer log); static QJsonArray getPrioritiestInfo(const QSharedPointer& hyperionInstance); static QJsonArray getPrioritiestInfo(int currentPriority, const PriorityMuxer::InputsMap& activeInputs); static QJsonArray getEffects(); diff --git a/include/blackborder/BlackBorderProcessor.h b/include/blackborder/BlackBorderProcessor.h index 2e1cb4ecf..515698caf 100644 --- a/include/blackborder/BlackBorderProcessor.h +++ b/include/blackborder/BlackBorderProcessor.h @@ -116,8 +116,9 @@ namespace hyperion private: /// Hyperion instance QWeakPointer _hyperionWeak; + /// Logger instance - Logger* _log; + QSharedPointer _log; /// /// Updates the current border based on the newly detected border. Returns true if the diff --git a/include/boblightserver/BoblightServer.h b/include/boblightserver/BoblightServer.h index 68c6c6442..78ce62166 100644 --- a/include/boblightserver/BoblightServer.h +++ b/include/boblightserver/BoblightServer.h @@ -90,7 +90,7 @@ private slots: int _priority; /// Logger instance - Logger * _log; + QSharedPointer _log; // current port uint16_t _port; diff --git a/include/cec/CECHandler.h b/include/cec/CECHandler.h index 6639f347b..900ce7fa7 100644 --- a/include/cec/CECHandler.h +++ b/include/cec/CECHandler.h @@ -32,7 +32,8 @@ class CECHandler : public QObject { Q_OBJECT public: - CECHandler(const QJsonDocument& config, QObject * parent = nullptr); + explicit CECHandler(const QJsonDocument& config, QObject * parent = nullptr); + ~CECHandler() override; QString scan() const; @@ -90,5 +91,5 @@ public slots: QMap _cecEventActionMap; - Logger * _logger {}; + QSharedPointer _logger; }; diff --git a/include/db/DBManager.h b/include/db/DBManager.h index bbc5be9b4..7ca062a3d 100644 --- a/include/db/DBManager.h +++ b/include/db/DBManager.h @@ -34,6 +34,7 @@ class DBManager : public QObject public: explicit DBManager(QObject* parent = nullptr); + ~DBManager() override; static void initializeDatabase(const QDir& dataDirectory, bool isReadOnly); @@ -165,7 +166,7 @@ class DBManager : public QObject void logErrorAndAppend(const QString& errorText, QStringList& errorList); protected: - Logger* _log; + QSharedPointer _log; private: static QDir _dataDirectory; diff --git a/include/effectengine/Effect.h b/include/effectengine/Effect.h index 3d4f8dd6f..6f36956da 100644 --- a/include/effectengine/Effect.h +++ b/include/effectengine/Effect.h @@ -103,7 +103,8 @@ public slots: /// Buffer for colorData QVector _colors; - Logger* _log; + /// Logger instance + QSharedPointer _log; // Reflects whenever this effects should interrupt (timeout or external request) std::atomic _interupt{}; diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h index 01ec83f89..634d03561 100644 --- a/include/effectengine/EffectEngine.h +++ b/include/effectengine/EffectEngine.h @@ -110,7 +110,7 @@ private slots: std::list _cachedActiveEffects; - Logger * _log; + QSharedPointer _log; // The global effect file handler EffectFileHandler * _effectFileHandler; diff --git a/include/effectengine/EffectFileHandler.h b/include/effectengine/EffectFileHandler.h index 4e7e452e1..487a44f53 100644 --- a/include/effectengine/EffectFileHandler.h +++ b/include/effectengine/EffectFileHandler.h @@ -77,7 +77,7 @@ public slots: private: QJsonObject _effectConfig; - Logger* _log; + QSharedPointer _log; const QString _rootPath; // available effects diff --git a/include/events/EventHandler.h b/include/events/EventHandler.h index 4eaf8e964..794f56273 100644 --- a/include/events/EventHandler.h +++ b/include/events/EventHandler.h @@ -5,6 +5,9 @@ #include #include +#include +#include + class Logger; @@ -36,7 +39,7 @@ public slots: void signalEvent(Event event); protected: - Logger * _log {}; + QSharedPointer _log; private: EventHandler(); diff --git a/include/events/EventScheduler.h b/include/events/EventScheduler.h index d350a55ff..ff5f61dd7 100644 --- a/include/events/EventScheduler.h +++ b/include/events/EventScheduler.h @@ -49,7 +49,7 @@ private slots: QList _scheduledEvents; QList _timers; - Logger * _log {}; + QSharedPointer _log; }; #endif // EVENTSCHEDULER_H diff --git a/include/events/OsEventHandler.h b/include/events/OsEventHandler.h index 6bed4f281..18125f459 100644 --- a/include/events/OsEventHandler.h +++ b/include/events/OsEventHandler.h @@ -1,5 +1,6 @@ #ifndef OSEVENTHANDLER_H #define OSEVENTHANDLER_H + #include #include @@ -13,6 +14,7 @@ #include #endif +#include #include class Logger; @@ -51,7 +53,7 @@ public slots: bool _isService; - Logger* _log{}; + QSharedPointer _log; }; #if defined(_WIN32) diff --git a/include/flatbufserver/FlatBufferConnection.h b/include/flatbufserver/FlatBufferConnection.h index 53dcabb45..5461b6035 100644 --- a/include/flatbufserver/FlatBufferConnection.h +++ b/include/flatbufserver/FlatBufferConnection.h @@ -146,7 +146,7 @@ private slots: QByteArray _receiveBuffer; QTimer _timer; - Logger * _log; + QSharedPointer _log; flatbuffers::FlatBufferBuilder _builder; bool _isRegistered; diff --git a/include/flatbufserver/FlatBufferServer.h b/include/flatbufserver/FlatBufferServer.h index 6fa1ef096..da050140e 100644 --- a/include/flatbufserver/FlatBufferServer.h +++ b/include/flatbufserver/FlatBufferServer.h @@ -66,7 +66,7 @@ private slots: private: QScopedPointer _server; NetOrigin* _netOrigin; - Logger* _log; + QSharedPointer _log; int _timeout; quint16 _port; const QJsonDocument _config; diff --git a/include/forwarder/MessageForwarder.h b/include/forwarder/MessageForwarder.h index d4458a403..3badeda0e 100644 --- a/include/forwarder/MessageForwarder.h +++ b/include/forwarder/MessageForwarder.h @@ -123,7 +123,7 @@ private slots: void stopFlatbufferTargets(); /// Logger instance - Logger *_log; + QSharedPointer _log; QJsonDocument _config; bool _isActive = true; diff --git a/include/grabber/GrabberConfig.h b/include/grabber/GrabberConfig.h index 0b14132cc..c5e0a1901 100644 --- a/include/grabber/GrabberConfig.h +++ b/include/grabber/GrabberConfig.h @@ -1,34 +1,36 @@ #ifndef GRABBERCONFIG_H #define GRABBERCONFIG_H -#if defined(ENABLE_MF) -#include -#elif defined(ENABLE_V4L2) -#include +#if defined(ENABLE_AMLOGIC) +#include #endif -#if defined(ENABLE_AUDIO) -#include +#if defined(ENABLE_DISPMANX) +#include +#endif -#ifdef WIN32 -#include +#ifdef ENABLE_DX +#include #endif -#ifdef __linux__ -#include +#ifdef ENABLE_DDA +#include #endif + +#ifdef ENABLE_DRM +#include #endif -#ifdef ENABLE_QT -#include +#if defined(ENABLE_FB) +#include #endif -#ifdef ENABLE_DX -#include +#if defined(ENABLE_OSX) +#include #endif -#ifdef ENABLE_DDA -#include +#ifdef ENABLE_QT +#include #endif #if defined(ENABLE_X11) @@ -39,20 +41,22 @@ #include #endif -#if defined(ENABLE_FB) -#include +#if defined(ENABLE_MF) +#include +#elif defined(ENABLE_V4L2) +#include #endif -#if defined(ENABLE_DISPMANX) -#include -#endif +#if defined(ENABLE_AUDIO) +#include -#if defined(ENABLE_AMLOGIC) -#include +#ifdef WIN32 +#include #endif -#if defined(ENABLE_OSX) -#include +#ifdef __linux__ +#include +#endif #endif #endif // GRABBERCONFIG_H diff --git a/include/grabber/amlogic/AmlogicGrabber.h b/include/grabber/amlogic/AmlogicGrabber.h index f61742aa8..e65f681f0 100644 --- a/include/grabber/amlogic/AmlogicGrabber.h +++ b/include/grabber/amlogic/AmlogicGrabber.h @@ -1,10 +1,13 @@ #pragma once +#include + // Utils includes #include #include #include #include +#include /// /// @@ -15,14 +18,14 @@ class AmlogicGrabber : public Grabber /// Construct a AmlogicGrabber that will capture snapshots with specified dimensions. /// /// - AmlogicGrabber(); + explicit AmlogicGrabber(int deviceIdx = 0); ~AmlogicGrabber() override; /// /// @brief Setup a new capture screen, will free the previous one /// @return True on success, false if no screen is found /// - bool setupScreen(); + bool setupScreen() override; /// /// Captures a single snapshot of the display and writes the data to the given image. The @@ -33,7 +36,7 @@ class AmlogicGrabber : public Grabber /// height) /// @return Zero on success else negative /// - int grabFrame(Image & image); + int grabFrame(Image &image) override; /// /// @brief Discover AmLogic screens available (for configuration). @@ -42,7 +45,7 @@ class AmlogicGrabber : public Grabber /// /// @return A JSON structure holding a list of devices found /// - QJsonObject discover(const QJsonObject& params); + QJsonObject discover(const QJsonObject ¶ms); /// /// Set the video mode (2D/3D) @@ -73,26 +76,28 @@ class AmlogicGrabber : public Grabber bool setPixelDecimation(int pixelDecimation) override; private: + + bool isGbmSupported(bool logMsg = true) const; + /** * Returns true if video is playing over the amlogic chip * @return True if video is playing else false */ bool isVideoPlaying(); - void closeDevice(int &fd); - bool openDevice(int &fd, const char* dev); + void closeDevice(int &fd) const; + bool openDevice(int &fd, const char *dev) const; - int grabFrame_amvideocap(Image & image); + int grabFrame_amvideocap(Image &image); /** The snapshot/capture device of the amlogic video chip */ - int _captureDev; - int _videoDev; + int _captureDev; + int _videoDev; Image _image_bgr; - void* _image_ptr; - ssize_t _bytesToRead; + ColorBgr* _image_ptr; - int _lastError; - bool _videoPlaying; - FramebufferFrameGrabber _fbGrabber; - int _grabbingModeNotification; + int _lastError; + bool _videoPlaying; + QScopedPointer _screenGrabber; + int _grabbingModeNotification; }; diff --git a/include/grabber/amlogic/AmlogicWrapper.h b/include/grabber/amlogic/AmlogicWrapper.h index 5e355dff5..4a279ba42 100644 --- a/include/grabber/amlogic/AmlogicWrapper.h +++ b/include/grabber/amlogic/AmlogicWrapper.h @@ -19,15 +19,17 @@ class AmlogicWrapper : public GrabberWrapper /// Constructs the Amlogic frame grabber /// /// @param[in] updateRate_Hz The image grab rate [Hz] - /// @param[in] pixelDecimation Decimation factor for image [pixels]/// + /// @param[in] deviceIdx Framebuffer device index + /// @param[in] pixelDecimation Decimation factor for image [pixels] /// - AmlogicWrapper(int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ, + explicit AmlogicWrapper(int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ, + int deviceIdx=0, int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION); /// /// Constructs the Amlogic frame grabber from configuration settings /// - AmlogicWrapper(const QJsonDocument& grabberConfig = QJsonDocument()); + explicit AmlogicWrapper(const QJsonDocument& grabberConfig = QJsonDocument()); public slots: diff --git a/include/grabber/audio/AudioGrabber.h b/include/grabber/audio/AudioGrabber.h index 115f3aa84..a8dfc385b 100644 --- a/include/grabber/audio/AudioGrabber.h +++ b/include/grabber/audio/AudioGrabber.h @@ -54,7 +54,7 @@ class AudioGrabber : public Grabber /// void restart(); - Logger* getLog(); + QSharedPointer getLog(); /// /// Set Device diff --git a/include/grabber/drm/DRMFrameGrabber.h b/include/grabber/drm/DRMFrameGrabber.h new file mode 100644 index 000000000..ba29ac9eb --- /dev/null +++ b/include/grabber/drm/DRMFrameGrabber.h @@ -0,0 +1,248 @@ +#pragma once + +#include +#include +#include + +// DRM +#include +#include +#include + +// Utils includes +#include +#include +#include +#include + +// Utility includes +#include + +Q_DECLARE_LOGGING_CATEGORY(grabber_drm) + +struct DrmProperty +{ + drmModePropertyPtr spec; + uint64_t value; +}; + +struct Connector +{ + drmModeConnectorPtr ptr; + std::map> props; +}; + +struct Encoder +{ + drmModeEncoderPtr ptr; + std::map> props; +}; + +struct DrmResources { + using drmModeConnectorPtr_unique = std::unique_ptr; + using drmModeCrtcPtr_unique = std::unique_ptr; + + std::vector connectors; + std::vector crtcs; +}; + +/// +/// The DRMFrameGrabber is used for creating snapshots of the display (screenshots) +/// +class DRMFrameGrabber : public Grabber +/** + * @brief The DRMFrameGrabber class is a screen grabber that uses the Linux Direct Rendering Manager (DRM) + * API to capture the screen content. This method is generally faster and more efficient than X11-based + * methods, as it operates at a lower level. It is suitable for systems without a running X server, + * such as dedicated media centers. + */ +{ +public: + /** + * @brief Constructs a DRMFrameGrabber. + * + * @param deviceIdx The index of the DRM device to use (e.g., 0 for /dev/dri/card0). + * @param cropLeft Number of pixels to crop from the left of the screen. + * @param cropRight Number of pixels to crop from the right of the screen. + * @param cropTop Number of pixels to crop from the top of the screen. + * @param cropBottom Number of pixels to crop from the bottom of the screen. + */ + explicit DRMFrameGrabber(int deviceIdx = 0, int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0); + + /** + * @brief Destructor for the DRMFrameGrabber. + * Ensures that all DRM resources are properly released by calling freeResources(). + */ + ~DRMFrameGrabber() override; + + /** + * @brief Captures a single frame from the configured DRM device. + * The captured frame is processed (cropped and scaled) and stored in the provided image object. + * + * @param[out] image The Image object to store the captured frame. It must be initialized + * with the correct dimensions before calling this function. + * @return 0 on success, a negative value on failure. + */ + int grabFrame(Image & image) override; + + /** + * @brief Initializes the DRM device for screen capturing. + * This involves opening the device, identifying active connectors, CRTCs, and planes, + * and setting up the necessary resources for grabbing frames. + * + * @return True if the screen was set up successfully, false otherwise. + */ + bool setupScreen() override; + + /** + * @brief Gets the resolution of the currently configured screen. + * + * @return A QSize object containing the width and height of the screen. + */ + QSize getScreenSize() const override; + + /** + * @brief Gets the resolution of a specific DRM device. + * + * @param device The name of the device (e.g., "card0"). + * @return A QSize object containing the width and height of the specified screen. + */ + QSize getScreenSize(const QString& device) const; + + /** + * @brief Sets the desired capture width and height. + * This will affect the dimensions of the image returned by grabFrame(). + * + * @param width The desired capture width. + * @param height The desired capture height. + * @return True on success, false on failure. + */ + bool setWidthHeight(int width, int height) override; + + /** + * @brief Returns the full device path for the current DRM grabber instance. + * For example, "/dev/dri/card0". + * + * @return A QString containing the device name. + */ + QString getDeviceName() const {return QString("%1/%2%3").arg(DRM_DIR_NAME, DRM_PRIMARY_MINOR_NAME).arg(_input);} + + /** + * @brief Retrieves a list of available DRM input devices. + * + * @return A QJsonArray where each element is a QJsonObject describing a found device. + */ + QJsonArray getInputDeviceDetails() const override; + + /** + * @brief Discovers available DRM devices and their properties. + * This is used for configuration purposes, allowing a user to see available screens. + * + * @param params JSON object with parameters to customize the discovery process. + * @return A QJsonObject containing a list of discovered devices and their details. + */ + QJsonObject discover(const QJsonObject& params); + +private: + + /** + * @brief Releases all allocated DRM resources. + * This includes closing the device file descriptor and freeing memory associated with + * connectors, encoders, CRTCs, planes, and framebuffers. + */ + void freeResources(); + + /** + * @brief Opens the DRM device file descriptor. + * @return True on success, false on failure. + */ + bool openDevice(); + + /** + * @brief Closes the DRM device file descriptor. + * @return True on success, false on failure. + */ + bool closeDevice(); + + /** + * @brief Gathers information about the active screen configuration. + * This includes enumerating connectors, encoders, finding the active CRTC and primary plane. + * @return True on success, false on failure. + */ + bool getScreenInfo(); + + /** + * @brief Helper function to discover DRM resources for a given device. + * @param[in] device The device path (e.g., "/dev/dri/card0"). + * @param[out] resources A struct to be filled with the discovered resource information. + * @return True on success, false on failure. + */ + bool discoverDrmResources(const QString& device, DrmResources& resources) const; + + /** + * @brief Sets the client capabilities for the DRM device connection. + * Specifically, it requests universal plane support. + */ + void setDrmClientCaps(); + + /** + * @brief Enumerates all available connectors and encoders for the device. + * @param resources The DRM resources structure obtained from the device. + */ + void enumerateConnectorsAndEncoders(const drmModeRes* resources); + + /** + * @brief Finds the active CRTC (CRT Controller) that is connected to a display. + * @param resources The DRM resources structure obtained from the device. + */ + void findActiveCrtc(const drmModeRes* resources); + + /** + * @brief Checks if a given plane is the primary plane for the active CRTC. + * @param planeId The ID of the plane to check. + * @param properties The properties of the plane object. + * @return True if it is the primary plane, false otherwise. + */ + bool isPrimaryPlaneForCrtc(uint32_t planeId, const drmModeObjectProperties* properties); + + /** + * @brief Finds the primary display plane associated with the active CRTC. + * The primary plane is the one that holds the main desktop image. + * @param planeResources The list of available planes. + */ + void findPrimaryPlane(const drmModePlaneRes* planeResources); + + /** + * @brief Retrieves properties for various DRM objects like connectors and planes. + * This is currently a placeholder and not fully implemented. + */ + void getDrmObjectProperties() const; + + /** + * @brief Retrieves the framebuffer(s) associated with the primary plane. + * This is necessary to get a handle to the screen's pixel data. + */ + void getFramebuffers(); + + /// The file descriptor for the opened DRM device. + int _deviceFd; + + /// Map of available connectors (e.g., HDMI, DP), keyed by connector ID. + std::map> _connectors; + + /// Map of available encoders, keyed by encoder ID. + std::map> _encoders; + + /// Pointer to the active CRTC (CRT Controller). + drmModeCrtcPtr _crtc; + + /// Map of available display planes, keyed by plane ID. + std::map _planes; + + /// Map of framebuffers attached to the CRTC, keyed by framebuffer ID. + std::map _framebuffers; + + /// The pixel format of the captured framebuffer. + PixelFormat _pixelFormat; +}; + diff --git a/include/grabber/drm/DRMWrapper.h b/include/grabber/drm/DRMWrapper.h new file mode 100644 index 000000000..9d189560c --- /dev/null +++ b/include/grabber/drm/DRMWrapper.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +/// +/// The DRMWrapper uses an instance of the DRMFrameGrabber to obtain ImageRgb's from the +/// displayed content. This ImageRgb is processed to a ColorRgb for each led and committed to the +/// attached Hyperion. +/// +class DRMWrapper : public GrabberWrapper +{ + Q_OBJECT +public: + /// + /// Constructs the DRM frame grabber with a specified grab size and update rate. + /// + /// @param[in] updateRate_Hz The image grab rate [Hz] + /// @param[in] device Framebuffer device name/path + /// @param[in] pixelDecimation Decimation factor for image [pixels] + /// @param[in] cropLeft Remove from left [pixels] + /// @param[in] cropRight Remove from right [pixels] + /// @param[in] cropTop Remove from top [pixels] + /// @param[in] cropBottom Remove from bottom [pixels] + /// + explicit DRMWrapper(int updateRate_Hz = GrabberWrapper::DEFAULT_RATE_HZ, + int deviceIdx = 0, + int pixelDecimation = GrabberWrapper::DEFAULT_PIXELDECIMATION, + int cropLeft = 0, int cropRight = 0, + int cropTop = 0, int cropBottom = 0); + /// + /// Constructs the QT frame grabber from configuration settings + /// + explicit DRMWrapper(const QJsonDocument &grabberConfig = QJsonDocument()); + +public slots: + /// + /// Performs a single frame grab and computes the led-colors + /// + void action() override; + +private: + /// The actual grabber + DRMFrameGrabber _grabber; +}; diff --git a/include/grabber/framebuffer/FramebufferFrameGrabber.h b/include/grabber/framebuffer/FramebufferFrameGrabber.h index e622f0b91..5265785fe 100644 --- a/include/grabber/framebuffer/FramebufferFrameGrabber.h +++ b/include/grabber/framebuffer/FramebufferFrameGrabber.h @@ -17,7 +17,7 @@ class FramebufferFrameGrabber : public Grabber /// /// @param[in] device The framebuffer device name/path /// - FramebufferFrameGrabber(int deviceIdx = 0); + explicit FramebufferFrameGrabber(int deviceIdx = 0); ~FramebufferFrameGrabber() override; @@ -29,23 +29,25 @@ class FramebufferFrameGrabber : public Grabber /// @param[out] image The snapped screenshot (should be initialized with correct width and /// height) /// - int grabFrame(Image & image); + int grabFrame(Image & image) override; /// /// @brief Setup a new capture screen, will free the previous one /// @return True on success, false if no screen is found /// - bool setupScreen(); + bool setupScreen() override; - QSize getScreenSize() const; + QSize getScreenSize() const override; QSize getScreenSize(const QString& device) const; /// ///@brief Set new width and height for framegrabber, overwrite Grabber.h implementation bool setWidthHeight(int width, int height) override; - QString getPath() const {return QString("/dev/fb%1").arg(_input);} + QString getDeviceName() const {return QString("/dev/fb%1").arg(_input);} + + QJsonArray getInputDeviceDetails() const override; /// /// @brief Discover Framebuffer screens available (for configuration). @@ -62,10 +64,7 @@ class FramebufferFrameGrabber : public Grabber bool closeDevice(); bool getScreenInfo(); - // /// Framebuffer device e.g. /dev/fb0 - QString _fbDevice; - - int _fbfd; + int _deviceFd; struct fb_var_screeninfo _varInfo; struct fb_fix_screeninfo _fixInfo; diff --git a/include/grabber/framebuffer/FramebufferWrapper.h b/include/grabber/framebuffer/FramebufferWrapper.h index 942e4f5f9..32a9fcb26 100644 --- a/include/grabber/framebuffer/FramebufferWrapper.h +++ b/include/grabber/framebuffer/FramebufferWrapper.h @@ -22,7 +22,7 @@ class FramebufferWrapper: public GrabberWrapper /// @param[in] deviceIdx Framebuffer device index /// @param[in] pixelDecimation Decimation factor for image [pixels] /// - FramebufferWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ, + explicit FramebufferWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ, int deviceIdx = 0, int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION ); @@ -30,7 +30,7 @@ class FramebufferWrapper: public GrabberWrapper /// /// Constructs the QT frame grabber from configuration settings /// - FramebufferWrapper(const QJsonDocument& grabberConfig = QJsonDocument()); + explicit FramebufferWrapper(const QJsonDocument& grabberConfig = QJsonDocument()); public slots: /// diff --git a/include/grabber/video/v4l2/V4L2Grabber.h b/include/grabber/video/v4l2/V4L2Grabber.h index 3db42711c..1e7bcc01f 100644 --- a/include/grabber/video/v4l2/V4L2Grabber.h +++ b/include/grabber/video/v4l2/V4L2Grabber.h @@ -72,7 +72,6 @@ class V4L2Grabber : public Grabber V4L2Grabber(); ~V4L2Grabber() override; - int grabFrame(Image &); void setDevice(const QString& devicePath, const QString& deviceName); bool setInput(int input) override; bool setWidthHeight(int width, int height) override; diff --git a/include/hyperion/BGEffectHandler.h b/include/hyperion/BGEffectHandler.h index de755930c..b80818af3 100644 --- a/include/hyperion/BGEffectHandler.h +++ b/include/hyperion/BGEffectHandler.h @@ -50,7 +50,7 @@ private slots: private: /// Hyperion instance pointer QWeakPointer _hyperionWeak; - Logger * _log; + QSharedPointer _log; QJsonDocument _bgEffectConfig; bool _isBgEffectEnabled; diff --git a/include/hyperion/ComponentRegister.h b/include/hyperion/ComponentRegister.h index ef3ab825b..597a96811 100644 --- a/include/hyperion/ComponentRegister.h +++ b/include/hyperion/ComponentRegister.h @@ -73,7 +73,7 @@ private slots: /// Hyperion instance QWeakPointer _hyperionWeak; /// Logger instance - Logger * _log; + QSharedPointer _log; /// current state of all components std::map _componentStates; /// on hyperion off we save the previous states of all components diff --git a/include/hyperion/Grabber.h b/include/hyperion/Grabber.h index aba5899b5..ba1d81eed 100644 --- a/include/hyperion/Grabber.h +++ b/include/hyperion/Grabber.h @@ -1,8 +1,11 @@ #pragma once -#include #include +#include +#include +#include + #include #include #include @@ -21,9 +24,8 @@ class Grabber : public QObject Q_OBJECT public: - - Grabber(const QString& grabberName = "", int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0); - + explicit Grabber(const QString &grabberName = "", int cropLeft = 0, int cropRight = 0, int cropTop = 0, int cropBottom = 0); + virtual ~Grabber(); /// /// Set the video mode (2D/3D) /// @param[in] mode The new video mode @@ -85,6 +87,8 @@ class Grabber : public QObject /// virtual void setEnabled(bool enable); + bool isEnabled() const { return _isEnabled; } + /// /// @brief get current resulting height of image (after crop) /// @@ -104,7 +108,7 @@ class Grabber : public QObject /// /// @brief Get capture interval in ms /// - int getUpdateInterval() const { return 1000/_fps; } + int getUpdateInterval() const { return 1000 / _fps; } /// /// @brief Get pixelDecimation @@ -118,11 +122,16 @@ class Grabber : public QObject /// /// @return true, on success (i.e. library is present), else false /// - virtual bool isAvailable(bool logError = true) { return _isAvailable; } + virtual bool isAvailable(bool logError = true) { return _isAvailable; } + + virtual int grabFrame(Image &) { return 0; } + virtual bool setupScreen() { return true; } + virtual QSize getScreenSize() const { return QSize(); } + virtual QJsonArray getInputDeviceDetails() const { return QJsonArray(); } public slots: - virtual void handleEvent(Event event) {} + virtual void handleEvent(Event event) { /* to be overridden by subclasses */ } protected slots: /// @@ -130,14 +139,13 @@ protected slots: /// /// @param[in] errorMsg The error message to be logged /// - virtual void setInError( const QString& errorMsg); + virtual void setInError(const QString &errorMsg); protected: - QString _grabberName; /// logger instance - Logger * _log; + QSharedPointer _log; ImageResampler _imageResampler; @@ -171,7 +179,10 @@ protected slots: int _input; /// number of pixels to crop after capturing - int _cropLeft, _cropRight, _cropTop, _cropBottom; + int _cropLeft; + int _cropRight; + int _cropTop; + int _cropBottom; // Device states @@ -185,7 +196,4 @@ protected slots: /// Is the device in error state and stopped? bool _isDeviceInError; - - - }; diff --git a/include/hyperion/GrabberWrapper.h b/include/hyperion/GrabberWrapper.h index 5cbbf8da1..6c9de7d15 100644 --- a/include/hyperion/GrabberWrapper.h +++ b/include/hyperion/GrabberWrapper.h @@ -193,7 +193,7 @@ private slots: QString _grabberName; /// The Logger instance - Logger * _log; + QSharedPointer _log; /// The timer for generating events with the specified update rate QScopedPointer _timer; diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 83ca3cd85..11d3fa40a 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -47,7 +47,7 @@ // settings utils #include -#include +#include // Forward class declaration class ImageProcessor; @@ -565,7 +565,7 @@ private slots: #endif /// Logger instance - Logger * _log; + QSharedPointer _log; /// count of hardware leds int _hwLedCount; diff --git a/include/hyperion/HyperionIManager.h b/include/hyperion/HyperionIManager.h index 6cde3e056..8d8f56a3c 100644 --- a/include/hyperion/HyperionIManager.h +++ b/include/hyperion/HyperionIManager.h @@ -245,7 +245,7 @@ private slots: void stopAll(); private: - Logger* _log; + QSharedPointer _log; QScopedPointer _instanceTable; QMap> _runningInstances; QMap> _startingInstances; diff --git a/include/hyperion/ImageProcessor.h b/include/hyperion/ImageProcessor.h index f8e2cc8dd..c63e468a1 100644 --- a/include/hyperion/ImageProcessor.h +++ b/include/hyperion/ImageProcessor.h @@ -270,7 +270,7 @@ private slots: private: /// Logger instance - Logger* _log; + QSharedPointer _log; /// The Led-string specification LedString _ledString; diff --git a/include/hyperion/ImageToLedsMap.h b/include/hyperion/ImageToLedsMap.h index 5b12067ab..42594a2a8 100644 --- a/include/hyperion/ImageToLedsMap.h +++ b/include/hyperion/ImageToLedsMap.h @@ -45,7 +45,7 @@ namespace hyperion /// @param[in] accuraryLevel The accuracy used during processing (only for selected types) /// ImageToLedsMap( - Logger* log, + QSharedPointer log, int width, int height, int horizontalBorder, @@ -355,7 +355,7 @@ namespace hyperion private: - Logger* _log; + QSharedPointer _log; /// The width of the indexed image const int _width; diff --git a/include/hyperion/LinearColorSmoothing.h b/include/hyperion/LinearColorSmoothing.h index 96d8cd758..88bbdef9f 100644 --- a/include/hyperion/LinearColorSmoothing.h +++ b/include/hyperion/LinearColorSmoothing.h @@ -185,7 +185,7 @@ private slots: QJsonObject _smoothConfig; /// Logger instance - Logger *_log; + QSharedPointer _log; /// Hyperion instance QWeakPointer _hyperionWeak; diff --git a/include/hyperion/MultiColorAdjustment.h b/include/hyperion/MultiColorAdjustment.h index 2edd549ff..3942edc98 100644 --- a/include/hyperion/MultiColorAdjustment.h +++ b/include/hyperion/MultiColorAdjustment.h @@ -65,5 +65,5 @@ class MultiColorAdjustment std::vector _ledAdjustments; // logger instance - Logger * _log; + QSharedPointer _log; }; diff --git a/include/hyperion/PriorityMuxer.h b/include/hyperion/PriorityMuxer.h index caaf6cd7c..0072beeaa 100644 --- a/include/hyperion/PriorityMuxer.h +++ b/include/hyperion/PriorityMuxer.h @@ -13,6 +13,7 @@ #include #include #include +#include // global defines constexpr auto SMOOTHING_MODE_DEFAULT = 0; @@ -265,7 +266,7 @@ private slots: hyperion::Components getComponentOfPriority(int priority) const; /// Logger instance - Logger* _log; + QSharedPointer _log; /// The current priority (lowest value in _activeInputs) int _currentPriority; diff --git a/include/hyperion/SettingsManager.h b/include/hyperion/SettingsManager.h index 9ea627736..f9b4578b3 100644 --- a/include/hyperion/SettingsManager.h +++ b/include/hyperion/SettingsManager.h @@ -78,7 +78,7 @@ class SettingsManager : public QObject /// Logger instance - Logger* _log; + QSharedPointer _log; /// Instance number quint8 _instance; diff --git a/include/jsonserver/JsonServer.h b/include/jsonserver/JsonServer.h index 305031f13..db51e85d9 100644 --- a/include/jsonserver/JsonServer.h +++ b/include/jsonserver/JsonServer.h @@ -70,7 +70,7 @@ public slots: QSet _openConnections; /// the logger instance - Logger * _log; + QSharedPointer _log; NetOrigin* _netOrigin; diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h index 2d96d8995..d2ff7a620 100644 --- a/include/leddevice/LedDevice.h +++ b/include/leddevice/LedDevice.h @@ -10,7 +10,7 @@ #include #include #include - +#include // STL includes #include @@ -58,7 +58,7 @@ class LedDevice : public QObject /// /// @param[in] log The logger to be used /// - void setLogger(Logger* log); + void setLogger(QSharedPointer log); /// /// @brief Set the current active LED-device type. @@ -435,7 +435,7 @@ public slots: QJsonObject _devConfig; /// The common Logger instance for all LED-devices - Logger * _log; + QSharedPointer _log; /// The buffer containing the packed RGB values std::vector _ledBuffer; diff --git a/include/leddevice/LedDeviceWrapper.h b/include/leddevice/LedDeviceWrapper.h index 52a2a9896..8129f7289 100644 --- a/include/leddevice/LedDeviceWrapper.h +++ b/include/leddevice/LedDeviceWrapper.h @@ -157,7 +157,7 @@ private slots: private: /// The common Logger instance for all LED-devices - Logger * _log; + QSharedPointer _log; /// Hyperion instance pointer QWeakPointer _hyperionWeak; diff --git a/include/mdns/MdnsBrowser.h b/include/mdns/MdnsBrowser.h index 16f552f52..17401a26a 100644 --- a/include/mdns/MdnsBrowser.h +++ b/include/mdns/MdnsBrowser.h @@ -67,7 +67,7 @@ public slots: void resolveServiceInstance(const QByteArray& serviceInstance, std::chrono::milliseconds waitTime = DEFAULT_DISCOVER_TIMEOUT) const; - void resolveFirstAddress(Logger* log, const QString& hostname, std::chrono::milliseconds timeout = DEFAULT_ADDRESS_RESOLVE_TIMEOUT); + void resolveFirstAddress(QSharedPointer log, const QString& hostname, std::chrono::milliseconds timeout = DEFAULT_ADDRESS_RESOLVE_TIMEOUT); Q_SIGNALS: @@ -105,7 +105,7 @@ private slots: private: /// The logger instance for mDNS-Service - Logger* _log; + QSharedPointer _log; QScopedPointer _server; QScopedPointer _cache; diff --git a/include/mdns/MdnsProvider.h b/include/mdns/MdnsProvider.h index 3282d7eb4..076665c80 100644 --- a/include/mdns/MdnsProvider.h +++ b/include/mdns/MdnsProvider.h @@ -45,7 +45,7 @@ private slots: private: /// The logger instance for mDNS-Service - Logger* _log; + QSharedPointer _log; QScopedPointer _server; QScopedPointer _hostname; diff --git a/include/protoserver/ProtoServer.h b/include/protoserver/ProtoServer.h index 05697d867..ab4c96181 100644 --- a/include/protoserver/ProtoServer.h +++ b/include/protoserver/ProtoServer.h @@ -65,8 +65,8 @@ private slots: private: QScopedPointer _server; - NetOrigin* _netOrigin; - Logger* _log; + NetOrigin* _netOrigin; + QSharedPointer _log; int _timeout; quint16 _port; const QJsonDocument _config; diff --git a/include/python/PythonProgram.h b/include/python/PythonProgram.h index cf0a8a587..a1dc6ddf5 100644 --- a/include/python/PythonProgram.h +++ b/include/python/PythonProgram.h @@ -7,6 +7,7 @@ #include #define slots Q_SLOTS +#include #include class Logger; @@ -14,7 +15,7 @@ class Logger; class PythonProgram { public: - PythonProgram(const QString & name, Logger * log); + PythonProgram(const QString & name, QSharedPointer log); ~PythonProgram(); operator PyThreadState* () @@ -27,6 +28,6 @@ class PythonProgram private: QString _name; - Logger* _log; + QSharedPointer _log; PyThreadState* _tstate; }; diff --git a/include/ssdp/SSDPDiscover.h b/include/ssdp/SSDPDiscover.h index f4ad94de1..9bb1620ed 100644 --- a/include/ssdp/SSDPDiscover.h +++ b/include/ssdp/SSDPDiscover.h @@ -1,12 +1,14 @@ #ifndef SSDPDISCOVER_H #define SSDPDISCOVER_H +#include + #include #include #include #include -#include +#include class Logger; class QUdpSocket; @@ -44,6 +46,9 @@ class SSDPDiscover : public QObject public: SSDPDiscover(QObject* parent = nullptr); + ~SSDPDiscover() override; + + /// /// /// @brief Search for specified service, results will be returned by signal newService(). Calling this method again will reset all found urns and search again @@ -188,7 +193,8 @@ private slots: private: - Logger* _log; + QSharedPointer _log; + QUdpSocket* _udpSocket; QHostAddress _ssdpAddr; quint16 _ssdpPort; diff --git a/include/ssdp/SSDPServer.h b/include/ssdp/SSDPServer.h index 96c75d7c2..0c2a1ff40 100644 --- a/include/ssdp/SSDPServer.h +++ b/include/ssdp/SSDPServer.h @@ -103,7 +103,7 @@ public slots: virtual QString getInfo() const { return {}; }; - Logger *_log; + QSharedPointer _log; private: diff --git a/include/utils/FileUtils.h b/include/utils/FileUtils.h index 7bf5db8b4..f3d562290 100644 --- a/include/utils/FileUtils.h +++ b/include/utils/FileUtils.h @@ -16,7 +16,7 @@ namespace FileUtils { /// /// @brief remove directory recursive given by path /// @param[in] path Path to directory - bool removeDir(const QString& path, Logger* log); + bool removeDir(const QString& path, QSharedPointer log); /// /// @brief check if the file exists @@ -25,7 +25,7 @@ namespace FileUtils { /// @param[in] ignError Ignore errors during file read (no log output) /// @return true on success else false /// - bool fileExists(const QString& path, Logger* log, bool ignError=false); + bool fileExists(const QString& path, QSharedPointer log, bool ignError=false); /// /// @brief read a file given by path. @@ -35,7 +35,7 @@ namespace FileUtils { /// @param[in] ignError Ignore errors during file read (no log output) /// @return true on success else false /// - bool readFile(const QString& path, QString& data, Logger* log, bool ignError=false); + bool readFile(const QString& path, QString& data, QSharedPointer log, bool ignError=false); /// /// write a file given by path. @@ -44,7 +44,7 @@ namespace FileUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false /// - bool writeFile(const QString& path, const QByteArray& data, Logger* log); + bool writeFile(const QString& path, const QByteArray& data, QSharedPointer log); /// /// @brief delete a file by given path @@ -53,12 +53,12 @@ namespace FileUtils { /// @param[in] ignError Ignore errors during file delete (no log output) /// @return true on success else false /// - bool removeFile(const QString& path, Logger* log, bool ignError=false); + bool removeFile(const QString& path, QSharedPointer log, bool ignError=false); /// /// @brief resolve the file error and print a message /// @param[in] file The file which caused the error /// @param[in] log The logger of the caller /// - void resolveFileError(const QFile& file, Logger* log); + void resolveFileError(const QFile& file, QSharedPointer log); } diff --git a/include/utils/JsonUtils.h b/include/utils/JsonUtils.h index 9629525a6..3cc4cc169 100644 --- a/include/utils/JsonUtils.h +++ b/include/utils/JsonUtils.h @@ -17,8 +17,8 @@ namespace JsonUtils { /// @param[in] ignError Ignore errors during file read (no log output) /// @return true on success else false /// - QPair readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError=false); - QPair readFile(const QString& path, QJsonValue& obj, Logger* log, bool ignError=false); + QPair readFile(const QString& path, QJsonObject& obj, QSharedPointer log, bool ignError=false); + QPair readFile(const QString& path, QJsonValue& obj, QSharedPointer log, bool ignError=false); /// /// @brief read a schema file and resolve $refs @@ -27,7 +27,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false /// - bool readSchema(const QString& path, QJsonObject& obj, Logger* log); + bool readSchema(const QString& path, QJsonObject& obj, QSharedPointer log); /// /// @brief parse a JSON QString and get a QJsonObject. Overloaded funtion @@ -37,8 +37,8 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false /// - QPair parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log); - QPair parse(const QString& path, const QString& data, QJsonValue& value, Logger* log); + QPair parse(const QString& path, const QString& data, QJsonObject& obj, QSharedPointer log); + QPair parse(const QString& path, const QString& data, QJsonValue& value, QSharedPointer log); /// /// @brief parse a JSON QString and get a QJsonArray. Overloaded function @@ -48,7 +48,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false // - QPair parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log); + QPair parse(const QString& path, const QString& data, QJsonArray& arr, QSharedPointer log); /// /// @brief parse a JSON QString and get a QJsonDocument @@ -58,7 +58,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false /// - QPair parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log); + QPair parse(const QString& path, const QString& data, QJsonDocument& doc, QSharedPointer log); /// /// @brief Validate JSON data against a schema @@ -68,7 +68,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false, plus validation errors /// - QPair validate(const QString& file, const QJsonValue& json, const QString& schemaPath, Logger* log); + QPair validate(const QString& file, const QJsonValue& json, const QString& schemaPath, QSharedPointer log); /// /// @brief Validate JSON data against a schema @@ -78,7 +78,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false, plus validation errors /// - QPair validate(const QString& file, const QJsonValue& json, const QJsonObject& schema, Logger* log); + QPair validate(const QString& file, const QJsonValue& json, const QJsonObject& schema, QSharedPointer log); /// /// @brief Validate JSON data against a schema @@ -88,7 +88,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false, plus correction messages /// - QPair correct(const QString& file, QJsonValue& json, const QJsonObject& schema, Logger* log); + QPair correct(const QString& file, QJsonValue& json, const QJsonObject& schema, QSharedPointer log); /// /// @brief Write JSON data to file @@ -97,7 +97,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false /// - bool write(const QString& filename, const QJsonObject& json, Logger* log); + bool write(const QString& filename, const QJsonObject& json, QSharedPointer log); /// /// @brief resolve schema $ref attribute @@ -106,7 +106,7 @@ namespace JsonUtils { /// @param[in] log The logger of the caller to print errors /// @return true on success else false /// - bool resolveRefs(const QJsonObject& schema, QJsonObject& obj, Logger* log); + bool resolveRefs(const QJsonObject& schema, QJsonObject& obj, QSharedPointer log); /// diff --git a/include/utils/Logger.h b/include/utils/Logger.h index 49a3fe107..350b6fa3f 100644 --- a/include/utils/Logger.h +++ b/include/utils/Logger.h @@ -1,37 +1,39 @@ #pragma once -// QT includes +#ifdef _WIN32 + #include +#endif + #include -#include -#include -#include -#include +#include #include #include +#include +#include +#include +#include #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - #include + #include #else - #include -#endif - -// stl includes -#ifdef _WIN32 - -#include + #include #endif #include +#include + +// Forward declaration +class LoggerManager; // ================================================================ #define LOG_MESSAGE(severity, logger, ...) (logger)->Message(severity, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) // standard log messages -#define Debug(logger, ...) LOG_MESSAGE(Logger::LOG_DEBUG , logger, __VA_ARGS__) -#define Info(logger, ...) LOG_MESSAGE(Logger::LOG_INFO , logger, __VA_ARGS__) -#define Warning(logger, ...) LOG_MESSAGE(Logger::LOG_WARNING, logger, __VA_ARGS__) -#define Error(logger, ...) LOG_MESSAGE(Logger::LOG_ERROR , logger, __VA_ARGS__) +#define Debug(logger, ...) LOG_MESSAGE(Logger::LogLevel::Debug , logger, __VA_ARGS__) +#define Info(logger, ...) LOG_MESSAGE(Logger::LogLevel::Info , logger, __VA_ARGS__) +#define Warning(logger, ...) LOG_MESSAGE(Logger::LogLevel::Warning, logger, __VA_ARGS__) +#define Error(logger, ...) LOG_MESSAGE(Logger::LogLevel::Error , logger, __VA_ARGS__) // conditional log messages #define DebugIf(condition, logger, ...) if (condition) Debug(logger, __VA_ARGS__) @@ -43,20 +45,24 @@ class Logger : public QObject { - Q_OBJECT + Q_OBJECT + // Grant friendship to the memory tracking function + template + friend QSharedPointer makeTrackedShared(Creator creator, Args&&... args); public: - - enum LogLevel { - LOG_UNSET = 0, - LOG_DEBUG = 1, - LOG_INFO = 2, - LOG_WARNING = 3, - LOG_ERROR = 4, - LOG_OFF = 5 - }; - - struct T_LOG_MESSAGE + enum class LogLevel + { + Unset = 0, + Debug, + Info, + Warning, + Error, + Off + }; + Q_ENUM(LogLevel) + + struct T_LOG_MESSAGE { QString loggerName; QString loggerSubName; @@ -67,12 +73,26 @@ class Logger : public QObject QString message; LogLevel level; QString levelString; - }; - - static Logger* getInstance(const QString & name = "", const QString & subName = "__", LogLevel minLevel= Logger::LogLevel::LOG_INFO); - static void deleteInstance(const QString & name = "", const QString & subName = "__"); - static void setLogLevel(LogLevel level, const QString & name = "", const QString & subName = "__"); - static LogLevel getLogLevel(const QString & name = "", const QString & subName = "__"); + }; + + /** + * Get a Logger instance for a given name + * @param name The name of the logger + * @param subName The sub name of the logger + * @param minLevel The minimum level of this logger + * @return The logger + */ + static QSharedPointer getInstance(const QString& name = "", const QString& subName = "__", LogLevel minLevel = LogLevel::Info); + + /** + * Delete a Logger instance for a given name + * @param name The name of the logger + * @param subName The sub name of the logger + */ + + static void deleteInstance(const QString & name = "", const QString & subName = "__"); + static void setLogLevel(LogLevel level, const QString & name = "", const QString & subName = "__"); + static LogLevel getLogLevel(const QString & name = "", const QString & subName = "__"); void Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...); void setMinLevel(LogLevel level) { _minLevel = static_cast(level); } @@ -84,7 +104,7 @@ class Logger : public QObject void newLogMessage(Logger::T_LOG_MESSAGE); protected: - Logger(const QString & name="", const QString & subName = "__", LogLevel minLevel = Logger::LogLevel::LOG_INFO); + explicit Logger(const QString & name="", const QString & subName = "__", LogLevel minLevel = LogLevel::Info); ~Logger() override; private: @@ -95,23 +115,27 @@ class Logger : public QObject #else static QMutex MapLock; #endif - static QMap LoggerMap; - static QAtomicInteger GLOBAL_MIN_LOG_LEVEL; + static QMap> LoggerMap; + static QAtomicInteger GLOBAL_MIN_LOG_LEVEL; - const QString _name; - const QString _subName; - const bool _syslogEnabled; - const unsigned _loggerId; + const QString _name; + const QString _subName; + const bool _syslogEnabled; + const unsigned _loggerId; /* Only non-const member, hence the atomic */ QAtomicInteger _minLevel; }; +/** + * @brief The LoggerManager class + * Handles the management of multiple loggers and their configuration + */ class LoggerManager : public QObject { - Q_OBJECT - -private: + Q_OBJECT + + private: // Run LoggerManager as singleton LoggerManager(); LoggerManager(const LoggerManager&) = delete; @@ -123,11 +147,26 @@ class LoggerManager : public QObject public: ~LoggerManager() override; - static QScopedPointer& getInstance(); + + /** + * @brief Get the singleton logger manager instance + */ + static QScopedPointer& getInstance(); public slots: - void handleNewLogMessage(const Logger::T_LOG_MESSAGE&); - QJsonArray getLogMessageBuffer(Logger::LogLevel filter= Logger::LogLevel::LOG_UNSET) const; + /** + * Handle a new log message + * @param msg The log message + */ + void handleNewLogMessage(const Logger::T_LOG_MESSAGE&); + + /** + * @brief Get the message buffer of all loggers + * @param filter A filter to apply on the buffer + * @return The buffer as a QJsonArray + */ + QJsonArray getLogMessageBuffer(Logger::LogLevel filter= Logger::LogLevel::Unset) const; + signals: void newLogMessage(const Logger::T_LOG_MESSAGE&); diff --git a/include/utils/MemoryTracker.h b/include/utils/MemoryTracker.h new file mode 100644 index 000000000..947b23695 --- /dev/null +++ b/include/utils/MemoryTracker.h @@ -0,0 +1,129 @@ +#ifndef MEMORYTRACKER_H +#define MEMORYTRACKER_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(memory_objects_create) +Q_DECLARE_LOGGING_CATEGORY(memory_objects_track) +Q_DECLARE_LOGGING_CATEGORY(memory_objects_destroy) +Q_DECLARE_LOGGING_CATEGORY(memory_non_objects_create) +Q_DECLARE_LOGGING_CATEGORY(memory_non_objects_destroy) + +#define MAKE_TRACKED_SHARED(T, ...) makeTrackedShared(this, __VA_ARGS__) +#define MAKE_TRACKED_SHARED_STATIC(T, ...) makeTrackedShared(nullptr, __VA_ARGS__) +#define TRACK_SCOPE qCDebug(memory_objects_track).noquote() << QString("%1()...").arg( __func__) +#define TRACK_SCOPE_SUBCOMPONENT qCDebug(memory_objects_track).noquote() << QString("|%1| %2()...").arg(_log->getSubName(), __func__) + +template +void objectDeleter(T* ptr, const QString& subComponent, const QString& typeName) +{ + if (!ptr) + { + return; + } + + if constexpr (std::is_base_of_v) + { + QThread const* thread = ptr->thread(); + if (thread && thread == QThread::currentThread()) { + qCDebug(memory_objects_destroy).noquote() << QString("|%1| QObject<%2> scheduled for deletion.").arg(subComponent, typeName); + ptr->deleteLater(); + } + else + { + if (thread && thread->isRunning()) + { + // Schedule deleteLater from the object's thread + qCDebug(memory_objects_destroy).noquote() << QString("|%1| QObject<%2>::deleteLater() scheduled via invokeMethod on thread '%3'").arg(subComponent, typeName).arg(thread->objectName()); + QMetaObject::invokeMethod(ptr, "deleteLater", Qt::QueuedConnection); + } + else + { + // The object's thread is not running, which is a potential issue. + // Log a critical error as this indicates a problem in the shutdown sequence. + // This should be an *extremely rare* fallback and indicates a bug in the thread shutdown sequence. + qCritical().noquote() << QString("|%1| QObject<%2> could not be deleted. Owning thread is not running. This may cause a memory leak. Please check thread shutdown sequence and object ownership.") + .arg(subComponent, typeName); + + // [MEMORYTRACKER] CRITICAL: This code path indicates a potential memory leak, as the QObject cannot be safely deleted. + // To aid debugging, ensure that all threads are properly shut down before object destruction. + // Consider adding more diagnostics here, such as emitting a signal or logging additional context (e.g., stack trace, object address). + //qCDebug(memory_objects).noquote() << QString("|%1| QObject<%2> object's owning thread is not running. Deleted immediately (thread not running)").arg(subComponent, typeName); + //direct delete will require friendship, to be added (to Logger) + //delete ptr; + } + } + } + else + { + qCDebug(memory_objects_destroy).noquote() << QString("|%1| Non-QObject<%2> deleted immediately.").arg(subComponent, typeName); + delete ptr; + } +} + +// Factory function template to create tracked QSharedPointer +template +QSharedPointer makeTrackedShared(Creator creator, Args&&... args) +{ + auto* rawPtr = new T(std::forward(args)...); + if (!rawPtr) + { + return QSharedPointer(); + } + + QString subComponent = "__"; + QString typeName; + if constexpr (std::is_base_of_v) + { + typeName = rawPtr->metaObject()->className(); + } + else + { + typeName = typeid(T).name(); + } + + QString creatorName = "non-QObject"; + if constexpr (std::is_pointer_v && std::is_base_of_v>) + { + if (creator != nullptr) + { + creatorName = creator->metaObject()->className(); + + QVariant prop = creator->property("instance"); + if (prop.isValid() && prop.canConvert()) + { + subComponent = prop.toString(); + } + } + } + + if (creator != nullptr) + { + qCDebug(memory_objects_create).noquote() << QString("|%1| Creating object of type '%2' at %3 by '%4'") + .arg(subComponent, typeName, QString("0x%1").arg(reinterpret_cast(rawPtr), QT_POINTER_SIZE * 2, 16, QChar('0')), creatorName); + } + else + { + qCDebug(memory_non_objects_create).noquote() << QString("|%1| Creating object of type '%2' at %3 by '%4'") + .arg(subComponent, typeName, QString("0x%1").arg(reinterpret_cast(rawPtr), QT_POINTER_SIZE * 2, 16, QChar('0')), creatorName); + } + + auto deleter = [subComponent, typeName](T* ptr) { + objectDeleter(ptr, subComponent, typeName); + }; + + return QSharedPointer(rawPtr, deleter); +} + +#endif // MEMORYTRACKER_H diff --git a/include/utils/NetOrigin.h b/include/utils/NetOrigin.h index 9d88a724f..ab6f0734e 100644 --- a/include/utils/NetOrigin.h +++ b/include/utils/NetOrigin.h @@ -16,7 +16,7 @@ class NetOrigin : public QObject Q_OBJECT private: friend class HyperionDaemon; - NetOrigin(QObject* parent = nullptr, Logger* log = Logger::getInstance("NETWORK")); + NetOrigin(QObject* parent = nullptr, QSharedPointer log = Logger::getInstance("NETWORK")); public: /// @@ -29,5 +29,5 @@ class NetOrigin : public QObject static NetOrigin *instance; private: - Logger* _log; + QSharedPointer _log; }; diff --git a/include/utils/NetUtils.h b/include/utils/NetUtils.h index ac958fceb..a3a2f63e5 100644 --- a/include/utils/NetUtils.h +++ b/include/utils/NetUtils.h @@ -26,7 +26,7 @@ const int MAX_PORT = 65535; /// @param log The logger of the caller to print /// @return True on success else false /// -inline bool portAvailable(quint16& port, Logger* log) +inline bool portAvailable(quint16& port, QSharedPointer log) { const quint16 prevPort = port; QTcpServer server; @@ -51,7 +51,7 @@ inline bool portAvailable(quint16& port, Logger* log) /// @param[in] host A hostname/IP-address to make reference to during logging /// @return True on success else false /// -inline bool isValidPort(Logger* log, int port, const QString& host) +inline bool isValidPort(QSharedPointer log, int port, const QString& host) { if ((port <= 0 || port > MAX_PORT) && port != -1) { @@ -106,7 +106,7 @@ inline bool resolveHostPort(const QString& address, QString& host, int& port) /// @param[out] hostAddress The resolved IP-Address /// @return True on success else false /// -inline bool resolveMDnsHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress) +inline bool resolveMDnsHostToAddress(QSharedPointer log, const QString& hostname, QHostAddress& hostAddress) { bool isHostAddressOK{ false }; if (!hostname.isEmpty()) @@ -126,7 +126,7 @@ inline bool resolveMDnsHostToAddress(Logger* log, const QString& hostname, QHost // Call the function asynchronously in MdnsBrowser's thread QMetaObject::invokeMethod(browser, "resolveFirstAddress", Qt::QueuedConnection, // Ensures it runs in the correct thread - Q_ARG(Logger*, log), + Q_ARG(QSharedPointer, log), Q_ARG(QString, hostname) ); @@ -215,7 +215,7 @@ inline QMdnsEngine::Record resolveMDnsServiceRecord(const QByteArray& serviceIns /// @param[in/out] port The port provided by the mDNS service, if not mDNS the input port is returned /// @return True on success else false /// -inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress, int& port) +inline bool resolveHostToAddress(QSharedPointer log, const QString& hostname, QHostAddress& hostAddress, int& port) { bool areHostAddressPartOK{ false }; QString target{ hostname }; @@ -276,7 +276,7 @@ inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddr /// @param[in/out] port The port provided by the mDNS service, if not mDNS the input port is returned /// @return True on success else false /// -inline bool resolveMdnsHost(Logger* log, QString& hostname, int& port) +inline bool resolveMdnsHost(QSharedPointer log, QString& hostname, int& port) { bool isResolved{ true }; @@ -328,7 +328,7 @@ inline bool resolveMdnsHost(Logger* log, QString& hostname, int& port) return isResolved; } -inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddress& hostAddress) +inline bool resolveHostToAddress(QSharedPointer log, const QString& hostname, QHostAddress& hostAddress) { int ignoredPort {MAX_PORT}; return resolveHostToAddress(log, hostname, hostAddress, ignoredPort); diff --git a/include/utils/PixelFormat.h b/include/utils/PixelFormat.h index 10eb9a1e7..db197145b 100644 --- a/include/utils/PixelFormat.h +++ b/include/utils/PixelFormat.h @@ -15,6 +15,8 @@ enum class PixelFormat { RGB32, BGR32, NV12, + NV21, + P030, I420, MJPEG, NO_CHANGE diff --git a/include/utils/Profiler.h b/include/utils/Profiler.h index e478385fb..7e2570609 100644 --- a/include/utils/Profiler.h +++ b/include/utils/Profiler.h @@ -42,7 +42,7 @@ class Profiler private: static void initLogger(); - static Logger* _logger; + static QSharedPointer _logger; const char* _file; const char* _func; unsigned int _line; diff --git a/include/utils/RgbChannelAdjustment.h b/include/utils/RgbChannelAdjustment.h index 7b40bf01a..8c343fe6e 100644 --- a/include/utils/RgbChannelAdjustment.h +++ b/include/utils/RgbChannelAdjustment.h @@ -70,7 +70,7 @@ class RgbChannelAdjustment QString _channelName; /// Logger instance - Logger * _log; + QSharedPointer _log; /// The adjustment of RGB channel ColorRgb _adjust; diff --git a/include/utils/TrackedMemory.h b/include/utils/TrackedMemory.h deleted file mode 100644 index e0fa804dd..000000000 --- a/include/utils/TrackedMemory.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef TRACKEDMEMORY_H -#define TRACKEDMEMORY_H - -#include -#include -#include -#include -#include -#include -#include - -#define ENABLE_MEMORY_TRACKING 1 - -#define USE_TRACKED_SHARED_PTR ENABLE_MEMORY_TRACKING -#define USE_TRACKED_CUSTOM_DELETE ENABLE_MEMORY_TRACKING - -#if USE_TRACKED_SHARED_PTR - #define MAKE_TRACKED_SHARED(T, ...) makeTrackedShared(__VA_ARGS__) -#else - #define MAKE_TRACKED_SHARED(T, ...) QSharedPointer(new T(__VA_ARGS__), &customDelete) -#endif - -// Custom Delete function templates - -template -void customDelete(T* ptr) -{ - if (!ptr) - return; - -#if USE_TRACKED_CUSTOM_DELETE - - QString subComponent = "__"; - QString typeName; - - if constexpr (std::is_base_of::value) - { - QVariant prop = ptr->property("instance"); - if (prop.isValid() && prop.canConvert()) - { - subComponent = prop.toString(); - } - typeName = ptr->metaObject()->className(); - } - else - { - typeName = typeid(T).name(); - } - - Logger* log = Logger::getInstance("MEMORY", subComponent); - Debug(log, "Deleting object of type '%s' at %p - current thread: '%s'", QSTRING_CSTR(typeName), static_cast(ptr), QSTRING_CSTR(QThread::currentThread()->objectName())); -#endif - - if constexpr (std::is_base_of::value) - { - QThread* thread = ptr->thread(); - if (thread && thread == QThread::currentThread()) { -#if USE_TRACKED_CUSTOM_DELETE - Debug(log, "QObject<%s> deleted immediately (current thread: '%s').", QSTRING_CSTR(typeName), QSTRING_CSTR(thread->objectName())); -#endif - ptr->deleteLater(); - } - else - { - if (thread && thread->isRunning()) - { - // Schedule deleteLater from the object's thread -#if USE_TRACKED_CUSTOM_DELETE - Debug(log, "QObject<%s>::deleteLater() scheduled via invokeMethod on thread '%s'", QSTRING_CSTR(typeName), QSTRING_CSTR(thread->objectName())); -#endif - QMetaObject::invokeMethod(ptr, "deleteLater", Qt::QueuedConnection); - } - else - { - // This should be an *extremely rare* fallback and indicates a bug in the thread shutdown sequence. -#if USE_TRACKED_CUSTOM_DELETE - Debug(log, "<%s> object's owning thread is not running. Deleted immediately (thread not running) - current thread: '%s'", QSTRING_CSTR(typeName), QSTRING_CSTR(QThread::currentThread()->objectName())); -#endif - delete ptr; - } - } - } - else - { -#if USE_TRACKED_CUSTOM_DELETE - Debug(log, "Non-QObject<%s> deleted immediately - current thread: '%s'.", QSTRING_CSTR(typeName), QSTRING_CSTR(QThread::currentThread()->objectName())); -#endif - delete ptr; - - } -} - -// Factory function template to create tracked QSharedPointer -template -QSharedPointer makeTrackedShared(Args&&... args) -{ - T* rawPtr = new T(std::forward(args)...); - if (!rawPtr) - return QSharedPointer(); - - QString subComponent = "__"; - QString typeName; - - if constexpr (std::is_base_of::value) - { - QVariant prop = rawPtr->property("instance"); - if (prop.isValid() && prop.canConvert()) - { - subComponent = prop.toString(); - } - typeName = rawPtr->metaObject()->className(); - } - else - { - typeName = typeid(T).name(); - } - - Logger* log = Logger::getInstance("MEMORY", subComponent); - Debug(log, "Creating object of type '%s' at %p (current thread '%s')", QSTRING_CSTR(typeName), static_cast(rawPtr), QSTRING_CSTR(QThread::currentThread()->objectName())); - - return QSharedPointer(rawPtr, &customDelete); -} - -#endif // TRACKEDMEMORY_H diff --git a/include/webserver/WebServer.h b/include/webserver/WebServer.h index defdd5ae8..f5fa09260 100644 --- a/include/webserver/WebServer.h +++ b/include/webserver/WebServer.h @@ -87,7 +87,7 @@ public slots: private: QJsonDocument _config; bool _useSsl; - Logger* _log; + QSharedPointer _log; QString _baseUrl; quint16 _port; StaticFileServing* _staticFileServing; diff --git a/libsrc/api/API.cpp b/libsrc/api/API.cpp index 38e3aef5f..ce983bc3a 100644 --- a/libsrc/api/API.cpp +++ b/libsrc/api/API.cpp @@ -38,7 +38,7 @@ const int IMAGE_WIDTH_MAX = 2000; const int IMAGE_SCALE = 2000; } -API::API(Logger *log, bool localConnection, QObject *parent) +API::API(QSharedPointer log, bool localConnection, QObject *parent) : QObject(parent), _currInstanceIndex (NO_INSTANCE_ID) , _hyperionWeak(nullptr) diff --git a/libsrc/api/CMakeLists.txt b/libsrc/api/CMakeLists.txt index 6818e7524..1759e22b1 100644 --- a/libsrc/api/CMakeLists.txt +++ b/libsrc/api/CMakeLists.txt @@ -13,12 +13,17 @@ add_library(hyperion-api ${CMAKE_SOURCE_DIR}/libsrc/api/JSONRPC_schemas.qrc ) +if(ENABLE_DX) + include_directories(${DIRECTX9_INCLUDE_DIRS}) + target_link_libraries(hyperion-api ${DIRECTX9_LIBRARIES}) +endif(ENABLE_DX) + +if(ENABLE_DRM) + include_directories(${LIBDRM_INCLUDE_DIRS}) + target_link_libraries(hyperion-api drm-grabber) +endif(ENABLE_DRM) + target_link_libraries(hyperion-api hyperion hyperion-utils - ${DIRECTX9_LIBRARIES} -) - -target_include_directories(hyperion-api PRIVATE - ${DIRECTX9_INCLUDE_DIRS} ) diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 3fc3e3e4b..f589cfa79 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -79,13 +79,14 @@ const char SETTINGS_UI_SCHEMA_FILE[] = ":/schema-settings-ui.json"; const bool verbose = false; } -JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject *parent, bool noListener) +JsonAPI::JsonAPI(QString peerAddress, QSharedPointer log, bool localConnection, QObject *parent, bool noListener) : API(log, localConnection, parent) ,_noListener(noListener) ,_peerAddress (std::move(peerAddress)) ,_jsonCB (nullptr) ,_isServiceAvailable(false) { + TRACK_SCOPE; Q_INIT_RESOURCE(JSONRPC_schemas); qRegisterMetaType("Event"); @@ -101,6 +102,11 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject _jsonCB = QSharedPointer(new JsonCallbacks( _log, _peerAddress, parent)); } +JsonAPI::~JsonAPI() +{ + TRACK_SCOPE; +} + QSharedPointer JsonAPI::getCallBack() const { return _jsonCB; diff --git a/libsrc/api/JsonCallbacks.cpp b/libsrc/api/JsonCallbacks.cpp index a6c7ce06a..43ed9ddbf 100644 --- a/libsrc/api/JsonCallbacks.cpp +++ b/libsrc/api/JsonCallbacks.cpp @@ -19,7 +19,7 @@ using namespace hyperion; -JsonCallbacks::JsonCallbacks(Logger *log, const QString& peerAddress, QObject* parent) +JsonCallbacks::JsonCallbacks(QSharedPointer log, const QString& peerAddress, QObject* parent) : QObject(parent) , _log (log) , _hyperionWeak(nullptr) @@ -28,11 +28,17 @@ JsonCallbacks::JsonCallbacks(Logger *log, const QString& peerAddress, QObject* p , _prioMuxerWeak(nullptr) , _islogMsgStreamingActive(false) { + TRACK_SCOPE; qRegisterMetaType("InputsMap"); connect(HyperionIManager::getInstance(), &HyperionIManager::instanceStateChanged, this, &JsonCallbacks::handleInstanceStateChange); } +JsonCallbacks::~JsonCallbacks() +{ + TRACK_SCOPE; +} + void JsonCallbacks::handleInstanceStateChange(InstanceState state, quint8 instanceID, const QString& /*name */) { switch (state) diff --git a/libsrc/api/JsonInfo.cpp b/libsrc/api/JsonInfo.cpp index 190d50690..8daba54ca 100644 --- a/libsrc/api/JsonInfo.cpp +++ b/libsrc/api/JsonInfo.cpp @@ -20,7 +20,7 @@ #include #endif -QJsonObject JsonInfo::getInfo(const QSharedPointer& hyperionInstance, Logger* log) +QJsonObject JsonInfo::getInfo(const QSharedPointer& hyperionInstance, QSharedPointer log) { QJsonObject info {}; @@ -63,7 +63,7 @@ QJsonObject JsonInfo::getInfo(const QSharedPointer& hyperionInstance, return info; } -QJsonArray JsonInfo::getAdjustmentInfo(const QSharedPointer& hyperionInstance, Logger* log) +QJsonArray JsonInfo::getAdjustmentInfo(const QSharedPointer& hyperionInstance, QSharedPointer log) { if (hyperionInstance.isNull()) { @@ -711,16 +711,20 @@ QJsonArray JsonInfo::discoverScreenInputs(const QJsonObject& params) const { QJsonArray screenInputs; -#ifdef ENABLE_QT - discoverGrabber(screenInputs, params); +#ifdef ENABLE_DDA + discoverGrabber(screenInputs, params); #endif -#ifdef ENABLE_DX - discoverGrabber(screenInputs, params); +#if defined(ENABLE_DRM) && !defined(ENABLE_AMLOGIC) + discoverGrabber(screenInputs, params); #endif -#ifdef ENABLE_DDA - discoverGrabber(screenInputs, params); +#ifdef ENABLE_OSX + discoverGrabber(screenInputs, params); +#endif + +#ifdef ENABLE_QT + discoverGrabber(screenInputs, params); #endif #ifdef ENABLE_X11 @@ -743,8 +747,8 @@ QJsonArray JsonInfo::discoverScreenInputs(const QJsonObject& params) const discoverGrabber(screenInputs, params); #endif -#ifdef ENABLE_OSX - discoverGrabber(screenInputs, params); +#ifdef ENABLE_DX + discoverGrabber(screenInputs, params); #endif return screenInputs; diff --git a/libsrc/blackborder/BlackBorderProcessor.cpp b/libsrc/blackborder/BlackBorderProcessor.cpp index e34b73413..7b65cf468 100644 --- a/libsrc/blackborder/BlackBorderProcessor.cpp +++ b/libsrc/blackborder/BlackBorderProcessor.cpp @@ -33,6 +33,11 @@ BlackBorderProcessor::BlackBorderProcessor(const QSharedPointer& hyper { subComponent = hyperion->property("instance").toString(); _log = Logger::getInstance("BLACKBORDER", subComponent); + } + TRACK_SCOPE_SUBCOMPONENT; + + if (hyperion) + { // init handleSettingsUpdate(settings::BLACKBORDER, _hyperionWeak.toStrongRef()->getSetting(settings::BLACKBORDER)); @@ -43,6 +48,7 @@ BlackBorderProcessor::BlackBorderProcessor(const QSharedPointer& hyper // listen for component state changes connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest); } + _detector = std::make_unique(_oldThreshold); } diff --git a/libsrc/boblightserver/BoblightClientConnection.h b/libsrc/boblightserver/BoblightClientConnection.h index 8a83b5cd7..b5bac71f4 100644 --- a/libsrc/boblightserver/BoblightClientConnection.h +++ b/libsrc/boblightserver/BoblightClientConnection.h @@ -140,7 +140,7 @@ private slots: std::vector _ledColors; /// logger instance - Logger * _log; + QSharedPointer _log; /// address of client QString _clientAddress; diff --git a/libsrc/boblightserver/BoblightServer.cpp b/libsrc/boblightserver/BoblightServer.cpp index 9f6e05f23..0bf989763 100644 --- a/libsrc/boblightserver/BoblightServer.cpp +++ b/libsrc/boblightserver/BoblightServer.cpp @@ -35,7 +35,7 @@ BoblightServer::BoblightServer(const QSharedPointer& hyperionInstance, connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &BoblightServer::compStateChangeRequest); } _log = Logger::getInstance("BOBLIGHT", subComponent); - + TRACK_SCOPE_SUBCOMPONENT; Debug(_log, "Instance created"); @@ -48,7 +48,7 @@ BoblightServer::BoblightServer(const QSharedPointer& hyperionInstance, BoblightServer::~BoblightServer() { - qDebug() << "BoblightServer::~BoblightServer()..."; + TRACK_SCOPE_SUBCOMPONENT; } void BoblightServer::start() diff --git a/libsrc/cec/CECHandler.cpp b/libsrc/cec/CECHandler.cpp index f7fd5835a..e1455299a 100644 --- a/libsrc/cec/CECHandler.cpp +++ b/libsrc/cec/CECHandler.cpp @@ -26,6 +26,7 @@ CECHandler::CECHandler(const QJsonDocument& config, QObject * parent) , _doubleTapTimeoutMs(CEC_DOUBLE_TAP_TIMEOUT_MS) , _cecEventActionMap() { + TRACK_SCOPE; qRegisterMetaType("Event"); _logger = Logger::getInstance("EVENTS-CEC"); @@ -36,6 +37,11 @@ CECHandler::CECHandler(const QJsonDocument& config, QObject * parent) _cecConfig.callbackParam = this; } +CECHandler::~CECHandler() +{ + TRACK_SCOPE; +} + void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { if(type == settings::CECEVENTS) diff --git a/libsrc/db/DBManager.cpp b/libsrc/db/DBManager.cpp index 23b5f3411..dcdf8fef4 100644 --- a/libsrc/db/DBManager.cpp +++ b/libsrc/db/DBManager.cpp @@ -34,6 +34,12 @@ DBManager::DBManager(QObject* parent) : QObject(parent) , _log(Logger::getInstance("DB")) { + TRACK_SCOPE; +} + +DBManager::~DBManager() +{ + TRACK_SCOPE; } void DBManager::initializeDatabase(const QDir& dataDirectory, bool isReadOnly) diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index 8ccbb6d78..ef30519f7 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -46,6 +46,7 @@ Effect::Effect(const QSharedPointer& hyperionInstance, int priority, i } _log = Logger::getInstance("EFFECTENGINE", subComponent); + TRACK_SCOPE_SUBCOMPONENT; _colors.fill(ColorRgb::BLACK); @@ -59,7 +60,7 @@ Effect::Effect(const QSharedPointer& hyperionInstance, int priority, i Effect::~Effect() { - qDebug() << "Effect::~Effect()..."; + TRACK_SCOPE_SUBCOMPONENT; delete _painter; _imageStack.clear(); } diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index 29a7bdff2..dd356cbe2 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -31,7 +31,7 @@ EffectEngine::EffectEngine(const QSharedPointer& hyperionInstance) subComponent = hyperion->property("instance").toString(); } _log = Logger::getInstance("EFFECTENGINE", subComponent); - + TRACK_SCOPE_SUBCOMPONENT; Q_INIT_RESOURCE(EffectEngine); qRegisterMetaType("hyperion::Components"); @@ -68,7 +68,7 @@ EffectEngine::EffectEngine(const QSharedPointer& hyperionInstance) EffectEngine::~EffectEngine() { - qDebug() << "EffectEngine::~EffectEngine()..."; + TRACK_SCOPE_SUBCOMPONENT; } std::list EffectEngine::getActiveEffects() const diff --git a/libsrc/events/EventHandler.cpp b/libsrc/events/EventHandler.cpp index f0d70a48f..698a5fe1f 100644 --- a/libsrc/events/EventHandler.cpp +++ b/libsrc/events/EventHandler.cpp @@ -13,6 +13,7 @@ EventHandler::EventHandler() : _isSuspended(false) , _isIdle(false) { + TRACK_SCOPE; qRegisterMetaType("Event"); _log = Logger::getInstance("EVENTS"); @@ -22,6 +23,7 @@ EventHandler::EventHandler() EventHandler::~EventHandler() { + TRACK_SCOPE; QObject::disconnect(this, &EventHandler::signalEvent, HyperionIManager::getInstance(), &HyperionIManager::handleEvent); } diff --git a/libsrc/events/EventScheduler.cpp b/libsrc/events/EventScheduler.cpp index 2a748f684..e0e23f118 100644 --- a/libsrc/events/EventScheduler.cpp +++ b/libsrc/events/EventScheduler.cpp @@ -12,6 +12,7 @@ EventScheduler::EventScheduler() : _isEnabled(false) { + TRACK_SCOPE; qRegisterMetaType("Event"); _log = Logger::getInstance("EVENTS-SCHED"); @@ -22,6 +23,7 @@ EventScheduler::EventScheduler() EventScheduler::~EventScheduler() { + TRACK_SCOPE; QObject::disconnect(this, &EventScheduler::signalEvent, EventHandler::getInstance().data(), &EventHandler::handleEvent); clearTimers(); Info(_log, "Hyperion event scheduler stopped"); diff --git a/libsrc/events/OsEventHandler.cpp b/libsrc/events/OsEventHandler.cpp index 188ac1844..376a307fc 100644 --- a/libsrc/events/OsEventHandler.cpp +++ b/libsrc/events/OsEventHandler.cpp @@ -31,6 +31,7 @@ OsEventHandlerBase::OsEventHandlerBase() , _isLockRegistered(false) , _isService(false) { + TRACK_SCOPE; qRegisterMetaType("Event"); _log = Logger::getInstance("EVENTS-OS"); @@ -46,6 +47,7 @@ OsEventHandlerBase::OsEventHandlerBase() OsEventHandlerBase::~OsEventHandlerBase() { + TRACK_SCOPE; quit(); QObject::disconnect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance().data(), &EventHandler::handleEvent); diff --git a/libsrc/flatbufserver/FlatBufferClient.h b/libsrc/flatbufserver/FlatBufferClient.h index 4a8886d53..06de39210 100644 --- a/libsrc/flatbufserver/FlatBufferClient.h +++ b/libsrc/flatbufserver/FlatBufferClient.h @@ -147,7 +147,7 @@ private slots: void processNV12Image(const uint8_t* nv12_data, int32_t width, int32_t height, int32_t stride_y, const ImageResampler& resampler, Image& outputImage); private: - Logger * _log; + QSharedPointer _log; QTcpSocket * _socket; QString _origin; const QString _clientAddress; diff --git a/libsrc/flatbufserver/FlatBufferServer.cpp b/libsrc/flatbufserver/FlatBufferServer.cpp index a0caf6222..a1a8f83ef 100644 --- a/libsrc/flatbufserver/FlatBufferServer.cpp +++ b/libsrc/flatbufserver/FlatBufferServer.cpp @@ -25,10 +25,12 @@ FlatBufferServer::FlatBufferServer(const QJsonDocument& config, QObject* parent) , _timeout(5000) , _config(config) { + TRACK_SCOPE; } FlatBufferServer::~FlatBufferServer() { + TRACK_SCOPE; } void FlatBufferServer::initServer() diff --git a/libsrc/forwarder/MessageForwarder.cpp b/libsrc/forwarder/MessageForwarder.cpp index 61d8191d5..fde8f721f 100644 --- a/libsrc/forwarder/MessageForwarder.cpp +++ b/libsrc/forwarder/MessageForwarder.cpp @@ -49,12 +49,13 @@ MessageForwarder::MessageForwarder(const QJsonDocument& config) , _muxerWeak(nullptr) , _messageForwarderFlatBufHelper(nullptr) { + TRACK_SCOPE; qRegisterMetaType("TargetHost"); } MessageForwarder::~MessageForwarder() { - qDebug() << "MessageForwarder::~MessageForwarder()..."; + TRACK_SCOPE; } void MessageForwarder::init() diff --git a/libsrc/grabber/CMakeLists.txt b/libsrc/grabber/CMakeLists.txt index daf0bc578..a14dc795c 100644 --- a/libsrc/grabber/CMakeLists.txt +++ b/libsrc/grabber/CMakeLists.txt @@ -6,10 +6,26 @@ if(ENABLE_DISPMANX) add_subdirectory(dispmanx) endif (ENABLE_DISPMANX) -if(ENABLE_FB) +if(ENABLE_DDA) + add_subdirectory(dda) +endif(ENABLE_DDA) + +if(ENABLE_DRM) + add_subdirectory(drm) +endif(ENABLE_DRM) + +if(ENABLE_DX) + add_subdirectory(directx) +endif(ENABLE_DX) + +if (ENABLE_FB) add_subdirectory(framebuffer) endif (ENABLE_FB) +if(ENABLE_QT) + add_subdirectory(qt) +endif(ENABLE_QT) + if(ENABLE_OSX) add_subdirectory(osx) endif(ENABLE_OSX) @@ -26,18 +42,6 @@ if(ENABLE_XCB) add_subdirectory(xcb) endif(ENABLE_XCB) -if(ENABLE_QT) - add_subdirectory(qt) -endif(ENABLE_QT) - -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() diff --git a/libsrc/grabber/amlogic/AmlogicGrabber.cpp b/libsrc/grabber/amlogic/AmlogicGrabber.cpp index 299cafd0a..b70dbcf03 100644 --- a/libsrc/grabber/amlogic/AmlogicGrabber.cpp +++ b/libsrc/grabber/amlogic/AmlogicGrabber.cpp @@ -1,9 +1,9 @@ -// STL includes +#include + #include #include #include -// Linux includes #include #include #include @@ -11,40 +11,54 @@ #include #include -// qt +#include // Required for dlopen, dlsym, dlclose + +#include "Amvideocap.h" + #include #include #include #include +#include #include // Local includes #include -#include -#include "Amvideocap.h" // Constants -namespace { -const bool verbose = false; +namespace +{ + const bool verbose = false; -const int DEFAULT_FB_DEVICE_IDX = 0; -const char DEFAULT_VIDEO_DEVICE[] = "/dev/amvideo"; -const char DEFAULT_CAPTURE_DEVICE[] = "/dev/amvideocap0"; -const int AMVIDEOCAP_WAIT_MAX_MS = 40; -const int AMVIDEOCAP_DEFAULT_RATE_HZ = 25; + const int DEFAULT_DEVICE_IDX = 0; + const char DEFAULT_VIDEO_DEVICE[] = "/dev/amvideo"; + const char DEFAULT_CAPTURE_DEVICE[] = "/dev/amvideocap0"; + const int AMVIDEOCAP_WAIT_MAX_MS = 40; + const int AMVIDEOCAP_DEFAULT_RATE_HZ = 25; -} //End of constants +} // End of constants -AmlogicGrabber::AmlogicGrabber() +AmlogicGrabber::AmlogicGrabber(int deviceIdx) : Grabber("GRABBER-AMLOGIC") // Minimum required width or height is 160 , _captureDev(-1) , _videoDev(-1) , _lastError(0) - , _fbGrabber(DEFAULT_FB_DEVICE_IDX) + , _screenGrabber(nullptr) , _grabbingModeNotification(0) { _image_ptr = _image_bgr.memptr(); _useImageResampler = true; + + if (isGbmSupported(verbose)) + { + Debug(_log, "System supports DRM/GBM, using DRMFrameGrabber for screen capture."); + _screenGrabber.reset(new DRMFrameGrabber(deviceIdx)); + } + else + { + Debug(_log, "DRM/GBM not supported, falling back to Framebuffer for screen capture."); + _screenGrabber.reset(new FramebufferFrameGrabber(deviceIdx)); + } } AmlogicGrabber::~AmlogicGrabber() @@ -53,36 +67,77 @@ AmlogicGrabber::~AmlogicGrabber() closeDevice(_videoDev); } +bool AmlogicGrabber::isGbmSupported(bool logMsg) const +{ + // Check for the existence of gbm_create_device, a core GBM function, within libdrm.so + + QString libName = "libMali"; + QString lib = libName + ".so"; + + // 1. Attempt to open the library. + // RTLD_LAZY resolves symbols only when they are needed. + void *handle = dlopen( QSTRING_CSTR(lib), RTLD_LAZY); + + if (!handle) + { + WarningIf(logMsg, _log, "Could not check if DRM/GBM is supported. Error: %s", dlerror()); + DebugIf(logMsg, _log, "This could mean the GBM driver is not installed or not in the library path."); + return false; + } + + // 2. Look for a specific GBM function symbol. + // We're not calling the function, just checking if its address exists. + // 'gbm_create_device' is a fundamental function in the GBM API. + const void *symbol = dlsym(handle, "gbm_create_device"); + + // 3. Close the library handle. + dlclose(handle); + + if (symbol != nullptr) + { + DebugIf(logMsg, _log, "Found 'gbm_create_device' in %s.so.", QSTRING_CSTR(libName)); + InfoIf(logMsg, _log, "System likely supports DRM/GBM. Found 'gbm_create_device' in %s.so.", QSTRING_CSTR(libName)); + return true; + } + + DebugIf(logMsg, _log, "'gbm_create_device' not found in %s.so.", QSTRING_CSTR(libName)); + InfoIf(logMsg, _log, "System likely does not support DRM/GBM."); + + return false; +} + +// ToDo Check, currently not used bool AmlogicGrabber::setupScreen() { - bool rc (false); + QSize screenSize = _screenGrabber->getScreenSize(); + if (screenSize.isEmpty()) + { + return false; + } - QSize screenSize = _fbGrabber.getScreenSize(); - if ( !screenSize.isEmpty() ) + if (!setWidthHeight(screenSize.width(), screenSize.height())) { - if (setWidthHeight(screenSize.width(), screenSize.height())) - { - rc = _fbGrabber.setupScreen(); - } + return false; } - return rc; + + return _screenGrabber->setupScreen(); } -bool AmlogicGrabber::openDevice(int &fd, const char* dev) +bool AmlogicGrabber::openDevice(int &fd, const char *dev) const { - bool rc = true; - if (fd<0) + if (fd < 0) { fd = ::open(dev, O_RDWR); - if ( fd < 0) + if (fd < 0) { - rc = false; + return false; } } - return rc; + + return true; } -void AmlogicGrabber::closeDevice(int &fd) +void AmlogicGrabber::closeDevice(int &fd) const { if (fd >= 0) { @@ -93,82 +148,82 @@ void AmlogicGrabber::closeDevice(int &fd) bool AmlogicGrabber::isVideoPlaying() { - bool rc = false; - if(QFile::exists(DEFAULT_VIDEO_DEVICE)) + if (!QFile::exists(DEFAULT_VIDEO_DEVICE)) { - int videoDisabled = 1; - if (!openDevice(_videoDev, DEFAULT_VIDEO_DEVICE)) - { - Error(_log, "Failed to open video device(%s): %d - %s", DEFAULT_VIDEO_DEVICE, errno, strerror(errno)); - } - else - { - // Check the video disabled flag - if(ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0) - { - Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno)); - closeDevice(_videoDev); - } - else - { - if ( videoDisabled == 0 ) - { - rc = true; - } - } - } + return false; + } + + int videoDisabled {1}; + if (!openDevice(_videoDev, DEFAULT_VIDEO_DEVICE)) + { + Error(_log, "Failed to open video device(%s): %d - %s", DEFAULT_VIDEO_DEVICE, errno, strerror(errno)); + return false; + } + // Check the video disabled flag + if (ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0) + { + Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno)); + closeDevice(_videoDev); + return false; + } + + if (videoDisabled == 0) + { + return true; } - return rc; + + return false; } -int AmlogicGrabber::grabFrame(Image & image) +int AmlogicGrabber::grabFrame(Image &image) { - int rc = 0; - if (_isEnabled && !_isDeviceInError) + if (!_isEnabled || _isDeviceInError) + { + return -1; + } + + // Make sure video is playing, else there is nothing to grab + if (isVideoPlaying()) { - // Make sure video is playing, else there is nothing to grab - if (isVideoPlaying()) + if (_grabbingModeNotification != 1) { - if (_grabbingModeNotification!=1) - { - Info(_log, "Switch to VPU capture mode"); - _grabbingModeNotification = 1; - _lastError = 0; - } - - if (grabFrame_amvideocap(image) < 0) { - closeDevice(_captureDev); - rc = -1; - } + Info(_log, "Switch to VPU capture mode"); + _grabbingModeNotification = 1; + _lastError = 0; + return -1; // Skip the first frame after mode switch } - else + + if (grabFrame_amvideocap(image) < 0) { - if (_grabbingModeNotification!=2) - { - Info( _log, "Switch to Framebuffer capture mode"); - _grabbingModeNotification = 2; - _lastError = 0; - } - rc = _fbGrabber.grabFrame(image); + closeDevice(_captureDev); + return -1; } + + return 0; } - return rc; + + if (_grabbingModeNotification != 2) + { + Info(_log, "Switch to Framebuffer capture mode"); + _grabbingModeNotification = 2; + _lastError = 0; + return -1; // Skip the first frame after mode switch + } + + return _screenGrabber->grabFrame(image); } -int AmlogicGrabber::grabFrame_amvideocap(Image & image) +int AmlogicGrabber::grabFrame_amvideocap(Image &image) { - int rc = 0; - // If the device is not open, attempt to open it if (_captureDev < 0) { - if (! openDevice(_captureDev, DEFAULT_CAPTURE_DEVICE)) + if (!openDevice(_captureDev, DEFAULT_CAPTURE_DEVICE)) { - ErrorIf( _lastError != 1, _log,"Failed to open the AMLOGIC device (%d - %s):", errno, strerror(errno)); + ErrorIf(_lastError != 1, _log, "Failed to open the AMLOGIC device (%d - %s):", errno, strerror(errno)); _lastError = 1; - rc = -1; - return rc; + return -1; } } @@ -177,159 +232,112 @@ int AmlogicGrabber::grabFrame_amvideocap(Image & image) long r3 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_AT_FLAGS, CAP_FLAG_AT_END); long r4 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WAIT_MAX_MS, AMVIDEOCAP_WAIT_MAX_MS); - if (r1<0 || r2<0 || r3<0 || r4<0 || _height==0 || _width==0) + if (r1 < 0 || r2 < 0 || r3 < 0 || r4 < 0 || _height == 0 || _width == 0) { - ErrorIf(_lastError != 2,_log,"Failed to configure capture device (%d - %s)", errno, strerror(errno)); + ErrorIf(_lastError != 2, _log, "Failed to configure capture device (%d - %s)", errno, strerror(errno)); _lastError = 2; - rc = -1; - } - else - { - int linelen = ((_width + 31) & ~31) * 3; - size_t _bytesToRead = linelen * _height; - - // Read the snapshot into the memory - ssize_t bytesRead = pread(_captureDev, _image_ptr, _bytesToRead, 0); - - if ( bytesRead < 0 && !EAGAIN && errno > 0 ) - { - ErrorIf(_lastError != 3, _log,"Capture frame failed failed - Retrying. Error [%d] - %s", errno, strerror(errno)); - _lastError = 3; - rc = -1; - } - else - { - if (bytesRead != -1 && static_cast(_bytesToRead) != bytesRead) - { - // Read of snapshot failed - ErrorIf(_lastError != 4, _log,"Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", _bytesToRead, bytesRead); - _lastError = 4; - rc = -1; - } - else { - //If bytesRead = -1 but no error or EAGAIN or ENODATA, return last image to cover video pausing scenario - // EAGAIN : // 11 - Resource temporarily unavailable - // ENODATA: // 61 - No data available - _imageResampler.processImage(static_cast(_image_ptr), - _width, - _height, - linelen, - PixelFormat::BGR24, image); - _lastError = 0; - rc = 0; - } - } + return -1; } - return rc; -} -QJsonObject AmlogicGrabber::discover(const QJsonObject& params) -{ - DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); + int linelen = ((_width + 31) & ~31) * 3; + auto bytesToRead = linelen * _height; - QJsonObject inputsDiscovered; + // Read the snapshot into the memory + auto bytesRead = pread(_captureDev, _image_ptr, bytesToRead, 0); - if(QFile::exists(DEFAULT_VIDEO_DEVICE) && QFile::exists(DEFAULT_CAPTURE_DEVICE) ) + if (bytesRead < 0 && !EAGAIN && errno > 0) { - QJsonArray video_inputs; - - QSize screenSize = _fbGrabber.getScreenSize(); - if ( !screenSize.isEmpty() ) - { - int fbIdx = _fbGrabber.getPath().right(1).toInt(); - - DebugIf(verbose, _log, "FB device [%s] found with resolution: %dx%d", QSTRING_CSTR(_fbGrabber.getPath()), screenSize.width(), screenSize.height()); - QJsonArray fps = { 1, 5, 10, 15, 20, 25, 30}; - - QJsonObject in; - - QString displayName; - displayName = QString("Display%1").arg(fbIdx); - - in["name"] = displayName; - in["inputIdx"] = fbIdx; - - QJsonArray formats; - QJsonObject format; - - QJsonArray resolutionArray; - - QJsonObject resolution; - - resolution["width"] = screenSize.width(); - resolution["height"] = screenSize.height(); - resolution["fps"] = fps; - - resolutionArray.append(resolution); - - format["resolutions"] = resolutionArray; - formats.append(format); - - in["formats"] = formats; - video_inputs.append(in); - } - - if (!video_inputs.isEmpty()) - { - inputsDiscovered["device"] = "amlogic"; - inputsDiscovered["device_name"] = "AmLogic"; - inputsDiscovered["type"] = "screen"; - inputsDiscovered["video_inputs"] = video_inputs; - - QJsonObject defaults, video_inputs_default, resolution_default; - resolution_default["fps"] = AMVIDEOCAP_DEFAULT_RATE_HZ; - video_inputs_default["resolution"] = resolution_default; - video_inputs_default["inputIdx"] = 0; - defaults["video_input"] = video_inputs_default; - inputsDiscovered["default"] = defaults; - } + ErrorIf(_lastError != 3, _log, "Capture frame failed failed - Retrying. Error [%d] - %s", errno, strerror(errno)); + _lastError = 3; + return -1; } - if (inputsDiscovered.isEmpty()) + if (bytesRead != -1 && bytesToRead != bytesRead) { - DebugIf(verbose, _log, "No displays found to capture from!"); + // Read of snapshot failed + ErrorIf(_lastError != 4, _log, "Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", bytesToRead, bytesRead); + _lastError = 4; + return -1; } - DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); - - return inputsDiscovered; + // If bytesRead = -1 but no error or EAGAIN or ENODATA, return last image to cover video pausing scenario + // EAGAIN : // 11 - Resource temporarily unavailable + // ENODATA: // 61 - No data available + _imageResampler.processImage(reinterpret_cast(_image_ptr), + _width, + _height, + linelen, + PixelFormat::BGR24, image); + _lastError = 0; + return 0; } void AmlogicGrabber::setVideoMode(VideoMode mode) { Grabber::setVideoMode(mode); - _fbGrabber.setVideoMode(mode); + _screenGrabber->setVideoMode(mode); } bool AmlogicGrabber::setPixelDecimation(int pixelDecimation) { - return ( Grabber::setPixelDecimation( pixelDecimation) && - _fbGrabber.setPixelDecimation( pixelDecimation)); + return (Grabber::setPixelDecimation(pixelDecimation) && + _screenGrabber->setPixelDecimation(pixelDecimation)); } void AmlogicGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) { Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); - _fbGrabber.setCropping(cropLeft, cropRight, cropTop, cropBottom); + _screenGrabber->setCropping(cropLeft, cropRight, cropTop, cropBottom); } bool AmlogicGrabber::setWidthHeight(int width, int height) { - bool rc (false); - if ( Grabber::setWidthHeight(width, height) ) + if (!Grabber::setWidthHeight(width, height)) { - _image_bgr.resize(static_cast(width), static_cast(height)); - _width = width; - _height = height; - _bytesToRead = _image_bgr.size(); - _image_ptr = _image_bgr.memptr(); - rc = _fbGrabber.setWidthHeight(width, height); + return false; } - return rc; + + _image_bgr.resize(static_cast(width), static_cast(height)); + _width = width; + _image_ptr = _image_bgr.memptr(); + + return _screenGrabber->setWidthHeight(width, height); } bool AmlogicGrabber::setFramerate(int fps) { - return (Grabber::setFramerate(fps) && - _fbGrabber.setFramerate(fps)); + return (_screenGrabber->setFramerate(fps)); +} + +QJsonObject AmlogicGrabber::discover(const QJsonObject ¶ms) +{ + DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); + + QJsonObject inputsDiscovered; + + QJsonArray const video_inputs = _screenGrabber->getInputDeviceDetails(); + if (video_inputs.isEmpty()) + { + DebugIf(verbose, _log, "No displays found to capture from!"); + return {}; + } + + inputsDiscovered["device"] = "amlogic"; + inputsDiscovered["device_name"] = "AmLogic"; + inputsDiscovered["type"] = "screen"; + inputsDiscovered["video_inputs"] = video_inputs; + + QJsonObject defaults; + QJsonObject video_inputs_default; + QJsonObject resolution_default; + + resolution_default["fps"] = AMVIDEOCAP_DEFAULT_RATE_HZ; + video_inputs_default["resolution"] = resolution_default; + video_inputs_default["inputIdx"] = 0; + defaults["video_input"] = video_inputs_default; + inputsDiscovered["default"] = defaults; + + DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); + + return inputsDiscovered; } diff --git a/libsrc/grabber/amlogic/AmlogicWrapper.cpp b/libsrc/grabber/amlogic/AmlogicWrapper.cpp index 6c3b302c5..85233bed7 100644 --- a/libsrc/grabber/amlogic/AmlogicWrapper.cpp +++ b/libsrc/grabber/amlogic/AmlogicWrapper.cpp @@ -1,17 +1,20 @@ #include -AmlogicWrapper::AmlogicWrapper(int updateRate_Hz, int pixelDecimation) +AmlogicWrapper::AmlogicWrapper(int updateRate_Hz, + int deviceIdx, + int pixelDecimation) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) - , _grabber() + , _grabber(deviceIdx) { _grabber.setPixelDecimation(pixelDecimation); } -AmlogicWrapper::AmlogicWrapper(const QJsonDocument& grabberConfig) +AmlogicWrapper::AmlogicWrapper(const QJsonDocument &grabberConfig) : AmlogicWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + grabberConfig["input"].toInt(0), GrabberWrapper::DEFAULT_PIXELDECIMATION) { - this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); + handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } void AmlogicWrapper::action() diff --git a/libsrc/grabber/amlogic/CMakeLists.txt b/libsrc/grabber/amlogic/CMakeLists.txt index d59066d0d..551205c26 100644 --- a/libsrc/grabber/amlogic/CMakeLists.txt +++ b/libsrc/grabber/amlogic/CMakeLists.txt @@ -12,4 +12,5 @@ add_library(amlogic-grabber target_link_libraries(amlogic-grabber hyperion + drm-grabber ) diff --git a/libsrc/grabber/audio/AudioGrabber.cpp b/libsrc/grabber/audio/AudioGrabber.cpp index 1e2be84dd..9c3b9b1d9 100644 --- a/libsrc/grabber/audio/AudioGrabber.cpp +++ b/libsrc/grabber/audio/AudioGrabber.cpp @@ -187,7 +187,7 @@ void AudioGrabber::processAudioFrame(int16_t* buffer, int length) emit newFrame(finalImage); } -Logger* AudioGrabber::getLog() +QSharedPointer AudioGrabber::getLog() { return _log; } diff --git a/libsrc/grabber/drm/CMakeLists.txt b/libsrc/grabber/drm/CMakeLists.txt new file mode 100644 index 000000000..473aa3c4e --- /dev/null +++ b/libsrc/grabber/drm/CMakeLists.txt @@ -0,0 +1,19 @@ +find_package(LibDRM 2.4.101 REQUIRED) + +add_library(drm-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/drm/DRMFrameGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/drm/DRMWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/drm/DRMFrameGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/drm/DRMWrapper.cpp +) + +target_include_directories(drm-grabber + PUBLIC + ${LIBDRM_INCLUDE_DIRS} +) + +target_link_libraries(drm-grabber + hyperion + ${LIBDRM_LIBRARIES} +) + diff --git a/libsrc/grabber/drm/DRMFrameGrabber.cpp b/libsrc/grabber/drm/DRMFrameGrabber.cpp new file mode 100644 index 000000000..de981ee9d --- /dev/null +++ b/libsrc/grabber/drm/DRMFrameGrabber.cpp @@ -0,0 +1,1113 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(grabber_drm, "grabber.screen.drm") + + +// Add missing AMD format modifier definitions for downward compatibility +#ifndef AMD_FMT_MOD_TILE_VER_GFX11 +#define AMD_FMT_MOD_TILE_VER_GFX11 4 +#endif +#ifndef AMD_FMT_MOD_TILE_VER_GFX12 +#define AMD_FMT_MOD_TILE_VER_GFX12 5 +#endif + + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#define ALIGN(v, a) (((v) + (a) - 1) & ~((a) - 1)) + +static QString getDrmFormat(uint32_t format) +{ + return QString::fromLatin1(reinterpret_cast(&format), sizeof format); +} + +static QString getDrmModifierName(uint64_t modifier) +{ + uint64_t vendor = modifier >> 56; + uint64_t mod = modifier & 0x00FFFFFFFFFFFFFF; + QString name = QString("VENDOR: 0x%1, MOD: 0x%2").arg(vendor, 2, 16, QChar('0')).arg(mod, 14, 16, QChar('0')); + + switch (modifier) + { + case DRM_FORMAT_MOD_INVALID: + return "DRM_FORMAT_MOD_INVALID"; + case DRM_FORMAT_MOD_LINEAR: + return "DRM_FORMAT_MOD_LINEAR"; + default: + break; + } + + switch (vendor) + { + case DRM_FORMAT_MOD_VENDOR_INTEL: + switch (mod) + { + case I915_FORMAT_MOD_X_TILED: + return "I915_FORMAT_MOD_X_TILED"; + case I915_FORMAT_MOD_Y_TILED: + return "I915_FORMAT_MOD_Y_TILED"; + case I915_FORMAT_MOD_Yf_TILED: + return "I915_FORMAT_MOD_Yf_TILED"; + case I915_FORMAT_MOD_Y_TILED_CCS: + return "I915_FORMAT_MOD_Y_TILED_CCS"; + case I915_FORMAT_MOD_Yf_TILED_CCS: + return "I915_FORMAT_MOD_Yf_TILED_CCS"; + default: + return QString("DRM_FORMAT_MOD_INTEL_UNKNOWN [0x%1]").arg(mod, 14, 16, QChar('0')); + } + case DRM_FORMAT_MOD_VENDOR_AMD: + if (mod & AMD_FMT_MOD_TILE_VER_GFX9) + return "AMD_FMT_MOD_TILE_VER_GFX9"; + if (mod & AMD_FMT_MOD_TILE_VER_GFX10) + return "AMD_FMT_MOD_TILE_VER_GFX10"; + if (mod & AMD_FMT_MOD_TILE_VER_GFX11) + return "AMD_FMT_MOD_TILE_VER_GFX11"; + if (mod & AMD_FMT_MOD_TILE_VER_GFX12) + return "AMD_FMT_MOD_TILE_VER_GFX12"; + if (mod & AMD_FMT_MOD_DCC_BLOCK_128B) + return "AMD_FMT_MOD_DCC_BLOCK_128B"; + if (mod & AMD_FMT_MOD_DCC_BLOCK_256B) + return "AMD_FMT_MOD_DCC_BLOCK_256B"; + return QString("DRM_FORMAT_MOD_AMD_UNKNOWN [0x%1]").arg(mod, 14, 16, QChar('0')); + case DRM_FORMAT_MOD_VENDOR_NVIDIA: + if (mod & 0x10) + return "DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D"; + return QString("DRM_FORMAT_MOD_NVIDIA_UNKNOWN [0x%1]").arg(mod , 14, 16, QChar('0')); + case DRM_FORMAT_MOD_VENDOR_BROADCOM: + switch (fourcc_mod_broadcom_mod(modifier)) + { + case DRM_FORMAT_MOD_BROADCOM_SAND32: + return "DRM_FORMAT_MOD_BROADCOM_SAND32"; + case DRM_FORMAT_MOD_BROADCOM_SAND64: + return "DRM_FORMAT_MOD_BROADCOM_SAND64"; + case DRM_FORMAT_MOD_BROADCOM_SAND128: + return "DRM_FORMAT_MOD_BROADCOM_SAND128"; + case DRM_FORMAT_MOD_BROADCOM_SAND256: + return "DRM_FORMAT_MOD_BROADCOM_SAND256"; + case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: + return "DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED"; + default: + return QString("DRM_FORMAT_MOD_BROADCOM_UNKNOWN [0x%1]").arg(mod, 14, 16, QChar('0')); + } + case DRM_FORMAT_MOD_VENDOR_ARM: + if ((modifier & DRM_FORMAT_MOD_ARM_AFBC(0)) == DRM_FORMAT_MOD_ARM_AFBC(0)) + return "DRM_FORMAT_MOD_ARM_AFBC"; + return QString("DRM_FORMAT_MOD_ARM_UNKNOWN [0x%1]").arg(mod, 14, 16, QChar('0')); + + default: + break; + } + + return name; +} + +static PixelFormat GetPixelFormatForDrmFormat(uint32_t format) +{ + switch (format) + { + case DRM_FORMAT_XRGB8888: return PixelFormat::BGR32; + case DRM_FORMAT_ARGB8888: return PixelFormat::BGR32; + case DRM_FORMAT_XBGR8888: return PixelFormat::RGB32; + case DRM_FORMAT_ABGR8888: return PixelFormat::RGB32; + case DRM_FORMAT_NV12: return PixelFormat::NV12; +#ifdef DRM_FORMAT_NV21 + case DRM_FORMAT_NV21: return PixelFormat::NV21; +#endif +#ifdef DRM_FORMAT_P030 + case DRM_FORMAT_P030: return PixelFormat::P030; +#endif + case DRM_FORMAT_YUV420: return PixelFormat::I420; + default: return PixelFormat::NO_CHANGE; + } +} + +// Code from: https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/blob/master/lib/igt_vc4.c#L339 +static size_t vc4_sand_tiled_offset(size_t column_width, size_t column_size, size_t x, size_t y, size_t bpp) +{ + size_t offset = 0; + size_t cols_x; + size_t pix_x; + + /* Offset to the beginning of the relevant column. */ + cols_x = x / column_width; + offset += cols_x * column_size; + + /* Offset to the relevant pixel. */ + pix_x = x % column_width; + offset += (column_width * y + pix_x) * bpp / 8; + + return offset; +} + +// Forward declarations for QDebug operators +QDebug operator<<(QDebug dbg, const drmModeFB2* fb); +QDebug operator<<(QDebug dbg, const drmModePlane* plane); + +DRMFrameGrabber::DRMFrameGrabber(int deviceIdx, int cropLeft, int cropRight, int cropTop, int cropBottom) + : Grabber("GRABBER-DRM", cropLeft, cropRight, cropTop, cropBottom), _deviceFd(-1), _crtc(nullptr) +{ + _input = deviceIdx; + _useImageResampler = true; +} + +DRMFrameGrabber::~DRMFrameGrabber() +{ + freeResources(); + closeDevice(); +} + +void DRMFrameGrabber::freeResources() +{ + _connectors.clear(); + _encoders.clear(); + + if (_crtc != nullptr) + { + drmModeFreeCrtc(_crtc); + _crtc = nullptr; + } + + for (auto const &[id, plane] : _planes) + { + if (plane != nullptr) + { + drmModeFreePlane(plane); + } + } + _planes.clear(); + + for (auto const &[id, framebuffer] : _framebuffers) + { + drmModeFreeFB2(framebuffer); + } + _framebuffers.clear(); +} + +bool DRMFrameGrabber::setupScreen() +{ + freeResources(); + closeDevice(); + + bool success = openDevice() && getScreenInfo(); + setEnabled(success); + + if (!success) + { + freeResources(); + closeDevice(); + } + + return success; +} + +bool DRMFrameGrabber::setWidthHeight(int width, int height) +{ + if (Grabber::setWidthHeight(width, height)) + { + return setupScreen(); + } + + return false; +} + +struct LinearFramebufferParams +{ + int deviceFd; + const drmModeFB2* framebuffer; + int w; + int h; + PixelFormat pixelFormat; + const ImageResampler& imageResampler; + QSharedPointer log; + Image& image; +}; + +static bool processLinearFramebuffer(const LinearFramebufferParams& params) +{ + int size = 0; + int lineLength = 0; + int fb_dmafd = 0; + + if (params.pixelFormat == PixelFormat::I420 || params.pixelFormat == PixelFormat::NV12 +#ifdef DRM_FORMAT_NV21 + || params.pixelFormat == PixelFormat::NV21 +#endif + ) + { + size = (params.w * params.h * 3) / 2; + lineLength = params.w; + } +#ifdef DRM_FORMAT_P030 + else if (params.pixelFormat == PixelFormat::P030) + { + size = (params.w * params.h * 2) + (DIV_ROUND_UP(params.w, 2) * DIV_ROUND_UP(params.h, 2) * 4); // Y16 + UV32 per 2px + lineLength = params.w * 2; // 16bpp luma + } +#endif + else if (params.pixelFormat == PixelFormat::RGB32 || params.pixelFormat == PixelFormat::BGR32) + { + size = params.w * params.h * 4; + lineLength = params.w * 4; + } + + int ret = drmPrimeHandleToFD(params.deviceFd, params.framebuffer->handles[0], O_RDONLY, &fb_dmafd); + if (ret != 0) + { + Error(params.log, "drmPrimeHandleToFD failed (handle=%u): %s", params.framebuffer->handles[0], strerror(errno)); + return false; + } + + auto* mmapFrameBuffer = (uint8_t*)mmap(nullptr, size, PROT_READ, MAP_SHARED, fb_dmafd, 0); + if (mmapFrameBuffer != MAP_FAILED) + { + params.imageResampler.processImage(mmapFrameBuffer, params.w, params.h, lineLength, params.pixelFormat, params.image); + munmap(mmapFrameBuffer, size); + close(fb_dmafd); + return true; + } + + Error(params.log, "Format: %s failed. Error: %s", QSTRING_CSTR(getDrmFormat(params.framebuffer->pixel_format)), strerror(errno)); + close(fb_dmafd); + return false; +} + +// --- Broadcom SAND helpers (format-agnostic dispatcher) --- + +struct PlaneInfo +{ + int bpp; // bits per pixel on this plane (8, 16, 32) + int width; // plane width in pixels + int height; // plane height in pixels + int stride; // destination packed bytes per line + size_t srcBaseOffset; // source base offset (fb->offsets[idx]) + size_t dstBaseOffset; // destination base offset in packed buffer + int fbPlaneIdx; // FB plane index (0..3) +}; + +static bool getBroadcomSandGeometry(uint64_t modifier, uint32_t& columnWidthBytes, uint32_t& columnHeight, QString& errorString) +{ + switch (fourcc_mod_broadcom_mod(modifier)) + { + case DRM_FORMAT_MOD_BROADCOM_SAND32: columnWidthBytes = 32; break; + case DRM_FORMAT_MOD_BROADCOM_SAND64: columnWidthBytes = 64; break; + case DRM_FORMAT_MOD_BROADCOM_SAND128: columnWidthBytes = 128; break; + case DRM_FORMAT_MOD_BROADCOM_SAND256: columnWidthBytes = 256; break; + case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: + errorString = "Broadcom modifier 'DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED' currently not supported"; + return false; + default: + errorString = "Unknown Broadcom modifier"; + return false; + } + + columnHeight = fourcc_mod_broadcom_param(modifier); + return true; +} + +static bool getPlanesForFormat(PixelFormat fmt, + int w, int h, + uint32_t /*columnWidthBytes*/, + const drmModeFB2* fb, + std::vector& planes, + uint32_t& totalSize, + QString& errorString) +{ + planes.clear(); + totalSize = 0; + + switch (fmt) + { + case PixelFormat::NV12: + { + // Pack tightly: Y then interleaved UV + auto const y_stride = (uint32_t)w; + auto const uv_stride = (uint32_t)w; // interleaved, one byte per pixel on luma grid + + PlaneInfo y{}; + y.bpp = 8; + y.width = w; + y.height = h; + y.stride = (int)y_stride; + y.fbPlaneIdx = 0; + y.srcBaseOffset = fb->offsets[y.fbPlaneIdx]; + y.dstBaseOffset = 0; + planes.push_back(y); + + PlaneInfo uv{}; + uv.bpp = 16; // two 8-bit chroma bytes per 2 luma samples horizontally + uv.width = DIV_ROUND_UP(w, 2); + uv.height = DIV_ROUND_UP(h, 2); + uv.stride = (int)uv_stride; + uv.fbPlaneIdx = 1; + uv.srcBaseOffset = fb->offsets[uv.fbPlaneIdx]; + uv.dstBaseOffset = (size_t)w * (size_t)h; + planes.push_back(uv); + + totalSize = (uint32_t)((w * h) + (w * h) / 2); + return true; + } + +#ifdef DRM_FORMAT_NV21 + case PixelFormat::NV21: + { + // Identical plane sizes to NV12; chroma order is VU (handled by resampler) + auto const y_stride = (uint32_t)w; + auto const uv_stride = (uint32_t)w; + + PlaneInfo y{}; + y.bpp = 8; + y.width = w; + y.height = h; + y.stride = (int)y_stride; + y.fbPlaneIdx = 0; + y.srcBaseOffset = fb->offsets[y.fbPlaneIdx]; + y.dstBaseOffset = 0; + planes.push_back(y); + + PlaneInfo vu{}; + vu.bpp = 16; + vu.width = DIV_ROUND_UP(w, 2); + vu.height = DIV_ROUND_UP(h, 2); + vu.stride = (int)uv_stride; + vu.fbPlaneIdx = 1; + vu.srcBaseOffset = fb->offsets[vu.fbPlaneIdx]; + vu.dstBaseOffset = (size_t)w * (size_t)h; + planes.push_back(vu); + + totalSize = (uint32_t)((w * h) + (w * h) / 2); + return true; + } +#endif + +#ifdef DRM_FORMAT_P030 + case PixelFormat::P030: + { + // 10-bit semi-planar: Y in 16-bit words, UV packed 2x10b per 2 luma pixels => 32b per pair + auto const y_stride = (uint32_t)(w * 2); + auto const uv_stride = (uint32_t)(w * 2); // (w/2)*4 bytes == 2*w bytes + + PlaneInfo y{}; + y.bpp = 16; + y.width = w; + y.height = h; + y.stride = (int)y_stride; + y.fbPlaneIdx = 0; + y.srcBaseOffset = fb->offsets[y.fbPlaneIdx]; + y.dstBaseOffset = 0; + planes.push_back(y); + + PlaneInfo uv{}; + uv.bpp = 32; + uv.width = DIV_ROUND_UP(w, 2); + uv.height = DIV_ROUND_UP(h, 2); + uv.stride = (int)uv_stride; + uv.fbPlaneIdx = 1; + uv.srcBaseOffset = fb->offsets[uv.fbPlaneIdx]; + uv.dstBaseOffset = (size_t)w * (size_t)h * 2; + planes.push_back(uv); + + totalSize = (uint32_t)((size_t)w * (size_t)h * 3); // Y(2*w*h) + UV(w*h) + return true; + } +#endif + + default: + errorString = QString("Broadcom SAND: unsupported PixelFormat %1") + .arg(QSTRING_CSTR(pixelFormatToString(fmt))); + return false; + } +} + +static inline void copyBroadcomSandPlane(const PlaneInfo& p, + int lumaWidth, + uint32_t columnWidthBytes, + uint32_t columnHeight, + const uint8_t* src, + uint8_t* dst, + Logger* log) +{ + const uint32_t columnSize = columnWidthBytes * columnHeight; + // Keep proportional column width logic relative to luma width + const size_t columnWidthInPixels = (size_t)(columnWidthBytes) * (size_t)p.width / std::max(1, lumaWidth); + + for (int i = 0; i < p.height; i++) + { + for (int j = 0; j < p.width; j++) + { + size_t src_offset = p.srcBaseOffset; + size_t dst_offset = p.dstBaseOffset; + + src_offset += vc4_sand_tiled_offset(columnWidthInPixels, columnSize, (size_t)j, (size_t)i, (size_t)p.bpp); + dst_offset += (size_t)p.stride * (size_t)i + (size_t)j * (size_t)p.bpp / 8u; + + switch (p.bpp) + { + case 8: + *(dst + dst_offset) = *(src + src_offset); + break; + case 16: + *(uint16_t*)(dst + dst_offset) = *(const uint16_t*)(src + src_offset); + break; + case 32: + *(uint32_t*)(dst + dst_offset) = *(const uint32_t*)(src + src_offset); + break; + default: + Error(log, "Unsupported bpp %d in Broadcom SAND layout", p.bpp); + break; + } + } + } +} + +static bool untileBroadcomSandToLinear(int deviceFd, + const drmModeFB2* fb, + int w, + int h, + PixelFormat fmt, + const std::vector& planes, + uint32_t totalSize, + ImageResampler const& imageResampler, + Logger* log, + Image& image) +{ + int fb_dmafd = 0; + int ret = drmPrimeHandleToFD(deviceFd, fb->handles[0], O_RDONLY, &fb_dmafd); + if (ret < 0) + { + Error(log, "drmPrimeHandleToFD failed (broadcom handle=%u): %s", fb->handles[0], strerror(errno)); + return false; + } + + auto* src_buf = (uint8_t*)mmap(nullptr, totalSize, PROT_READ, MAP_SHARED, fb_dmafd, 0); + if (src_buf == MAP_FAILED) + { + close(fb_dmafd); + return false; + } + + std::vector dst_buf(totalSize); + uint8_t* dst_ptr = dst_buf.data(); + + uint32_t columnWidthBytes = 0; + uint32_t columnHeight = 0; + QString geomErr; + if (!getBroadcomSandGeometry(fb->modifier, columnWidthBytes, columnHeight, geomErr)) + { + munmap(src_buf, totalSize); + close(fb_dmafd); + return false; + } + + for (const auto& p : planes) + { + copyBroadcomSandPlane(p, w, columnWidthBytes, columnHeight, src_buf, dst_ptr, log); + } + + int lineLength = w; +#ifdef DRM_FORMAT_P030 + if (fmt == PixelFormat::P030) + lineLength = w * 2; +#endif + + imageResampler.processImage(dst_ptr, w, h, lineLength, fmt, image); + + munmap(src_buf, totalSize); + close(fb_dmafd); + return true; +} + +static bool processBroadcomSandFramebuffer(int deviceFd, + const drmModeFB2* framebuffer, + int w, + int h, + PixelFormat pixelFormat, + ImageResampler const& imageResampler, + Logger* log, + Image& image, + QString& errorString) +{ + uint32_t columnWidthBytes = 0; + uint32_t columnHeight = 0; + if (!getBroadcomSandGeometry(framebuffer->modifier, columnWidthBytes, columnHeight, errorString)) + { + return false; + } + + std::vector planes; + uint32_t totalSize = 0; + if (!getPlanesForFormat(pixelFormat, w, h, columnWidthBytes, framebuffer, planes, totalSize, errorString)) + { + return false; + } + + if (totalSize == 0) + { + Error(log, "Computed framebuffer size is 0 for Broadcom SAND layout"); + return false; + } + + return untileBroadcomSandToLinear(deviceFd, framebuffer, w, h, pixelFormat, planes, totalSize, imageResampler, log, image); +} + +int DRMFrameGrabber::grabFrame(Image &image) +{ + if (!_isEnabled || _isDeviceInError) + { + return -1; + } + + if (_framebuffers.empty()) + { + Error(_log, "No framebuffers found. Was setupScreen() successful?"); + return -1; + } + + bool newImage{false}; + QString errorString; + + // We only need to process the first framebuffer. + auto it = _framebuffers.begin(); + if (it != _framebuffers.end()) + { + const auto& [id, framebuffer] = *it; + + _pixelFormat = GetPixelFormatForDrmFormat(framebuffer->pixel_format); + uint64_t modifier = framebuffer->modifier; + + qCDebug(grabber_drm) << QString("Framebuffer ID: %1 - Width: %2 - Height: %3 - DRM Format: %4 - PixelFormat: %5, Modifier: %6") + .arg(id) + .arg(framebuffer->width) + .arg(framebuffer->height) + .arg(QSTRING_CSTR(getDrmFormat(framebuffer->pixel_format))) + .arg(QSTRING_CSTR(pixelFormatToString(_pixelFormat))) + .arg(QSTRING_CSTR(getDrmModifierName(modifier))); + + int w = framebuffer->width; + int h = framebuffer->height; + Grabber::setWidthHeight(w, h); + + // Linear modifier path + if (_pixelFormat != PixelFormat::NO_CHANGE && modifier == DRM_FORMAT_MOD_LINEAR) + { + LinearFramebufferParams params{ + _deviceFd, + framebuffer, + w, + h, + _pixelFormat, + _imageResampler, + _log, + image + }; + if (processLinearFramebuffer(params)) + { + newImage = true; + } + } + // Broadcom SAND path + else if ((modifier >> 56ULL) == DRM_FORMAT_MOD_VENDOR_BROADCOM) + { + if (processBroadcomSandFramebuffer(_deviceFd, framebuffer, w, h, _pixelFormat, _imageResampler, _log.data(), image, errorString)) + { + newImage = true; + } + } + else + { + errorString = QString("Currently unsupported format: %1 and/or modifier: %2") + .arg(getDrmFormat(framebuffer->pixel_format)) + .arg(getDrmModifierName(framebuffer->modifier)); + } + } + + if (!errorString.isEmpty()) + { + this->setInError(errorString); + return -1; + } + + if (!newImage) + { + qCDebug(grabber_drm) << "No image captured from DRM framebuffer."; + return -1; + } + + return 0; +} + +bool DRMFrameGrabber::openDevice() +{ + if (_deviceFd >= 0) + { + return true; + } + + if (!_isAvailable) + { + return false; + } + + // Try read-only first to minimize required privileges. Some drivers require O_RDWR; fallback in that case. + _deviceFd = ::open(QSTRING_CSTR(getDeviceName()), O_RDONLY | O_CLOEXEC); + if (_deviceFd < 0) + { + // fallback to read-write if required by driver + _deviceFd = ::open(QSTRING_CSTR(getDeviceName()), O_RDWR | O_CLOEXEC); + } + if (_deviceFd < 0) + { + QString errorReason = QString("Error opening %1, [%2] %3").arg(getDeviceName()).arg(errno).arg(std::strerror(errno)); + this->setInError(errorReason); + return false; + } + + return true; +} + +bool DRMFrameGrabber::closeDevice() +{ + if (_deviceFd < 0) + { + return true; + } + + bool success = (::close(_deviceFd) == 0); + _deviceFd = -1; + + return success; +} + +QSize DRMFrameGrabber::getScreenSize() const +{ + return getScreenSize(getDeviceName()); +} + +static QSize findActiveCrtcSize(int drmfd, const drmModeConnector* connector) +{ + if (!connector || !connector->encoder_id) + { + return QSize(); + } + + drmModeEncoderPtr encoder = drmModeGetEncoder(drmfd, connector->encoder_id); + if (!encoder) + { + return QSize(); + } + + QSize size; + if (encoder->crtc_id) + { + drmModeCrtcPtr crtc = drmModeGetCrtc(drmfd, encoder->crtc_id); + if (crtc) + { + size.setWidth(crtc->width); + size.setHeight(crtc->height); + drmModeFreeCrtc(crtc); + } + } + + drmModeFreeEncoder(encoder); + return size; +} + +QSize DRMFrameGrabber::getScreenSize(const QString &device) const +{ + DrmResources drmResources; + if (!discoverDrmResources(device, drmResources)) + { + return {}; + } + + // 3. Iterate through connectors to find a connected one + for (const auto& connector : drmResources.connectors) + { + if (connector->connection != DRM_MODE_CONNECTED || connector->count_modes <= 0) + { + continue; + } + + for (const auto& crtc : drmResources.crtcs) + { + if (crtc->mode_valid) + { + return QSize(crtc->width, crtc->height); + } + } + } + + return {}; +} + +bool DRMFrameGrabber::discoverDrmResources(const QString& device, DrmResources& resources) const +{ + // 1. Open the DRM device + int drmfd = ::open(QSTRING_CSTR(device), O_RDWR); + if (drmfd < 0) + { + return false; + } + + // 2. Get device resources + drmModeResPtr drmModeResources = drmModeGetResources(drmfd); + if (!drmModeResources) + { + ::close(drmfd); + return false; + } + + for (int i = 0; i < drmModeResources->count_connectors; ++i) + { + drmModeConnectorPtr connector = drmModeGetConnector(drmfd, drmModeResources->connectors[i]); + if (connector) + { + resources.connectors.emplace_back(connector, drmModeFreeConnector); + } + } + + for (int i = 0; i < drmModeResources->count_crtcs; ++i) + { + drmModeCrtcPtr crtc = drmModeGetCrtc(drmfd, drmModeResources->crtcs[i]); + if (crtc) + { + resources.crtcs.emplace_back(crtc, drmModeFreeCrtc); + } + } + + drmModeFreeResources(drmModeResources); + ::close(drmfd); + + return true; +} + +void DRMFrameGrabber::setDrmClientCaps() +{ + if (drmSetClientCap(_deviceFd, DRM_CLIENT_CAP_ATOMIC, 1) < 0) + { + Debug(_log, "drmSetClientCap(DRM_CLIENT_CAP_ATOMIC) failed: %s", strerror(errno)); + } + if (drmSetClientCap(_deviceFd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) + { + Debug(_log, "drmSetClientCap(DRM_CLIENT_CAP_UNIVERSAL_PLANES) failed: %s", strerror(errno)); + } +} + +void DRMFrameGrabber::enumerateConnectorsAndEncoders(const drmModeRes* resources) +{ + for (int i = 0; i < resources->count_connectors; i++) + { + auto connector = std::make_unique(); + connector->ptr = drmModeGetConnector(_deviceFd, resources->connectors[i]); + _connectors.insert_or_assign(resources->connectors[i], std::move(connector)); + } + + for (int i = 0; i < resources->count_encoders; i++) + { + auto encoder = std::make_unique(); + encoder->ptr = drmModeGetEncoder(_deviceFd, resources->encoders[i]); + _encoders.insert_or_assign(resources->encoders[i], std::move(encoder)); + } +} + +void DRMFrameGrabber::findActiveCrtc(const drmModeRes* resources) +{ + for (int i = 0; i < resources->count_crtcs; i++) + { + _crtc = drmModeGetCrtc(_deviceFd, resources->crtcs[i]); + if (_crtc && _crtc->mode_valid) + { + return; // Found active CRTC, so we can exit + } + drmModeFreeCrtc(_crtc); + _crtc = nullptr; + } +} + +bool DRMFrameGrabber::isPrimaryPlaneForCrtc(uint32_t planeId, const drmModeObjectProperties* properties) +{ + for (unsigned int j = 0; j < properties->count_props; ++j) + { + auto prop = drmModeGetProperty(_deviceFd, properties->props[j]); + if (!prop) continue; + + bool isPrimary = (strcmp(prop->name, "type") == 0 && properties->prop_values[j] == DRM_PLANE_TYPE_PRIMARY); + drmModeFreeProperty(prop); + + if (isPrimary) + { + auto plane = drmModeGetPlane(_deviceFd, planeId); + if (plane && _crtc && plane->crtc_id == _crtc->crtc_id) + { + qCDebug(grabber_drm) << plane; + _planes.insert({planeId, plane}); + return true; + } + if (plane) + { + drmModeFreePlane(plane); + } + } + } + return false; +} + +void DRMFrameGrabber::findPrimaryPlane(const drmModePlaneRes* planeResources) +{ + for (unsigned int i = 0; i < planeResources->count_planes; ++i) + { + uint32_t planeId = planeResources->planes[i]; + auto properties = drmModeObjectGetProperties(_deviceFd, planeId, DRM_MODE_OBJECT_PLANE); + if (!properties) continue; + + bool found = isPrimaryPlaneForCrtc(planeId, properties); + drmModeFreeObjectProperties(properties); + + if (found) + { + break; + } + } +} + +void DRMFrameGrabber::getDrmObjectProperties() const +{ + for (auto const &[id, connector] : _connectors) + { + auto properties = drmModeObjectGetProperties(_deviceFd, id, DRM_MODE_OBJECT_CONNECTOR); + if (!properties) continue; + for (unsigned int i = 0; i < properties->count_props; i++) + { + auto prop = drmModeGetProperty(_deviceFd, properties->props[i]); + connector->props.insert({std::string(prop->name), {.spec = prop, .value = properties->prop_values[i]}}); + } + drmModeFreeObjectProperties(properties); + } + + for (auto const &[id, encoder] : _encoders) + { + auto properties = drmModeObjectGetProperties(_deviceFd, id, DRM_MODE_OBJECT_ENCODER); + if (!properties) continue; + for (unsigned int i = 0; i < properties->count_props; i++) + { + auto prop = drmModeGetProperty(_deviceFd, properties->props[i]); + encoder->props.insert({std::string(prop->name), {.spec = prop, .value = properties->prop_values[i]}}); + } + drmModeFreeObjectProperties(properties); + } +} + +void DRMFrameGrabber::getFramebuffers() +{ + for (auto const &[id, plane] : _planes) + { + drmModeFB2Ptr fb = drmModeGetFB2(_deviceFd, plane->fb_id); + qCDebug(grabber_drm) << fb; + if (fb == nullptr) continue; + + if (fb->handles[0] == 0) + { + setInError("Not able to acquire framebuffer handles. Screen capture not possible. Check permissions."); + drmModeFreeFB2(fb); + continue; + } + _framebuffers.insert({plane->fb_id, fb}); + } +} + +bool DRMFrameGrabber::getScreenInfo() +{ + if (_deviceFd < 0) + { + this->setInError("DRM device not open"); + return false; + } + + setDrmClientCaps(); + + drmModeResPtr resources = drmModeGetResources(_deviceFd); + if (!resources) + { + this->setInError(QString("Unable to get DRM resources on %1").arg(getDeviceName())); + return false; + } + + enumerateConnectorsAndEncoders(resources); + findActiveCrtc(resources); + + drmModePlaneResPtr planeResources = drmModeGetPlaneResources(_deviceFd); + if (planeResources) + { + findPrimaryPlane(planeResources); + drmModeFreePlaneResources(planeResources); + } + else + { + Debug(_log, "drmModeGetPlaneResources returned NULL or failed: %s", strerror(errno)); + } + + getDrmObjectProperties(); + getFramebuffers(); + + drmModeFreeResources(resources); + + qCDebug(grabber_drm) << QString("Framebuffer count: %1").arg(_framebuffers.size()); + + return !_framebuffers.empty(); +} + +QJsonArray DRMFrameGrabber::getInputDeviceDetails() const +{ + // Find framebuffer devices 0-9 + QDir deviceDirectory(DRM_DIR_NAME); + QStringList deviceFilter(QString("%1%2").arg(DRM_PRIMARY_MINOR_NAME).arg('?')); + deviceDirectory.setNameFilters(deviceFilter); + deviceDirectory.setSorting(QDir::Name); + QFileInfoList deviceFiles = deviceDirectory.entryInfoList(QDir::System); + + QJsonArray video_inputs; + for (const auto &deviceFile : deviceFiles) + { + QString const fileName = deviceFile.fileName(); + int deviceIdx = fileName.right(1).toInt(); + QString device = deviceFile.absoluteFilePath(); + qCDebug(grabber_drm) << QString("DRM device [%1] found").arg(QSTRING_CSTR(device)); + + QSize screenSize = getScreenSize(device); + //Only add devices with a valid screen size, i.e. where a monitor is connected + if ( !screenSize.isEmpty() ) + { + QJsonArray fps = {"1", "5", "10", "15", "20", "25", "30", "40", "50", "60"}; + + QJsonObject input; + + QString displayName; + displayName = QString("Output %1").arg(deviceIdx); + + input["name"] = displayName; + input["inputIdx"] = deviceIdx; + + QJsonArray formats; + QJsonObject format; + + QJsonArray resolutionArray; + + QJsonObject resolution; + + resolution["width"] = screenSize.width(); + resolution["height"] = screenSize.height(); + resolution["fps"] = fps; + + resolutionArray.append(resolution); + + format["resolutions"] = resolutionArray; + formats.append(format); + + input["formats"] = formats; + video_inputs.append(input); + } + } + + return video_inputs; +} + +QJsonObject DRMFrameGrabber::discover(const QJsonObject ¶ms) +{ + qCDebug(grabber_drm) << "params: " << QJsonDocument(params).toJson(QJsonDocument::Compact); + + if (!isAvailable(false)) + { + return {}; + } + + QJsonObject inputsDiscovered; + + QJsonArray const video_inputs = getInputDeviceDetails(); + if (video_inputs.isEmpty()) + { + qCDebug(grabber_drm) << "No displays found to capture from!"; + return {}; + } + + inputsDiscovered["device"] = "drm"; + inputsDiscovered["device_name"] = "DRM"; + inputsDiscovered["type"] = "screen"; + inputsDiscovered["video_inputs"] = video_inputs; + + QJsonObject defaults; + QJsonObject video_inputs_default; + QJsonObject resolution_default; + + resolution_default["fps"] = _fps; + video_inputs_default["resolution"] = resolution_default; + video_inputs_default["inputIdx"] = 0; + defaults["video_input"] = video_inputs_default; + inputsDiscovered["default"] = defaults; + + qCDebug(grabber_drm) << "Discovered input devices:" << QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact).constData(); + + return inputsDiscovered; +} + +QDebug operator<<(QDebug dbg, const drmModeFB2* fb) +{ + QDebugStateSaver saver(dbg); + dbg.nospace(); + + if (!fb) + { + dbg << "drmModeFB2Ptr(null)"; + return dbg; + } + + dbg << "drmModeFB2(" + << "id: " << fb->fb_id + << ", size: " << fb->width << "x" << fb->height + << ", DRM format: " << getDrmFormat(fb->pixel_format) + << ", DRM modifier: " << getDrmModifierName(fb->modifier) + << ", flags: " << Qt::hex << fb->flags << Qt::dec + << ", handles: " << fb->handles[0] << fb->handles[1] << fb->handles[2] << fb->handles[3] + << ", pitches: " << fb->pitches[0] << fb->pitches[1] << fb->pitches[2] << fb->pitches[3] + << ", offsets: " << fb->offsets[0] << fb->offsets[1] << fb->offsets[2] << fb->offsets[3] + << ")"; + + return dbg; +} + +QDebug operator<<(QDebug dbg, const drmModePlane* plane) +{ + QDebugStateSaver saver(dbg); + dbg.nospace(); + + if (!plane) + { + dbg << "drmModePlanePtr(null)"; + return dbg; + } + + dbg << "drmModePlane(" + << "id: " << plane->plane_id + << ", CRTC id: " << plane->crtc_id + << ", FB id: " << plane->fb_id + << ", pos: " << plane->x << "," << plane->y + << ", CRTC pos: " << plane->crtc_x << "," << plane->crtc_y + << ", possible CRTCs: " << Qt::hex << plane->possible_crtcs << Qt::dec + << ", gamma size: " << plane->gamma_size + << ", format count: " << plane->count_formats + << ")"; + + return dbg; +} \ No newline at end of file diff --git a/libsrc/grabber/drm/DRMWrapper.cpp b/libsrc/grabber/drm/DRMWrapper.cpp new file mode 100644 index 000000000..4c63d38a0 --- /dev/null +++ b/libsrc/grabber/drm/DRMWrapper.cpp @@ -0,0 +1,34 @@ +#include + +DRMWrapper::DRMWrapper(int updateRate_Hz, + int deviceIdx, + int pixelDecimation, + int cropLeft, int cropRight, int cropTop, int cropBottom) + : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) + , _grabber(deviceIdx, cropLeft, cropRight, cropTop, cropBottom) +{ + _grabber.setPixelDecimation(pixelDecimation); +} + +DRMWrapper::DRMWrapper(const QJsonDocument &grabberConfig) + : DRMWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + grabberConfig["input"].toInt(0), + GrabberWrapper::DEFAULT_PIXELDECIMATION, + 0, 0, 0, 0) +{ + _isAvailable = _grabber.isAvailable(); + if (_isAvailable) + { + handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); + } +} + +void DRMWrapper::action() +{ + if (!_isAvailable) + { + return; + } + + transferFrame(_grabber); +} diff --git a/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp b/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp index 1d197226f..966c31aa6 100644 --- a/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp +++ b/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -6,8 +8,6 @@ #include #include #include - -// STL includes #include //Qt @@ -25,14 +25,11 @@ const bool verbose = false; const char DISCOVERY_DIRECTORY[] = "/dev/"; const char DISCOVERY_FILEPATTERN[] = "fb?"; -} //End of constants - -// Local includes -#include +}; //End of constants FramebufferFrameGrabber::FramebufferFrameGrabber(int deviceIdx) : Grabber("GRABBER-FB") - , _fbfd (-1) + , _deviceFd(-1) { _input = deviceIdx; _useImageResampler = true; @@ -45,156 +42,150 @@ FramebufferFrameGrabber::~FramebufferFrameGrabber() bool FramebufferFrameGrabber::setupScreen() { - bool rc (false); - - if ( _fbfd >= 0 ) + if ( _deviceFd >= 0 ) { closeDevice(); } - rc = getScreenInfo(); - setEnabled(rc); + bool success = getScreenInfo(); + setEnabled(success); - return rc; + return success; } bool FramebufferFrameGrabber::setWidthHeight(int width, int height) { - bool rc (false); - if(Grabber::setWidthHeight(width, height)) + if (Grabber::setWidthHeight(width, height)) { - rc = setupScreen(); + return setupScreen(); } - return rc; + + return false; } int FramebufferFrameGrabber::grabFrame(Image & image) { - int rc = 0; + if (!_isEnabled || _isDeviceInError) + { + return -1; + } - if (_isEnabled && !_isDeviceInError) + if ( !getScreenInfo() ) { - if ( getScreenInfo() ) - { - /* map the device to memory */ - uint8_t * fbp = static_cast(mmap(nullptr, _fixInfo.smem_len, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, _fbfd, 0)); - if (fbp == MAP_FAILED) { - - QString errorReason = QString ("Error mapping %1, [%2] %3").arg(_fbDevice).arg(errno).arg(std::strerror(errno)); - this->setInError ( errorReason ); - closeDevice(); - rc = -1; - } - else - { - _imageResampler.processImage(fbp, - static_cast(_varInfo.xres), - static_cast(_varInfo.yres), - static_cast(_fixInfo.line_length), - _pixelFormat, - image); - munmap(fbp, _fixInfo.smem_len); - } - } + return -1; + } + + /* map the device to memory */ + auto * fbp = static_cast(mmap(nullptr, _fixInfo.smem_len, PROT_READ, MAP_SHARED, _deviceFd, 0)); + if (fbp == MAP_FAILED) + { + QString errorReason = QString ("Error mapping %1, [%2] %3").arg(getDeviceName()).arg(errno).arg(std::strerror(errno)); + this->setInError ( errorReason ); closeDevice(); + return -1; } - return rc; + + _imageResampler.processImage(fbp, + static_cast(_varInfo.xres), + static_cast(_varInfo.yres), + static_cast(_fixInfo.line_length), + _pixelFormat, + image); + munmap(fbp, _fixInfo.smem_len); + + closeDevice(); + + return 0; } bool FramebufferFrameGrabber::openDevice() { - bool rc = true; - - _fbDevice = getPath(); /* Open the framebuffer device */ - _fbfd = ::open(QSTRING_CSTR(_fbDevice), O_RDONLY); - if (_fbfd < 0) + _deviceFd = ::open(QSTRING_CSTR(getDeviceName()), O_RDONLY); + if (_deviceFd < 0) { - QString errorReason = QString ("Error opening %1, [%2] %3").arg(_fbDevice).arg(errno).arg(std::strerror(errno)); + QString errorReason = QString ("Error opening %1, [%2] %3").arg(getDeviceName()).arg(errno).arg(std::strerror(errno)); this->setInError ( errorReason ); - rc = false; + return false; } - return rc; + return true; } bool FramebufferFrameGrabber::closeDevice() { - bool rc = false; - if (_fbfd >= 0) + if (_deviceFd < 0) { - if( ::close(_fbfd) == 0) { - rc = true; - } - _fbfd = -1; + return true; } - return rc; + + bool success = (::close(_deviceFd) == 0); + _deviceFd = -1; + + return success; } QSize FramebufferFrameGrabber::getScreenSize() const { - return getScreenSize(_fbDevice); + return getScreenSize(getDeviceName()); } QSize FramebufferFrameGrabber::getScreenSize(const QString& device) const { - QSize size; - int fbfd = ::open(QSTRING_CSTR(device), O_RDONLY); - if (fbfd != -1) + if (fbfd == -1) { - struct fb_var_screeninfo vinfo; - int result = ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo); - if (result == 0) - { - size.setWidth(static_cast(vinfo.xres)); - size.setHeight(static_cast(vinfo.yres)); - DebugIf(verbose, _log, "FB device [%s] found with resolution: %dx%d", QSTRING_CSTR(device), size.width(), size.height()); - } - ::close(fbfd); + return {}; + } + + QSize size; + struct fb_var_screeninfo vinfo; + int result = ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo); + if (result == 0) + { + size.setWidth(static_cast(vinfo.xres)); + size.setHeight(static_cast(vinfo.yres)); + DebugIf(verbose, _log, "FB device [%s] found with resolution: %dx%d", QSTRING_CSTR(device), size.width(), size.height()); } + ::close(fbfd); + return size; } bool FramebufferFrameGrabber::getScreenInfo() { - bool rc (false); + if ( !openDevice() ) + { + return false; + } - if ( openDevice() ) + if (ioctl(_deviceFd, FBIOGET_FSCREENINFO, &_fixInfo) < 0 || ioctl (_deviceFd, FBIOGET_VSCREENINFO, &_varInfo) < 0) { - if (ioctl(_fbfd, FBIOGET_FSCREENINFO, &_fixInfo) < 0 || ioctl (_fbfd, FBIOGET_VSCREENINFO, &_varInfo) < 0) - { - QString errorReason = QString ("Error getting screen information for %1, [%2] %3").arg(_fbDevice).arg(errno).arg(std::strerror(errno)); - this->setInError ( errorReason ); - closeDevice(); - } - else - { - rc = true; - switch (_varInfo.bits_per_pixel) - { - case 16: _pixelFormat = PixelFormat::BGR16; - break; - case 24: _pixelFormat = PixelFormat::BGR24; - break; - case 32: _pixelFormat = PixelFormat::BGR32; - break; - default: - rc= false; - QString errorReason = QString ("Unknown pixel format: %1 bits per pixel").arg(static_cast(_varInfo.bits_per_pixel)); - this->setInError ( errorReason ); - closeDevice(); - } - } + QString errorReason = QString ("Error getting screen information for %1, [%2] %3").arg(getDeviceName()).arg(errno).arg(std::strerror(errno)); + this->setInError ( errorReason ); + closeDevice(); + return false; } - return rc; -} -QJsonObject FramebufferFrameGrabber::discover(const QJsonObject& params) -{ - DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); + switch (_varInfo.bits_per_pixel) + { + case 16: _pixelFormat = PixelFormat::BGR16; + break; + case 24: _pixelFormat = PixelFormat::BGR24; + break; + case 32: _pixelFormat = PixelFormat::BGR32; + break; + default: + QString errorReason = QString ("Unknown pixel format: %1 bits per pixel").arg(static_cast(_varInfo.bits_per_pixel)); + this->setInError ( errorReason ); + closeDevice(); + return false; + } - QJsonObject inputsDiscovered; + return true; +} +QJsonArray FramebufferFrameGrabber::getInputDeviceDetails() const +{ //Find framebuffer devices 0-9 QDir deviceDirectory (DISCOVERY_DIRECTORY); QStringList deviceFilter(DISCOVERY_FILEPATTERN); @@ -202,14 +193,13 @@ QJsonObject FramebufferFrameGrabber::discover(const QJsonObject& params) deviceDirectory.setSorting(QDir::Name); QFileInfoList deviceFiles = deviceDirectory.entryInfoList(QDir::System); - int fbIdx (0); QJsonArray video_inputs; - - QFileInfoList::const_iterator deviceFileIterator; - for (deviceFileIterator = deviceFiles.constBegin(); deviceFileIterator != deviceFiles.constEnd(); ++deviceFileIterator) + for (const auto &deviceFile : deviceFiles) { - fbIdx = (*deviceFileIterator).fileName().right(1).toInt(); - QString device = (*deviceFileIterator).absoluteFilePath(); + QString const fileName = deviceFile.fileName(); + int deviceIdx = fileName.right(1).toInt(); + QString device = deviceFile.absoluteFilePath(); + DebugIf(verbose, _log, "FB device [%s] found", QSTRING_CSTR(device)); QSize screenSize = getScreenSize(device); @@ -220,10 +210,10 @@ QJsonObject FramebufferFrameGrabber::discover(const QJsonObject& params) QJsonObject in; QString displayName; - displayName = QString("FB%1").arg(fbIdx); + displayName = QString("FB%1").arg(deviceIdx); in["name"] = displayName; - in["inputIdx"] = fbIdx; + in["inputIdx"] = deviceIdx; QJsonArray formats; QJsonObject format; @@ -244,26 +234,39 @@ QJsonObject FramebufferFrameGrabber::discover(const QJsonObject& params) in["formats"] = formats; video_inputs.append(in); } - - if (!video_inputs.isEmpty()) - { - inputsDiscovered["device"] = "framebuffer"; - inputsDiscovered["device_name"] = "Framebuffer"; - inputsDiscovered["type"] = "screen"; - inputsDiscovered["video_inputs"] = video_inputs; - - QJsonObject defaults, video_inputs_default, resolution_default; - resolution_default["fps"] = _fps; - video_inputs_default["resolution"] = resolution_default; - video_inputs_default["inputIdx"] = 0; - defaults["video_input"] = video_inputs_default; - inputsDiscovered["default"] = defaults; - } } - if (inputsDiscovered.isEmpty()) + return video_inputs; +} + +QJsonObject FramebufferFrameGrabber::discover(const QJsonObject& params) +{ + DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); + + QJsonObject inputsDiscovered; + + QJsonArray const video_inputs = getInputDeviceDetails(); + if (video_inputs.isEmpty()) { DebugIf(verbose, _log, "No displays found to capture from!"); + return {}; + } + + if (!video_inputs.isEmpty()) + { + inputsDiscovered["device"] = "framebuffer"; + inputsDiscovered["device_name"] = "Framebuffer"; + inputsDiscovered["type"] = "screen"; + inputsDiscovered["video_inputs"] = video_inputs; + + QJsonObject defaults; + QJsonObject video_inputs_default; + QJsonObject resolution_default; + resolution_default["fps"] = _fps; + video_inputs_default["resolution"] = resolution_default; + video_inputs_default["inputIdx"] = 0; + defaults["video_input"] = video_inputs_default; + inputsDiscovered["default"] = defaults; } DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); diff --git a/libsrc/grabber/framebuffer/FramebufferWrapper.cpp b/libsrc/grabber/framebuffer/FramebufferWrapper.cpp index 1797e5138..58d3d9fc7 100644 --- a/libsrc/grabber/framebuffer/FramebufferWrapper.cpp +++ b/libsrc/grabber/framebuffer/FramebufferWrapper.cpp @@ -1,20 +1,20 @@ #include -FramebufferWrapper::FramebufferWrapper( int updateRate_Hz, - int deviceIdx, - int pixelDecimation) +FramebufferWrapper::FramebufferWrapper(int updateRate_Hz, + int deviceIdx, + int pixelDecimation) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) , _grabber(deviceIdx) { _grabber.setPixelDecimation(pixelDecimation); } -FramebufferWrapper::FramebufferWrapper(const QJsonDocument& grabberConfig) +FramebufferWrapper::FramebufferWrapper(const QJsonDocument &grabberConfig) : FramebufferWrapper(GrabberWrapper::DEFAULT_RATE_HZ, - 0, + grabberConfig["input"].toInt(0), GrabberWrapper::DEFAULT_PIXELDECIMATION) { - this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); + handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } void FramebufferWrapper::action() diff --git a/libsrc/grabber/qt/QtWrapper.cpp b/libsrc/grabber/qt/QtWrapper.cpp index aa6df2d8a..8740166fb 100644 --- a/libsrc/grabber/qt/QtWrapper.cpp +++ b/libsrc/grabber/qt/QtWrapper.cpp @@ -13,7 +13,7 @@ QtWrapper::QtWrapper( int updateRate_Hz, QtWrapper::QtWrapper(const QJsonDocument& grabberConfig) : QtWrapper(GrabberWrapper::DEFAULT_RATE_HZ, - 0, + grabberConfig["input"].toInt(0), GrabberWrapper::DEFAULT_PIXELDECIMATION, 0,0,0,0) { diff --git a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp index e6256d7e8..b7f06eb96 100644 --- a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp @@ -1549,19 +1549,24 @@ void V4L2Grabber::enumVideoCaptureDevices() } if (!deviceControlList.isEmpty()) + { _deviceControls.insert("/dev/"+it.fileName(), deviceControlList); + } if (close(fd) < 0) continue; QFile devNameFile(dev+"/name"); - if (devNameFile.exists()) + if (devNameFile.exists() && devNameFile.open(QFile::ReadOnly)) { - devNameFile.open(QFile::ReadOnly); devName = devNameFile.readLine(); devName = devName.trimmed(); properties.name = devName; devNameFile.close(); } + else + { + Error(_log, "Device file '%s' cannot be opened.", QSTRING_CSTR(devNameFile.fileName())); + } _deviceProperties.insert("/dev/"+it.fileName(), properties); } diff --git a/libsrc/grabber/xcb/XcbCommandExecutor.h b/libsrc/grabber/xcb/XcbCommandExecutor.h index b58a43fc7..4a95e1ed2 100644 --- a/libsrc/grabber/xcb/XcbCommandExecutor.h +++ b/libsrc/grabber/xcb/XcbCommandExecutor.h @@ -7,7 +7,7 @@ void check_error(xcb_generic_error_t * error) { if (error) { - Logger * LOGGER = Logger::getInstance("XCB"); + QSharedPointer LOGGER = Logger::getInstance("XCB"); Error(LOGGER, "XCB request failed, event_error: response_type:%u error_code:%u " "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n", diff --git a/libsrc/hyperion/BGEffectHandler.cpp b/libsrc/hyperion/BGEffectHandler.cpp index 6d57570fd..74646fc31 100644 --- a/libsrc/hyperion/BGEffectHandler.cpp +++ b/libsrc/hyperion/BGEffectHandler.cpp @@ -17,6 +17,7 @@ BGEffectHandler::BGEffectHandler(const QSharedPointer& hyperionInstanc subComponent = hyperion->property("instance").toString(); } _log = Logger::getInstance("HYPERION", subComponent); + TRACK_SCOPE_SUBCOMPONENT; if (hyperion) { @@ -30,7 +31,7 @@ BGEffectHandler::BGEffectHandler(const QSharedPointer& hyperionInstanc BGEffectHandler::~BGEffectHandler() { - qDebug() << "BGEffectHandler::~BGEffectHandler()..."; + TRACK_SCOPE_SUBCOMPONENT; } void BGEffectHandler::disconnect() diff --git a/libsrc/hyperion/CaptureCont.cpp b/libsrc/hyperion/CaptureCont.cpp index 5f0b3d44d..72757e22c 100644 --- a/libsrc/hyperion/CaptureCont.cpp +++ b/libsrc/hyperion/CaptureCont.cpp @@ -33,11 +33,12 @@ CaptureCont::CaptureCont(const QSharedPointer& hyperionInstance) , _audioCapturePriority(0) , _audioCaptureInactiveTimer(nullptr) { + TRACK_SCOPE; } CaptureCont::~CaptureCont() { - qDebug() << "CaptureCont::~CaptureCont()..."; + TRACK_SCOPE; } void CaptureCont::start() diff --git a/libsrc/hyperion/ComponentRegister.cpp b/libsrc/hyperion/ComponentRegister.cpp index a52c615c9..976c5a9e4 100644 --- a/libsrc/hyperion/ComponentRegister.cpp +++ b/libsrc/hyperion/ComponentRegister.cpp @@ -19,6 +19,7 @@ ComponentRegister::ComponentRegister(const QSharedPointer& hyperionIns subComponent = hyperion->property("instance").toString(); } _log = Logger::getInstance("COMPONENTREG", subComponent); + TRACK_SCOPE_SUBCOMPONENT; // init all comps to false QVector vect; @@ -81,7 +82,7 @@ ComponentRegister::ComponentRegister(const QSharedPointer& hyperionIns ComponentRegister::~ComponentRegister() { - qDebug() << "ComponentRegister::~ComponentRegister()..."; + TRACK_SCOPE_SUBCOMPONENT; } int ComponentRegister::isComponentEnabled(hyperion::Components comp) const diff --git a/libsrc/hyperion/Grabber.cpp b/libsrc/hyperion/Grabber.cpp index 37ea49e7c..4ef42ec1a 100644 --- a/libsrc/hyperion/Grabber.cpp +++ b/libsrc/hyperion/Grabber.cpp @@ -23,9 +23,15 @@ Grabber::Grabber(const QString& grabberName, int cropLeft, int cropRight, int cr , _isEnabled(true) , _isDeviceInError(false) { + TRACK_SCOPE; Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); } +Grabber::~Grabber() +{ + TRACK_SCOPE; +} + void Grabber::setEnabled(bool enable) { Info(_log,"Capture interface is now %s", enable ? "enabled" : "disabled"); diff --git a/libsrc/hyperion/GrabberWrapper.cpp b/libsrc/hyperion/GrabberWrapper.cpp index a5f48b62d..8f348ea09 100644 --- a/libsrc/hyperion/GrabberWrapper.cpp +++ b/libsrc/hyperion/GrabberWrapper.cpp @@ -32,6 +32,7 @@ GrabberWrapper::GrabberWrapper(const QString& grabberName, Grabber * ggrabber, i , _ggrabber(ggrabber) , _isAvailable(true) { + TRACK_SCOPE; GrabberWrapper::instance = this; _timer.reset(new QTimer(this)); @@ -64,6 +65,7 @@ GrabberWrapper::GrabberWrapper(const QString& grabberName, Grabber * ggrabber, i GrabberWrapper::~GrabberWrapper() { + TRACK_SCOPE; _timer->stop(); GrabberWrapper::instance = nullptr; } @@ -177,6 +179,10 @@ QStringList GrabberWrapper::availableGrabbers(GrabberTypeFilter type) #ifdef ENABLE_DDA grabbers << "dda"; #endif + + #ifdef ENABLE_DRM + grabbers << "drm"; + #endif } if (type == GrabberTypeFilter::VIDEO || type == GrabberTypeFilter::ALL) @@ -252,6 +258,8 @@ void GrabberWrapper::handleSettingsUpdate(settings::type type, const QJsonDocume // width/height _ggrabber->setWidthHeight(obj["width"].toInt(96), obj["height"].toInt(96)); + _ggrabber->setInput(obj["input"].toInt(0)); + // display index for MAC _ggrabber->setDisplayIndex(obj["input"].toInt(0)); diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index cf21eb9bb..93299d3de 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -19,7 +19,7 @@ #include #include #include "utils/WaitTime.h" -#include "utils/TrackedMemory.h" +#include "utils/MemoryTracker.h" // LedDevice includes #include @@ -59,22 +59,22 @@ Hyperion::Hyperion(quint8 instance, QObject* parent) , _settingsManager(nullptr) , _componentRegister(nullptr) , _imageProcessor(nullptr) - , _muxer(nullptr) , _raw2ledAdjustment(nullptr) + , _muxer(nullptr) , _ledDeviceWrapper(nullptr) , _deviceSmooth(nullptr) -#if defined(ENABLE_EFFECTENGINE) + , _captureCont(nullptr) + , _BGEffectHandler(nullptr) +#if defined(ENABLE_EFFECTENGINE) , _effectEngine(nullptr) +#endif +#if defined(ENABLE_BOBLIGHT_SERVER) + , _boblightServer(nullptr) #endif , _log(nullptr) , _hwLedCount(0) , _layoutLedCount(0) , _colorOrder("rgb") - , _BGEffectHandler(nullptr) - , _captureCont(nullptr) -#if defined(ENABLE_BOBLIGHT_SERVER) - , _boblightServer(nullptr) -#endif , _lastImageEmission(0) , _lastRawLedDataEmission(0) , _lastLedDeviceDataEmission(0) @@ -89,12 +89,13 @@ Hyperion::Hyperion(quint8 instance, QObject* parent) this->setProperty("instance", QVariant::fromValue(subComponent)); _log = Logger::getInstance("HYPERION", subComponent); + TRACK_SCOPE_SUBCOMPONENT; } Hyperion::~Hyperion() { Debug(_log, "Hyperion instance [%u] is stopping...", _instIndex); - qDebug() << "Hyperion::~Hyperion() - Hyperion instance [" << _instIndex << "] is stopping..."; + TRACK_SCOPE_SUBCOMPONENT; } void Hyperion::start() diff --git a/libsrc/hyperion/HyperionIManager.cpp b/libsrc/hyperion/HyperionIManager.cpp index 3efea7a4a..5be6dbe16 100644 --- a/libsrc/hyperion/HyperionIManager.cpp +++ b/libsrc/hyperion/HyperionIManager.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include // qt #include @@ -16,6 +16,7 @@ HyperionIManager::HyperionIManager(QObject* parent) , _log(Logger::getInstance("HYPERION-INSTMGR")) , _instanceTable(new InstanceTable(this)) { + TRACK_SCOPE; HIMinstance = this; qRegisterMetaType("InstanceState"); @@ -24,6 +25,7 @@ HyperionIManager::HyperionIManager(QObject* parent) HyperionIManager::~HyperionIManager() { + TRACK_SCOPE; } QSharedPointer HyperionIManager::getHyperionInstance(quint8 instanceId) diff --git a/libsrc/hyperion/ImageProcessor.cpp b/libsrc/hyperion/ImageProcessor.cpp index 0934ba6ae..9aaeb435c 100644 --- a/libsrc/hyperion/ImageProcessor.cpp +++ b/libsrc/hyperion/ImageProcessor.cpp @@ -1,3 +1,5 @@ +#include +#include // Hyperion includes #include @@ -7,10 +9,7 @@ // Blackborder includes #include -#include -#include - -#include "utils/TrackedMemory.h" +#include "utils/MemoryTracker.h" using namespace hyperion; diff --git a/libsrc/hyperion/ImageToLedsMap.cpp b/libsrc/hyperion/ImageToLedsMap.cpp index fdfa84c4e..aa2dc8a4c 100644 --- a/libsrc/hyperion/ImageToLedsMap.cpp +++ b/libsrc/hyperion/ImageToLedsMap.cpp @@ -3,7 +3,7 @@ using namespace hyperion; ImageToLedsMap::ImageToLedsMap( - Logger* log, + QSharedPointer log, int width, int height, int horizontalBorder, @@ -20,6 +20,7 @@ ImageToLedsMap::ImageToLedsMap( , _clusterCount() , _colorsMap() { + TRACK_SCOPE; _nextPixelCount = reducedPixelSetFactor + 1; setAccuracyLevel(accuracyLevel); @@ -114,10 +115,10 @@ ImageToLedsMap::ImageToLedsMap( } - ImageToLedsMap::~ImageToLedsMap() - { - qDebug() << "ImageToLedsMap::~ImageToLedsMap()..."; - } +ImageToLedsMap::~ImageToLedsMap() +{ + TRACK_SCOPE; +} int ImageToLedsMap::width() const { diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 3c7812e11..84f0b6936 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -1,9 +1,4 @@ -// Qt includes -#include -#include - #include -#include #include #include @@ -17,6 +12,11 @@ #define ALWAYS_INLINE inline #endif +#include +#include + +#include + /// Clamps the rounded values to the byte-interval of [0, 255]. ALWAYS_INLINE long clampRounded(const floatT x) { return std::min(255L, std::max(0L, std::lroundf(x))); @@ -83,6 +83,7 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonObject &config, const QSha _prioMuxerWeak = hyperion->getMuxerInstance(); } _log= Logger::getInstance("SMOOTHING", subComponent); + TRACK_SCOPE_SUBCOMPONENT; // init cfg (default) updateConfig(SmoothingConfigID::SYSTEM, DEFAULT_SETTLINGTIME, DEFAULT_UPDATEFREQUENCY, DEFAULT_OUTPUTDEPLAY); @@ -92,14 +93,13 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonObject &config, const QSha } LinearColorSmoothing::~LinearColorSmoothing() { - qDebug() << "LinearColorSmoothing::~LinearColorSmoothing()..."; + TRACK_SCOPE_SUBCOMPONENT; } void LinearColorSmoothing::start() { Info(_log, "LinearColorSmoothing starting..."); - _timer.reset(new QTimer(this)); _timer->setTimerType(Qt::PreciseTimer); diff --git a/libsrc/hyperion/MultiColorAdjustment.cpp b/libsrc/hyperion/MultiColorAdjustment.cpp index 31b261f84..74fb0959f 100644 --- a/libsrc/hyperion/MultiColorAdjustment.cpp +++ b/libsrc/hyperion/MultiColorAdjustment.cpp @@ -9,10 +9,12 @@ MultiColorAdjustment::MultiColorAdjustment(int ledCnt) : _ledAdjustments(static_cast(ledCnt), nullptr) , _log(Logger::getInstance("ADJUSTMENT")) { + TRACK_SCOPE; } MultiColorAdjustment::~MultiColorAdjustment() { + TRACK_SCOPE; for (ColorAdjustment* adjustment : _adjustment) { delete adjustment; diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index 45ee80e0f..86e8729ae 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -33,6 +33,7 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) { QString subComponent = parent->property("instance").toString(); _log= Logger::getInstance("MUXER", subComponent); + TRACK_SCOPE_SUBCOMPONENT; // init lowest priority info _lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY; @@ -50,7 +51,7 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) PriorityMuxer::~PriorityMuxer() { - qDebug() << "PriorityMuxer::~PriorityMuxer()..."; + TRACK_SCOPE_SUBCOMPONENT; } void PriorityMuxer::start() diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h index 8d3ff0cfd..d106d9fad 100644 --- a/libsrc/jsonserver/JsonClientConnection.h +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -52,5 +52,5 @@ private slots: QByteArray _receiveBuffer; /// The logger instance - Logger * _log; + QSharedPointer _log; }; diff --git a/libsrc/jsonserver/JsonServer.cpp b/libsrc/jsonserver/JsonServer.cpp index daf9358a0..367d0cf06 100644 --- a/libsrc/jsonserver/JsonServer.cpp +++ b/libsrc/jsonserver/JsonServer.cpp @@ -29,11 +29,13 @@ JsonServer::JsonServer(const QJsonDocument& config) , _netOrigin(NetOrigin::getInstance()) , _config(config) { + TRACK_SCOPE; Debug(_log, "JSON API server created"); } JsonServer::~JsonServer() { + TRACK_SCOPE; stop(); qDeleteAll(_openConnections); _openConnections.clear(); diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp index a5817a30c..469e2d9f1 100644 --- a/libsrc/leddevice/LedDevice.cpp +++ b/libsrc/leddevice/LedDevice.cpp @@ -65,12 +65,13 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent) , _isRefreshEnabled(false) , _isAutoStart(true) { + TRACK_SCOPE; _activeDeviceType = deviceConfig["type"].toString("UNSPECIFIED").toLower(); } LedDevice::~LedDevice() { - qDebug() << "LedDevice::~LedDevice()..."; + TRACK_SCOPE_SUBCOMPONENT; } void LedDevice::start() @@ -594,7 +595,7 @@ QJsonObject LedDevice::getProperties(const QJsonObject& params) return properties; } -void LedDevice::setLogger(Logger* log) +void LedDevice::setLogger(QSharedPointer log) { _log = log; } diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 636a88ec7..c08fe4b7d 100644 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -16,7 +16,7 @@ LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig) { - Logger * log = Logger::getInstance("LEDDEVICE"); + QSharedPointer log = Logger::getInstance("LEDDEVICE"); QJsonDocument config(deviceConfig); QString type = deviceConfig["type"].toString("UNSPECIFIED").toLower(); diff --git a/libsrc/leddevice/LedDeviceWrapper.cpp b/libsrc/leddevice/LedDeviceWrapper.cpp index 73ad7b8f0..3d54d1928 100644 --- a/libsrc/leddevice/LedDeviceWrapper.cpp +++ b/libsrc/leddevice/LedDeviceWrapper.cpp @@ -39,13 +39,12 @@ LedDeviceWrapper::LedDeviceWrapper(const QSharedPointer& hyperionInsta hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, false); } _log = Logger::getInstance("LEDDEVICE", subComponent); - - + TRACK_SCOPE_SUBCOMPONENT; } LedDeviceWrapper::~LedDeviceWrapper() { - qDebug() << "LedDeviceWrapper::~LedDeviceWrapper()..."; + TRACK_SCOPE_SUBCOMPONENT; } void LedDeviceWrapper::createLedDevice(const QJsonObject& config) diff --git a/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp b/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp index d96ed85ce..3d8b1165b 100644 --- a/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp +++ b/libsrc/leddevice/dev_hid/LedDeviceHyperionUsbasp.cpp @@ -202,7 +202,7 @@ int LedDeviceHyperionUsbasp::write(const std::vector &ledValues) libusb_device_handle * LedDeviceHyperionUsbasp::openDevice(libusb_device *device) { - Logger * log = Logger::getInstance("LedDevice"); + QSharedPointer log = Logger::getInstance("LedDevice"); libusb_device_handle * handle = nullptr; int error = libusb_open(device, &handle); diff --git a/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp b/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp index 463511940..27ec4de55 100644 --- a/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp +++ b/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp @@ -82,7 +82,7 @@ bool LedDeviceLightpack::init(const QJsonObject &deviceConfig) { Debug(_log, "USB context initialized"); - if ( _log->getMinLevel() == Logger::LogLevel::LOG_DEBUG ) + if ( _log->getMinLevel() == Logger::LogLevel::Debug ) { int logLevel = LIBUSB_LOG_LEVEL_INFO; #if LIBUSB_API_VERSION >= 0x01000106 diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index 2f3b95fac..44d641956 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -352,6 +352,8 @@ double CiColor::getDistanceBetweenTwoPoints(CiColor p1, XYColor p2) LedDevicePhilipsHueBridge::LedDevicePhilipsHueBridge(const QJsonObject &deviceConfig) : ProviderUdpSSL(deviceConfig), _restApi(nullptr), _apiPort(API_DEFAULT_PORT), _api_major(0), _api_minor(0), _api_patch(0), _isPhilipsHueBridge(false), _isDiyHue(false), _isHueEntertainmentReady(false), _isAPIv2Ready(false), _useEntertainmentAPI(false), _useApiV2(true) { + TRACK_SCOPE_SUBCOMPONENT; + #ifdef ENABLE_MDNS QMetaObject::invokeMethod(MdnsBrowser::getInstance().data(), "browseForServiceType", Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType))); @@ -360,7 +362,7 @@ LedDevicePhilipsHueBridge::LedDevicePhilipsHueBridge(const QJsonObject &deviceCo LedDevicePhilipsHueBridge::~LedDevicePhilipsHueBridge() { - qDebug() << "LedDevicePhilipsHueBridge::~LedDevicePhilipsHueBridge()"; + TRACK_SCOPE_SUBCOMPONENT; } bool LedDevicePhilipsHueBridge::init(const QJsonObject &deviceConfig) @@ -1534,7 +1536,7 @@ const std::set PhilipsHueLight::GAMUT_B_MODEL_IDS = const std::set PhilipsHueLight::GAMUT_C_MODEL_IDS = {"LCA001", "LCA002", "LCA003", "LCG002", "LCP001", "LCP002", "LCT010", "LCT011", "LCT012", "LCT014", "LCT015", "LCT016", "LCT024", "LCX001", "LCX002", "LLC020", "LST002"}; -PhilipsHueLight::PhilipsHueLight(Logger *log, bool useApiV2, const QString &id, const QJsonObject &lightAttributes, int onBlackTimeToPowerOff, +PhilipsHueLight::PhilipsHueLight(QSharedPointer log, bool useApiV2, const QString &id, const QJsonObject &lightAttributes, int onBlackTimeToPowerOff, int onBlackTimeToPowerOn) : _log(log), _isUsingApiV2(useApiV2), _id(id), _on(false), _transitionTime(0), _color({0.0, 0.0, 0.0}), _hasColor(false), _colorBlack({0.0, 0.0, 0.0}), _lastSendColorTime(0), _lastBlackTime(-1), _lastWhiteTime(-1), _blackScreenTriggered(false), _onBlackTimeToPowerOff(onBlackTimeToPowerOff), _onBlackTimeToPowerOn(onBlackTimeToPowerOn) { diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h index 93207e7bd..cc1b8ec0b 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.h @@ -129,7 +129,7 @@ class PhilipsHueLight /// @param onBlackTimeToPowerOff Timeframe of Black output that triggers powering off the light /// @param onBlackTimeToPowerOn Timeframe of non Black output that triggers powering on the light /// - PhilipsHueLight(Logger* log, bool useApiV2, const QString& id, const QJsonObject& lightAttributes, + PhilipsHueLight(QSharedPointer log, bool useApiV2, const QString& id, const QJsonObject& lightAttributes, int onBlackTimeToPowerOff, int onBlackTimeToPowerOn); @@ -180,7 +180,7 @@ class PhilipsHueLight private: - Logger* _log; + QSharedPointer _log; bool _isUsingApiV2; QString _id; diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.cpp b/libsrc/leddevice/dev_net/LedDeviceWled.cpp index 763946cc5..d0534a269 100644 --- a/libsrc/leddevice/dev_net/LedDeviceWled.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceWled.cpp @@ -98,6 +98,7 @@ LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig) ,_isSwitchOffOtherSegments(DEFAULT_IS_SWITCH_OFF_OTHER_SEGMENTS) ,_isStreamToSegment(false) { + TRACK_SCOPE_SUBCOMPONENT; #ifdef ENABLE_MDNS QMetaObject::invokeMethod(MdnsBrowser::getInstance().data(), "browseForServiceType", Qt::QueuedConnection, Q_ARG(QByteArray, MdnsServiceRegister::getServiceType(_activeDeviceType))); @@ -106,6 +107,7 @@ LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig) LedDeviceWled::~LedDeviceWled() { + TRACK_SCOPE_SUBCOMPONENT; delete _restApi; _restApi = nullptr; } diff --git a/libsrc/leddevice/dev_net/LedDeviceYeelight.cpp b/libsrc/leddevice/dev_net/LedDeviceYeelight.cpp index 408dc53d6..d9e8181b1 100644 --- a/libsrc/leddevice/dev_net/LedDeviceYeelight.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceYeelight.cpp @@ -96,7 +96,7 @@ const quint16 SSDP_PORT = 1982; } //End of constants -YeelightLight::YeelightLight( Logger *log, const QString &hostname, quint16 port = API_DEFAULT_PORT) +YeelightLight::YeelightLight( QSharedPointer log, const QString &hostname, quint16 port = API_DEFAULT_PORT) :_log(log) ,_debugLevel(0) ,_isInError(false) diff --git a/libsrc/leddevice/dev_net/LedDeviceYeelight.h b/libsrc/leddevice/dev_net/LedDeviceYeelight.h index 4e8f3257f..badb1f448 100644 --- a/libsrc/leddevice/dev_net/LedDeviceYeelight.h +++ b/libsrc/leddevice/dev_net/LedDeviceYeelight.h @@ -96,7 +96,7 @@ class YeelightLight /// @param[in] hostname or IP-address /// @param[in] port, default port 55443 is used when not provided /// - YeelightLight( Logger *log, const QString &hostname, quint16 port); + YeelightLight(QSharedPointer log, const QString &hostname, quint16 port); /// /// @brief Destructor of the Yeelight light @@ -353,7 +353,7 @@ class YeelightLight /// /// void log(int logLevel,const char* msg, const char* type, ...); - Logger* _log; + QSharedPointer _log; int _debugLevel; /// Error status of Yeelight light diff --git a/libsrc/leddevice/dev_net/ProviderRestApi.h b/libsrc/leddevice/dev_net/ProviderRestApi.h index e9992571c..a99056414 100644 --- a/libsrc/leddevice/dev_net/ProviderRestApi.h +++ b/libsrc/leddevice/dev_net/ProviderRestApi.h @@ -436,7 +436,7 @@ class ProviderRestApi : public QObject /// /// @param[in] log The logger to be used /// - void setLogger(Logger* log) { _log = log; } + void setLogger(QSharedPointer log) { _log = log; } protected slots: /// Handle the SSLErrors @@ -461,7 +461,7 @@ protected slots: bool matchesPinnedCertificate(const QSslCertificate& certificate); - Logger* _log; + QSharedPointer _log; /// QNetworkAccessManager object for sending REST-requests. QScopedPointer _networkManager; diff --git a/libsrc/mdns/MdnsBrowser.cpp b/libsrc/mdns/MdnsBrowser.cpp index 00a8fd7c8..fcd071af5 100644 --- a/libsrc/mdns/MdnsBrowser.cpp +++ b/libsrc/mdns/MdnsBrowser.cpp @@ -30,12 +30,14 @@ MdnsBrowser::MdnsBrowser(QObject* parent) , _server(nullptr) , _cache(nullptr) { + TRACK_SCOPE; qRegisterMetaType("QMdnsEngine::Message"); qRegisterMetaType("QHostAddress"); } MdnsBrowser::~MdnsBrowser() { + TRACK_SCOPE; } void MdnsBrowser::initMdns() @@ -129,7 +131,7 @@ void MdnsBrowser::onHostNameResolved(const QHostAddress& address) } } -void MdnsBrowser::resolveFirstAddress(Logger* log, const QString& hostname, std::chrono::milliseconds timeout) +void MdnsBrowser::resolveFirstAddress(QSharedPointer log, const QString& hostname, std::chrono::milliseconds timeout) { qRegisterMetaType("Message"); diff --git a/libsrc/mdns/MdnsProvider.cpp b/libsrc/mdns/MdnsProvider.cpp index b5df49dbe..c11d594bb 100644 --- a/libsrc/mdns/MdnsProvider.cpp +++ b/libsrc/mdns/MdnsProvider.cpp @@ -20,10 +20,12 @@ MdnsProvider::MdnsProvider(QObject* parent) , _server(nullptr) , _hostname(nullptr) { + TRACK_SCOPE; } MdnsProvider::~MdnsProvider() { + TRACK_SCOPE; } void MdnsProvider::init() diff --git a/libsrc/protoserver/ProtoClientConnection.h b/libsrc/protoserver/ProtoClientConnection.h index 049d9852f..fa8f747e5 100644 --- a/libsrc/protoserver/ProtoClientConnection.h +++ b/libsrc/protoserver/ProtoClientConnection.h @@ -147,7 +147,7 @@ private slots: void sendErrorReply(const std::string & error); private: - Logger*_log; + QSharedPointer _log; /// The TCP-Socket that is connected tot the Proto-client QTcpSocket* _socket; diff --git a/libsrc/protoserver/ProtoServer.cpp b/libsrc/protoserver/ProtoServer.cpp index 8f50956b1..06d99fe32 100644 --- a/libsrc/protoserver/ProtoServer.cpp +++ b/libsrc/protoserver/ProtoServer.cpp @@ -24,10 +24,12 @@ ProtoServer::ProtoServer(const QJsonDocument& config, QObject* parent) , _timeout(5000) , _config(config) { + TRACK_SCOPE; } ProtoServer::~ProtoServer() { + TRACK_SCOPE; } void ProtoServer::initServer() diff --git a/libsrc/python/PythonInit.cpp b/libsrc/python/PythonInit.cpp index 5f8067fbf..83b9c373c 100644 --- a/libsrc/python/PythonInit.cpp +++ b/libsrc/python/PythonInit.cpp @@ -26,6 +26,7 @@ PythonInit::PythonInit() { + TRACK_SCOPE; // register modules EffectModule::registerHyperionExtensionModule(); @@ -189,6 +190,7 @@ void PythonInit::handlePythonError(PyStatus status, PyConfig& config) PythonInit::~PythonInit() { + TRACK_SCOPE; Debug(Logger::getInstance("DAEMON"), "Cleaning up Python interpreter"); #if (PY_VERSION_HEX < 0x030C0000) diff --git a/libsrc/python/PythonProgram.cpp b/libsrc/python/PythonProgram.cpp index 942187bc2..12b667089 100644 --- a/libsrc/python/PythonProgram.cpp +++ b/libsrc/python/PythonProgram.cpp @@ -7,7 +7,7 @@ PyThreadState* mainThreadState; -PythonProgram::PythonProgram(const QString& name, Logger* log) : +PythonProgram::PythonProgram(const QString& name, QSharedPointer log) : _name(name) , _log(log) , _tstate(nullptr) diff --git a/libsrc/ssdp/SSDPDiscover.cpp b/libsrc/ssdp/SSDPDiscover.cpp index 45401788b..18d5bff7d 100644 --- a/libsrc/ssdp/SSDPDiscover.cpp +++ b/libsrc/ssdp/SSDPDiscover.cpp @@ -36,7 +36,12 @@ SSDPDiscover::SSDPDiscover(QObject* parent) ,_regExFilter(_filter) ,_skipDupKeys(false) { + TRACK_SCOPE; +} +SSDPDiscover::~SSDPDiscover() +{ + TRACK_SCOPE; } void SSDPDiscover::searchForService(const QString& st) diff --git a/libsrc/ssdp/SSDPServer.cpp b/libsrc/ssdp/SSDPServer.cpp index 9bb65af66..3ca3ff5f3 100644 --- a/libsrc/ssdp/SSDPServer.cpp +++ b/libsrc/ssdp/SSDPServer.cpp @@ -79,10 +79,12 @@ SSDPServer::SSDPServer(QObject * parent) , _udpSocket(nullptr) , _running(false) { + TRACK_SCOPE; } SSDPServer::~SSDPServer() { + TRACK_SCOPE; } void SSDPServer::initServer() diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt index db984c583..745404ee6 100644 --- a/libsrc/utils/CMakeLists.txt +++ b/libsrc/utils/CMakeLists.txt @@ -83,7 +83,8 @@ add_library(hyperion-utils # Wait event loop function ${CMAKE_SOURCE_DIR}/include/utils/WaitTime.h # Tracking Shared objects' memory - ${CMAKE_SOURCE_DIR}/include/utils/TrackedMemory.h + ${CMAKE_SOURCE_DIR}/include/utils/MemoryTracker.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/MemoryTracker.cpp # Weak connection ${CMAKE_SOURCE_DIR}/include/utils/WeakConnect.h # Semver namespace diff --git a/libsrc/utils/DefaultSignalHandler.cpp b/libsrc/utils/DefaultSignalHandler.cpp index cb4d17e41..bd262b0ee 100644 --- a/libsrc/utils/DefaultSignalHandler.cpp +++ b/libsrc/utils/DefaultSignalHandler.cpp @@ -66,7 +66,7 @@ namespace DefaultSignalHandler { #endif void print_trace() { - Logger* log = Logger::getInstance("CORE"); + QSharedPointer log = Logger::getInstance("CORE"); #ifdef _WIN32 void* stack[50]; @@ -181,7 +181,7 @@ namespace DefaultSignalHandler { #endif void install() { - Logger* log = Logger::getInstance("CORE"); + QSharedPointer log = Logger::getInstance("CORE"); #ifdef _WIN32 signal(SIGABRT, signal_handler); diff --git a/libsrc/utils/FileUtils.cpp b/libsrc/utils/FileUtils.cpp index 401fd678b..119554417 100644 --- a/libsrc/utils/FileUtils.cpp +++ b/libsrc/utils/FileUtils.cpp @@ -21,7 +21,7 @@ namespace FileUtils { return fi.path(); } - bool removeDir(const QString& path, Logger* log) + bool removeDir(const QString& path, QSharedPointer log) { if(!QDir(path).removeRecursively()) { @@ -31,7 +31,7 @@ namespace FileUtils { return true; } - bool fileExists(const QString& path, Logger* log, bool ignError) + bool fileExists(const QString& path, QSharedPointer log, bool ignError) { if(!QFile::exists(path)) { @@ -41,7 +41,7 @@ namespace FileUtils { return true; } - bool readFile(const QString& path, QString& data, Logger* log, bool ignError) + bool readFile(const QString& path, QString& data, QSharedPointer log, bool ignError) { QFile file(path); if(!fileExists(path, log, ignError)) @@ -61,7 +61,7 @@ namespace FileUtils { return true; } - bool writeFile(const QString& path, const QByteArray& data, Logger* log) + bool writeFile(const QString& path, const QByteArray& data, QSharedPointer log) { QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) @@ -80,7 +80,7 @@ namespace FileUtils { return true; } - bool removeFile(const QString& path, Logger* log, bool ignError) + bool removeFile(const QString& path, QSharedPointer log, bool ignError) { QFile file(path); if(!file.remove()) @@ -92,7 +92,7 @@ namespace FileUtils { return true; } - void resolveFileError(const QFile& file, Logger* log) + void resolveFileError(const QFile& file, QSharedPointer log) { QFile::FileError error = file.error(); QByteArray fileNameUtf8 = file.fileName().toUtf8(); diff --git a/libsrc/utils/JsonUtils.cpp b/libsrc/utils/JsonUtils.cpp index a22a076cb..6af2f4fd6 100644 --- a/libsrc/utils/JsonUtils.cpp +++ b/libsrc/utils/JsonUtils.cpp @@ -14,7 +14,7 @@ namespace JsonUtils { -QPair readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError) +QPair readFile(const QString& path, QJsonObject& obj, QSharedPointer log, bool ignError) { QJsonValue value(obj); @@ -23,7 +23,7 @@ QPair readFile(const QString& path, QJsonObject& obj, Logger* return result; } -QPair readFile(const QString& path, QJsonValue& obj, Logger* log, bool ignError) +QPair readFile(const QString& path, QJsonValue& obj, QSharedPointer log, bool ignError) { QString data; if(!FileUtils::readFile(path, data, log, ignError)) @@ -36,7 +36,7 @@ QPair readFile(const QString& path, QJsonValue& obj, Logger* } -bool readSchema(const QString& path, QJsonObject& obj, Logger* log) +bool readSchema(const QString& path, QJsonObject& obj, QSharedPointer log) { QJsonObject schema; if(!readFile(path, schema, log).first) @@ -52,7 +52,7 @@ bool readSchema(const QString& path, QJsonObject& obj, Logger* log) return true; } -QPair parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log) +QPair parse(const QString& path, const QString& data, QJsonObject& obj, QSharedPointer log) { QJsonValue value(obj); @@ -61,7 +61,7 @@ QPair parse(const QString& path, const QString& data, QJsonOb return result; } -QPair parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log) +QPair parse(const QString& path, const QString& data, QJsonArray& arr, QSharedPointer log) { QJsonValue value(arr); @@ -70,7 +70,7 @@ QPair parse(const QString& path, const QString& data, QJsonAr return result; } -QPair parse(const QString& path, const QString& data, QJsonValue& value, Logger* log) +QPair parse(const QString& path, const QString& data, QJsonValue& value, QSharedPointer log) { QJsonDocument doc; QPair parsingResult = JsonUtils::parse(path, data, doc, log); @@ -78,7 +78,7 @@ QPair parse(const QString& path, const QString& data, QJsonVa return parsingResult; } -QPair parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log) +QPair parse(const QString& path, const QString& data, QJsonDocument& doc, QSharedPointer log) { QStringList errorList; @@ -110,7 +110,7 @@ QPair parse(const QString& path, const QString& data, QJsonDo return qMakePair(true, errorList); } -QPair validate(const QString& file, const QJsonValue& json, const QString& schemaPath, Logger* log) +QPair validate(const QString& file, const QJsonValue& json, const QString& schemaPath, QSharedPointer log) { // get the schema data QJsonObject schema; @@ -125,7 +125,7 @@ QPair validate(const QString& file, const QJsonValue& json, c return validationResult; } -QPair validate(const QString& file, const QJsonValue& json, const QJsonObject& schema, Logger* log) +QPair validate(const QString& file, const QJsonValue& json, const QJsonObject& schema, QSharedPointer log) { QStringList errorList; @@ -146,7 +146,7 @@ QPair validate(const QString& file, const QJsonValue& json, c return qMakePair(true, errorList); } -QPair correct(const QString& file, QJsonValue& json, const QJsonObject& schema, Logger* log) +QPair correct(const QString& file, QJsonValue& json, const QJsonObject& schema, QSharedPointer log) { bool wasCorrected {false}; QStringList corrections; @@ -171,7 +171,7 @@ QPair correct(const QString& file, QJsonValue& json, const QJ return qMakePair(wasCorrected, corrections); } -bool write(const QString& filename, const QJsonObject& json, Logger* log) +bool write(const QString& filename, const QJsonObject& json, QSharedPointer log) { QJsonDocument doc; @@ -181,7 +181,7 @@ bool write(const QString& filename, const QJsonObject& json, Logger* log) return FileUtils::writeFile(filename, data, log); } -bool resolveRefs(const QJsonObject& schema, QJsonObject& obj, Logger* log) +bool resolveRefs(const QJsonObject& schema, QJsonObject& obj, QSharedPointer log) { for (QJsonObject::const_iterator i = schema.begin(); i != schema.end(); ++i) { diff --git a/libsrc/utils/Logger.cpp b/libsrc/utils/Logger.cpp index c7cd90966..538b2a12c 100644 --- a/libsrc/utils/Logger.cpp +++ b/libsrc/utils/Logger.cpp @@ -1,5 +1,5 @@ #include -#include + #include @@ -16,6 +16,8 @@ #include #include +#include + #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QRecursiveMutex Logger::MapLock; @@ -23,17 +25,25 @@ QRecursiveMutex Logger::MapLock; QMutex Logger::MapLock{ QMutex::Recursive }; #endif -QMap Logger::LoggerMap{ }; -QAtomicInteger Logger::GLOBAL_MIN_LOG_LEVEL{ static_cast(Logger::LogLevel::LOG_UNSET) }; +QMap> Logger::LoggerMap{}; +QAtomicInteger Logger::GLOBAL_MIN_LOG_LEVEL{ static_cast(Logger::LogLevel::Unset) }; namespace { - const char* LogLevelStrings[] = { "", "DEBUG", "INFO", "WARNING", "ERROR", "OFF" }; + const std::array LogLevelStrings = {{ "", "DEBUG", "INFO", "WARNING", "ERROR", "OFF" }}; #ifndef _WIN32 - const int LogLevelSysLog[] = { LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR }; + const auto LogLevelSysLog = [] { + std::array arr{}; + arr[static_cast(Logger::LogLevel::Unset)] = LOG_DEBUG; + arr[static_cast(Logger::LogLevel::Debug)] = LOG_DEBUG; + arr[static_cast(Logger::LogLevel::Info)] = LOG_INFO; + arr[static_cast(Logger::LogLevel::Warning)] = LOG_WARNING; + arr[static_cast(Logger::LogLevel::Error)] = LOG_ERR; + return arr; + }(); #endif - const size_t MAX_IDENTIFICATION_LENGTH = 22; + const size_t MAX_IDENTIFICATION_LENGTH = 22; QAtomicInteger LoggerCount = 0; QAtomicInteger LoggerId = 0; @@ -44,38 +54,50 @@ namespace QThreadStorage RepeatMessage; } // namespace -Logger* Logger::getInstance(const QString& name, const QString& subName, Logger::LogLevel minLevel) +QSharedPointer Logger::getInstance(const QString& name, const QString& subName, Logger::LogLevel minLevel) { - QMutexLocker lock(&MapLock); + QMutexLocker lock(&MapLock); + QString key = name + subName; - Logger* log = LoggerMap.value(name + subName, nullptr); - if (log == nullptr) - { - log = new Logger(name, subName, minLevel); - LoggerMap.insert(name + subName, log); - connect(log, &Logger::newLogMessage, LoggerManager::getInstance().data(), &LoggerManager::handleNewLogMessage); - } + // Try to resurrect logger from weak_ptr + if (LoggerMap.contains(key)) + { + if (auto sp = LoggerMap.value(key).lock()) + { + return sp; + } + } + + // Not found or expired, create a new one. + QSharedPointer newLog = MAKE_TRACKED_SHARED_STATIC(Logger, name, subName, minLevel); - return log; + LoggerMap.insert(key, newLog); + connect(newLog.get(), &Logger::newLogMessage, LoggerManager::getInstance().data(), &LoggerManager::handleNewLogMessage); + + return newLog; } void Logger::deleteInstance(const QString& name, const QString& subName) { - QMutexLocker lock(&MapLock); - - if (name.isEmpty()) - { - for (auto* logger : std::as_const(LoggerMap)) { - logger->deleteLater(); - } - - LoggerMap.clear(); - } - else - { - LoggerMap.value(name + subName, nullptr)->deleteLater(); - LoggerMap.remove(name + subName); - } + QMutexLocker lock(&MapLock); + + if (name.isEmpty()) + { + for (const auto& weakLogger : std::as_const(LoggerMap)) { + if (auto strongLogger = weakLogger.lock()) + { + strongLogger->deleteLater(); + } + } + + LoggerMap.clear(); + } + else + { + // This will cause the weak_ptr in the map to expire. + // The logger will be deleted when the last shared_ptr is destroyed. + LoggerMap.remove(name + subName); + } } void Logger::setLogLevel(LogLevel level, const QString& name, const QString& subName) @@ -86,7 +108,7 @@ void Logger::setLogLevel(LogLevel level, const QString& name, const QString& sub } else { - Logger* log = Logger::getInstance(name, subName, level); + auto log = Logger::getInstance(name, subName, level); log->setMinLevel(level); } } @@ -98,8 +120,8 @@ Logger::LogLevel Logger::getLogLevel(const QString& name, const QString& subName return static_cast(int(GLOBAL_MIN_LOG_LEVEL)); } - const Logger* log = Logger::getInstance(name + subName); - return log->getMinLevel(); + const auto log = Logger::getInstance(name + subName); + return log->getMinLevel(); } Logger::Logger(const QString& name, const QString& subName, LogLevel minLevel) @@ -111,47 +133,47 @@ Logger::Logger(const QString& name, const QString& subName, LogLevel minLevel) { qRegisterMetaType(); - if (LoggerCount.fetchAndAddOrdered(1) == 1) - { #ifndef _WIN32 - if (_syslogEnabled) - { - openlog(nullptr, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); - } -#endif + if (LoggerCount.fetchAndAddOrdered(1) == 1 && _syslogEnabled) + { + openlog(nullptr, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); } +#else + // To keep LoggerCount behaviour consistent across platforms + LoggerCount.fetchAndAddOrdered(1); +#endif } Logger::~Logger() { - if (LoggerCount.fetchAndSubOrdered(1) == 0) - { #ifndef _WIN32 - if (_syslogEnabled) - { - closelog(); - } -#endif + if (LoggerCount.fetchAndSubOrdered(1) == 0 && _syslogEnabled) + { + closelog(); } +#else + // To keep LoggerCount behaviour consistent across platforms + LoggerCount.fetchAndSubOrdered(1); +#endif } void Logger::write(const Logger::T_LOG_MESSAGE& message) { - QString location; - if (message.level == LOG_DEBUG) - { - location = QString("%1:%2:%3() | ") - .arg(message.fileName) - .arg(message.line) - .arg(message.function); - } + QString location; + if (message.level == Logger::LogLevel::Debug) + { + location = QString("%1:%2:%3() | ") + .arg(message.fileName) + .arg(message.line) + .arg(message.function); + } QString name = "|" + message.loggerSubName + "| " + message.loggerName; name.resize(MAX_IDENTIFICATION_LENGTH, ' '); const QDateTime timestamp = QDateTime::fromMSecsSinceEpoch(message.utime); QString const msg = QString("%1 %2 : <%3> %4%5\n") - .arg(timestamp.toString(Qt::ISODateWithMs), name, LogLevelStrings[message.level], location, message.message); + .arg(timestamp.toString(Qt::ISODateWithMs), name, message.levelString, location, message.message); #ifdef _WIN32 if (IsDebuggerPresent()) @@ -167,10 +189,14 @@ void Logger::write(const Logger::T_LOG_MESSAGE& message) void Logger::Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...) { - LogLevel const globalLevel = LogLevel(int(GLOBAL_MIN_LOG_LEVEL)); + auto const globalLevel = static_cast(int(GLOBAL_MIN_LOG_LEVEL)); + + // Determine the effective minimum level: prefer global if set, otherwise this logger's level + const auto effectiveMinLevel = (globalLevel == Logger::LogLevel::Unset) + ? getMinLevel() + : globalLevel; - if ((globalLevel == Logger::LogLevel::LOG_UNSET && level < _minLevel) // no global level, use level from logger - || (globalLevel > LogLevel::LOG_UNSET && level < globalLevel)) // global level set, use global level + if (level < effectiveMinLevel) { return; } @@ -190,9 +216,9 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u write(repMsg); #ifndef _WIN32 - if (_syslogEnabled && repMsg.level >= LOG_WARNING) + if (_syslogEnabled && repMsg.level >= Logger::LogLevel::Warning) { - syslog(LogLevelSysLog[repMsg.level], "Previous line repeats %d times", RepeatCount.localData()); + syslog(LogLevelSysLog[static_cast(repMsg.level)], "Previous line repeats %d times", RepeatCount.localData()); } #endif @@ -231,16 +257,16 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u logMsg.utime = QDateTime::currentMSecsSinceEpoch(); logMsg.message = QString(msg); logMsg.level = level; - logMsg.levelString = LogLevelStrings[level]; + logMsg.levelString = LogLevelStrings[static_cast(level)]; write(logMsg); #ifndef _WIN32 - if (_syslogEnabled && level >= LOG_WARNING) - { - syslog(LogLevelSysLog[level], "%s", msg); - } + if (_syslogEnabled && level >= Logger::LogLevel::Warning) + { + syslog(LogLevelSysLog[static_cast(level)], "%s", msg); + } #endif - RepeatMessage.setLocalData(logMsg); + RepeatMessage.setLocalData(logMsg); } } diff --git a/libsrc/utils/MemoryTracker.cpp b/libsrc/utils/MemoryTracker.cpp new file mode 100644 index 000000000..41e06a5fa --- /dev/null +++ b/libsrc/utils/MemoryTracker.cpp @@ -0,0 +1,7 @@ +#include + +Q_LOGGING_CATEGORY(memory_objects_create, "memory.objects.create"); +Q_LOGGING_CATEGORY(memory_objects_track, "memory.objects.track"); +Q_LOGGING_CATEGORY(memory_objects_destroy, "memory.objects.destroy"); +Q_LOGGING_CATEGORY(memory_non_objects_create, "memory.non_objects.create"); +Q_LOGGING_CATEGORY(memory_non_objects_destroy, "memory.non_objects.destroy"); \ No newline at end of file diff --git a/libsrc/utils/NetOrigin.cpp b/libsrc/utils/NetOrigin.cpp index 686ce1e1e..8de70b149 100644 --- a/libsrc/utils/NetOrigin.cpp +++ b/libsrc/utils/NetOrigin.cpp @@ -5,7 +5,7 @@ NetOrigin* NetOrigin::instance = nullptr; -NetOrigin::NetOrigin(QObject* parent, Logger* log) +NetOrigin::NetOrigin(QObject* parent, QSharedPointer log) : QObject(parent) , _log(log) { diff --git a/libsrc/utils/Process.cpp b/libsrc/utils/Process.cpp index baa2723af..460dfc8ec 100644 --- a/libsrc/utils/Process.cpp +++ b/libsrc/utils/Process.cpp @@ -10,7 +10,7 @@ { void restartHyperion(int exitCode) { - Logger* log = Logger::getInstance("Process"); + QSharedPointer log = Logger::getInstance("Process"); Info(log, "Restarting hyperion ..."); auto arguments = QCoreApplication::arguments(); @@ -53,7 +53,7 @@ { void restartHyperion(int exitCode) { - Logger* log = Logger::getInstance("Process"); + QSharedPointer log = Logger::getInstance("Process"); Info(log, "Restarting hyperion ..."); std::cout << std::endl diff --git a/libsrc/utils/Profiler.cpp b/libsrc/utils/Profiler.cpp index bab62213e..4dcae3bbe 100644 --- a/libsrc/utils/Profiler.cpp +++ b/libsrc/utils/Profiler.cpp @@ -14,7 +14,7 @@ struct StopWatchItem { static unsigned int blockCounter = 0; static std::map GlobalProfilerMap; -Logger* Profiler::_logger = nullptr; +QSharedPointer Profiler::_logger = nullptr; double getClockDelta(clock_t start) { diff --git a/libsrc/webserver/CgiHandler.h b/libsrc/webserver/CgiHandler.h index 3c38de430..f263c6603 100644 --- a/libsrc/webserver/CgiHandler.h +++ b/libsrc/webserver/CgiHandler.h @@ -29,7 +29,7 @@ class CgiHandler : public QObject QtHttpRequest * _request; QStringList _args; QString _baseUrl; - Logger * _log; + QSharedPointer _log; }; #endif // CGIHANDLER_H diff --git a/libsrc/webserver/QtHttpServer.cpp b/libsrc/webserver/QtHttpServer.cpp index 247c79311..204f7f358 100644 --- a/libsrc/webserver/QtHttpServer.cpp +++ b/libsrc/webserver/QtHttpServer.cpp @@ -14,12 +14,12 @@ QtHttpServerWrapper::QtHttpServerWrapper (QObject * parent) : QTcpServer (parent) , m_useSsl (false) { - + TRACK_SCOPE; } QtHttpServerWrapper::~QtHttpServerWrapper (void) { - + TRACK_SCOPE; } void QtHttpServerWrapper::setUseSecure (const bool ssl) diff --git a/libsrc/webserver/StaticFileServing.h b/libsrc/webserver/StaticFileServing.h index 8a058ea69..f95cb02db 100644 --- a/libsrc/webserver/StaticFileServing.h +++ b/libsrc/webserver/StaticFileServing.h @@ -34,7 +34,7 @@ public slots: QString _baseUrl; QMimeDatabase * _mimeDb; CgiHandler _cgi; - Logger * _log; + QSharedPointer _log; QByteArray _ssdpDescription; void printErrorToReply (QtHttpReply * reply, QtHttpReply::StatusCode code, const QString& errorMessage); diff --git a/libsrc/webserver/WebJsonRpc.h b/libsrc/webserver/WebJsonRpc.h index 695bcb14b..3378533fd 100644 --- a/libsrc/webserver/WebJsonRpc.h +++ b/libsrc/webserver/WebJsonRpc.h @@ -19,7 +19,7 @@ class WebJsonRpc : public QObject { private: QtHttpServer* _server; QtHttpClientWrapper* _wrapper; - Logger* _log; + QSharedPointer _log; JsonAPI* _jsonAPI; bool _stopHandle = false; diff --git a/libsrc/webserver/WebServer.cpp b/libsrc/webserver/WebServer.cpp index c84f92536..f41d0dc0f 100644 --- a/libsrc/webserver/WebServer.cpp +++ b/libsrc/webserver/WebServer.cpp @@ -34,10 +34,12 @@ WebServer::WebServer(const QJsonDocument& config, bool useSsl, QObject* parent) , _staticFileServing (nullptr) , _server(nullptr) { + TRACK_SCOPE; } WebServer::~WebServer() { + TRACK_SCOPE; } void WebServer::initServer() @@ -173,14 +175,22 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c } // load and verify crt + QList cList; QFile cfile(crtPath); - cfile.open(QIODevice::ReadOnly); - QList validList; - QList cList = QSslCertificate::fromDevice(&cfile, QSsl::Pem); - cfile.close(); + if (!cfile.open(QIODevice::ReadOnly)) + { + Error(_log, "Error opening SSL certificate file: %s", QSTRING_CSTR(cfile.errorString())); + } + else + { + cList = QSslCertificate::fromDevice(&cfile, QSsl::Pem); + cfile.close(); + } // Filter for valid certs - for (const auto& entry : cList) { + QList validList; + for (const auto& entry : cList) + { if (!entry.isNull() && QDateTime::currentDateTime().daysTo(entry.expiryDate()) > 0) { validList.append(entry); @@ -191,27 +201,37 @@ void WebServer::handleSettingsUpdate(settings::type type, const QJsonDocument& c } } - if (!validList.isEmpty()) { + if (!validList.isEmpty()) + { Debug(_log, "Setup SSL certificate"); _server->setCertificates(validList); } - else { + else + { Error(_log, "No valid SSL certificate has been found ('%s')", crtPath.toUtf8().constData()); } // load and verify key QFile kfile(keyPath); - kfile.open(QIODevice::ReadOnly); - // The key should be RSA enrcrypted and PEM format, optional the passPhrase - QSslKey key(&kfile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, obj["keyPassPhrase"].toString().toUtf8()); - kfile.close(); + if (!kfile.open(QIODevice::ReadOnly)) + { + Error(_log, "Error opening SSL key file: %s", QSTRING_CSTR(kfile.errorString())); + } + else + { + // The key should be RSA enrcrypted and PEM format, optional the passPhrase + QSslKey key(&kfile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, obj["keyPassPhrase"].toString().toUtf8()); + kfile.close(); - if (key.isNull()) { - Error(_log, "The provided SSL key is invalid or not supported use RSA encrypt and PEM format ('%s')", keyPath.toUtf8().constData()); - } - else { - Debug(_log, "Setup private SSL key"); - _server->setPrivateKey(key); + if (key.isNull()) + { + Error(_log, "The provided SSL key is invalid or not supported use RSA encrypt and PEM format ('%s')", keyPath.toUtf8().constData()); + } + else + { + Debug(_log, "Setup private SSL key"); + _server->setPrivateKey(key); + } } } diff --git a/libsrc/webserver/WebSocketJsonHandler.h b/libsrc/webserver/WebSocketJsonHandler.h index 1f4c234b8..a9aa7f831 100644 --- a/libsrc/webserver/WebSocketJsonHandler.h +++ b/libsrc/webserver/WebSocketJsonHandler.h @@ -24,7 +24,7 @@ private slots: private: QWebSocket* _websocket; - Logger* _log; + QSharedPointer _log; QScopedPointer _jsonAPI; QString _peerAddress; QString _origin; diff --git a/qtlogging.ini b/qtlogging.ini new file mode 100644 index 000000000..2a77996e7 --- /dev/null +++ b/qtlogging.ini @@ -0,0 +1,29 @@ +[Rules] +# Trace All +*.debug=false +qt.*.debug=false + +# Memory/Object logging +#memory.*.debug=true + +# Object logging +#memory.objects.*.debug=true +#memory.objects.create.debug=true +#memory.objects.destroy.debug=true +#memory.objects.track.debug=true + +# Object logging for static objects +#memory.non_objects.*.debug=true +#memory.non_objects.create.debug=true +#memory.non_objects.destroy.debug=true + +# mDNS logging +#mdns.browser.debug=true +#mdns.provider.debug=true + +# Grabber logging +#grabber.screen.*.debug=true +#grabber.screen.drm.debug=true + +#grabber.video.*.debug=true +#grabber.video.v4l2.debug=true diff --git a/src/hyperion-aml/CMakeLists.txt b/src/hyperion-aml/CMakeLists.txt index 9c76bff2b..a685e343c 100644 --- a/src/hyperion-aml/CMakeLists.txt +++ b/src/hyperion-aml/CMakeLists.txt @@ -14,6 +14,7 @@ target_link_libraries(${PROJECT_NAME} amlogic-grabber framebuffer-grabber Qt${QT_VERSION_MAJOR}::Widgets + dl ) if(ENABLE_MDNS) diff --git a/src/hyperion-aml/hyperion-aml.cpp b/src/hyperion-aml/hyperion-aml.cpp index 0c02e2eb8..3ca0ac839 100644 --- a/src/hyperion-aml/hyperion-aml.cpp +++ b/src/hyperion-aml/hyperion-aml.cpp @@ -45,8 +45,8 @@ int main(int argc, char ** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -92,7 +92,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-dispmanx/hyperion-dispmanx.cpp b/src/hyperion-dispmanx/hyperion-dispmanx.cpp index 639432faf..4fe090c92 100644 --- a/src/hyperion-dispmanx/hyperion-dispmanx.cpp +++ b/src/hyperion-dispmanx/hyperion-dispmanx.cpp @@ -47,8 +47,8 @@ int main(int argc, char ** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -94,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } diff --git a/src/hyperion-framebuffer/hyperion-framebuffer.cpp b/src/hyperion-framebuffer/hyperion-framebuffer.cpp index efc2290e3..dc5162d92 100644 --- a/src/hyperion-framebuffer/hyperion-framebuffer.cpp +++ b/src/hyperion-framebuffer/hyperion-framebuffer.cpp @@ -46,8 +46,8 @@ int main(int argc, char ** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -95,7 +95,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-osx/hyperion-osx.cpp b/src/hyperion-osx/hyperion-osx.cpp index 24fc9a829..53c961e12 100644 --- a/src/hyperion-osx/hyperion-osx.cpp +++ b/src/hyperion-osx/hyperion-osx.cpp @@ -46,8 +46,8 @@ int main(int argc, char ** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -95,7 +95,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-qt/hyperion-qt.cpp b/src/hyperion-qt/hyperion-qt.cpp index 01d7bc2fb..66b2c95c7 100644 --- a/src/hyperion-qt/hyperion-qt.cpp +++ b/src/hyperion-qt/hyperion-qt.cpp @@ -45,8 +45,8 @@ int main(int argc, char ** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QGuiApplication const app(argc, argv); @@ -94,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-remote/JsonConnection.h b/src/hyperion-remote/JsonConnection.h index a35f08087..89943c9e4 100644 --- a/src/hyperion-remote/JsonConnection.h +++ b/src/hyperion-remote/JsonConnection.h @@ -281,7 +281,7 @@ private slots: bool parseReply(const QJsonObject & reply); // Logger class - Logger* _log; + QSharedPointer _log; /// The TCP-Socket with the connection to the server //QTcpSocket _socket; diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 963e5732b..28daf9940 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -82,8 +82,8 @@ int main(int argc, char * argv[]) { DefaultSignalHandler::install(); - Logger* log = Logger::getInstance("REMOTE"); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance("REMOTE"); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -167,7 +167,7 @@ int main(int argc, char * argv[]) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index 3f38b906e..b1e0637ef 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -49,8 +49,8 @@ int main(int argc, char** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -137,7 +137,7 @@ int main(int argc, char** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index 62867ba87..deaeb1d1a 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -45,8 +45,8 @@ int main(int argc, char ** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -94,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-xcb/hyperion-xcb.cpp b/src/hyperion-xcb/hyperion-xcb.cpp index 588263d26..d7959f878 100644 --- a/src/hyperion-xcb/hyperion-xcb.cpp +++ b/src/hyperion-xcb/hyperion-xcb.cpp @@ -45,8 +45,8 @@ int main(int argc, char ** argv) DefaultSignalHandler::install(); ErrorManager errorManager; - Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::LOG_INFO); + QSharedPointer log = Logger::getInstance(CAPTURE_TYPE.toUpper()); + Logger::setLogLevel(Logger::LogLevel::Info); QCoreApplication const app(argc, argv); @@ -94,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index a383b0479..f113cbfbb 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -57,6 +57,9 @@ add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE ${MACOS_BUNDLE_RESOURCE_FILES} ) +# Disable debug output for release builds +target_compile_definitions(${PROJECT_NAME} PRIVATE $<$:QT_NO_DEBUG_OUTPUT>) + find_package(OpenSSL QUIET) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus QUIET) target_link_libraries(${PROJECT_NAME} @@ -75,27 +78,33 @@ target_link_libraries(${PROJECT_NAME} Qt${QT_VERSION_MAJOR}::Widgets $<$:Qt${QT_VERSION_MAJOR}::DBus> # Grabber + $<$:amlogic-grabber> $<$:dispmanx-grabber> + $<$:directx-grabber> + $<$:dda-grabber> + $<$:drm-grabber> $<$:framebuffer-grabber> + $<$:mf-grabber> $<$:osx-grabber> + $<$:qt-grabber> $<$:v4l2-grabber> - $<$:mf-grabber> - $<$:audio-grabber> - $<$:amlogic-grabber> $<$:x11-grabber> $<$:xcb-grabber> - $<$:qt-grabber> - $<$:directx-grabber> - $<$:dda-grabber> + $<$:audio-grabber> # Input $<$:flatbufserver> $<$:protoserver> $<$:cechandler> # Services - $<$,$>:effectengine python> $<$:mdns> ) +if(ENABLE_EFFECTENGINE AND TARGET python) + target_link_libraries(${PROJECT_NAME} + $<$:effectengine python> + ) +endif() + ##################################### ########### Install steps ########### ##################################### @@ -120,6 +129,8 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin") MACOSX_BUNDLE_INFO_STRING "${MAC_BUNDLE_NAME} ${HYPERION_VERSION}" MACOSX_BUNDLE_SHORT_VERSION_STRING ${HYPERION_VERSION} MACOSX_BUNDLE_LONG_VERSION_STRING ${HYPERION_VERSION} + INSTALL_RPATH_USE_LINK_PATH ON + INSTALL_RPATH "@executable_path/../Frameworks" ) install(TARGETS ${PROJECT_NAME} BUNDLE DESTINATION . COMPONENT "Hyperion") diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 0ed8ffeda..f2fbea4cc 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -538,19 +538,23 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs std::string const level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug if (level == "silent") { - Logger::setLogLevel(Logger::LOG_OFF); + Logger::setLogLevel(Logger::LogLevel::Off); + } + else if (level == "off") + { + Logger::setLogLevel(Logger::LogLevel::Off); } else if (level == "warn") { - Logger::setLogLevel(Logger::LOG_WARNING); + Logger::setLogLevel(Logger::LogLevel::Warning); } else if (level == "verbose") { - Logger::setLogLevel(Logger::LOG_INFO); + Logger::setLogLevel(Logger::LogLevel::Info); } else if (level == "debug") { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); } } @@ -572,7 +576,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) && !defined(ENABLE_DDA) +#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) && !defined(ENABLE_DRM) Info(_log, "No screen capture supported on this platform"); return; #endif @@ -622,6 +626,12 @@ void HyperionDaemon::updateScreenGrabbers(const QJsonDocument& grabberConfig) startGrabber(_screenGrabber, grabberConfig); } #endif +#ifdef ENABLE_DRM + else if (type == "drm") + { + startGrabber(_screenGrabber, grabberConfig); + } +#endif #ifdef ENABLE_FB else if (type == "framebuffer") { diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index 6e9f14b30..612950d72 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -10,28 +10,41 @@ #include + +#ifdef ENABLE_AMLOGIC + #include +#else + typedef QObject AmlogicWrapper; +#endif + #ifdef ENABLE_DISPMANX #include #else typedef QObject DispmanxWrapper; #endif -#if defined(ENABLE_V4L2) || defined(ENABLE_MF) - #include +#ifdef ENABLE_DDA + #include #else - typedef QObject VideoWrapper; + typedef QObject DDAWrapper; #endif -#ifdef ENABLE_FB - #include +#ifdef ENABLE_DRM + #include #else - typedef QObject FramebufferWrapper; + typedef QObject DRMWrapper; #endif -#ifdef ENABLE_AMLOGIC - #include +#ifdef ENABLE_DX + #include #else - typedef QObject AmlogicWrapper; + typedef QObject DirectXWrapper; +#endif + +#ifdef ENABLE_FB + #include +#else + typedef QObject FramebufferWrapper; #endif #ifdef ENABLE_OSX @@ -40,6 +53,12 @@ typedef QObject OsxWrapper; #endif +#ifdef ENABLE_QT + #include +#else + typedef QObject QtWrapper; +#endif + #ifdef ENABLE_X11 #include #else @@ -52,22 +71,10 @@ typedef QObject XcbWrapper; #endif -#ifdef ENABLE_QT - #include -#else - typedef QObject QtWrapper; -#endif - -#ifdef ENABLE_DX - #include -#else - typedef QObject DirectXWrapper; -#endif - -#ifdef ENABLE_DDA - #include +#if defined(ENABLE_V4L2) || defined(ENABLE_MF) + #include #else - typedef QObject DDAWrapper; + typedef QObject VideoWrapper; #endif #include @@ -245,7 +252,7 @@ private slots: } } - Logger* _log; + QSharedPointer _log; /// Core services QScopedPointer _instanceManager; diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index 28538c20a..96e9ddca5 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -112,12 +112,26 @@ QCoreApplication* createApplication(int& argc, char* argv[]) int main(int argc, char** argv) { + //Turn off Qt debug logging per default - to be removed when qtlogging.ini is defined + QLoggingCategory::setFilterRules("*.debug = false"); + qSetMessagePattern( + "%{time yyyy-MM-ddTHH:mm:ss.zzz} |--| : %{category}" + "%{if-debug} %{function}()[%{line}] TID:%{threadid}" + #if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + " (%{threadname})" + #endif + "%{endif} %{message}\n" +#if 0 + " << %{backtrace depth=3}" +#endif + ); + ErrorManager errorManager; DefaultSignalHandler::install(); // initialize main logger and set global log level - Logger* log = Logger::getInstance("MAIN"); - Logger::setLogLevel(Logger::LOG_WARNING); + QSharedPointer log = Logger::getInstance("MAIN"); + Logger::setLogLevel(Logger::LogLevel::Warning); // Initialising QCoreApplication QScopedPointer app(createApplication(argc, argv)); @@ -216,19 +230,19 @@ int main(int argc, char** argv) int logLevelCheck = 0; if (parser.isSet(silentLogOption)) { - Logger::setLogLevel(Logger::LOG_OFF); + Logger::setLogLevel(Logger::LogLevel::Off); logLevelCheck++; } if (parser.isSet(infoLogOption)) { - Logger::setLogLevel(Logger::LOG_INFO); + Logger::setLogLevel(Logger::LogLevel::Info); logLevelCheck++; } if (parser.isSet(debugLogOption)) { - Logger::setLogLevel(Logger::LOG_DEBUG); + Logger::setLogLevel(Logger::LogLevel::Debug); logLevelCheck++; } @@ -273,7 +287,7 @@ int main(int argc, char** argv) QFile::remove(destinationFilePath); } - if (Logger::getLogLevel() == Logger::LOG_DEBUG) + if (Logger::getLogLevel() == Logger::LogLevel::Debug) { std::cout << "Copy \"" << sourceFilePath.toStdString() << "\" -> \"" << destinationFilePath.toStdString() << "\"" << '\n'; } diff --git a/test/TestImage2LedsMap.cpp b/test/TestImage2LedsMap.cpp index 08d735870..f8590e764 100644 --- a/test/TestImage2LedsMap.cpp +++ b/test/TestImage2LedsMap.cpp @@ -10,8 +10,8 @@ int main() { - Logger* log = Logger::getInstance("TestImageLedsMap"); - Logger::setLogLevel(Logger::DEBUG); + QSharedPointer log = Logger::getInstance("TestImageLedsMap"); + Logger::setLogLevel(Logger::LogLevel::Debug); const QString schemaFile = ":/hyperion-schema"; const QString configFile = ":/hyperion_default.config";