Media Foundation/V4L2 grabber ... (#1119)

* - New Media Foundation grabber
- JsonAPI available grabber fix
- commented json config removed

* Added libjpeg-turbo to dependencies

* Fix OSX build
Removed Azure Pipelines from build scripts

* Remove Platform from Dashboard

* Correct Grabber Namings

* Grabber UI improvements, generic JSONEditor Selection Update

* Active grabber fix

* Stop Framebuffer grabber on failure

* - Image format NV12 and I420 added
- Flip mode
- Scaling factor for MJPEG
- VSCode (compile before run)
- CI (push) dependency libjpeg-turbo added

* Refactor MediaFoundation (Part 1)

* Remove QDebug output

* Added image flipping ability to MF Grabber

* fix issue 1160

* -Reload MF Grabber only once per WebUI update
- Cleanup

* Improvements

* - Set 'Software Frame Decimation' begin to 0
- Removed grabber specific device name from Log
- Keep pixel format when switching resolution
- Display 'Flip mode' correct in Log
- BGR24 images always flipped

* Refactor MediaFoundation (Part 2)

* Refactor V4L2 grabber (part 1) (#62)

* Media Foundation grabber adapted to V4L2 change

* Enable Media Foundation grabber on windows

* Have fps as int, fix height typo

* Added video standards to JsonAPI output

* Error handling in source reader improved

* Fix "Frame to small" error

* Discovery VideoSources and Dynamically Update Editor

* Hide all element when no video grabber discovered, upate naming

* Do not show unsupported grabbers

* Copy Log to Clipboard

* Update Grabber schema and Defaults

* Update access levels and validate crop ranges

* Height and width in Qt grabber corrected

* Correct formatting

* Untabify

* Global component states across instances

* Components divided on the dashboard

* refactor

* Fix Merge-issues

* Database migration aligning with updated grabber model

* Align Grabber.js with new utility functions

* Allow editor-validation for enum-lists

* Handle "Show Explainations scenario" correctly

* Grabber - Ensure save is only possible on valid content

* Dashboard update + fix GlobalSignal connection

* Ensure default database is populated with current release

* Correct grabber4L2 access level

* Display Signal detection area in preview

* Write Hyperion version into default config on compiling.

* Create defaultconfig.json dynamically

* WebUI changes

* Correct grabber config look-ups

* Refactor i18n language loading

* Fix en.json

* Split global capture from instance capture config

* Update grabber default values

* Standalone grabber: Add --debug switch

* Enhance showInputOptionsForKey for multiple keys

* Add grabber instance link to system grabber config

* Only show signal detection area, if grabber is enabled

* Always show Active element on grabber page

* Remote control - Only display gabber status, if global grabber is enabled

* WebUI optimization (thx to @mkcologne)
Start Grabber only when global settings are enabled
Fixed an issue in the WebUI preview

* V4L2/MF changes

* Jsoneditor, Correct translation for default values

* Refactor LED-Device handling in UI and make element naming consistent

* MF Discovery extended

* Fix LGTM finding

* Support Grabber Bri, Hue, Sat and Con in UI, plus their defaults

* Concider Access level for item filtering

* Concider Access level for item filtering

* Revert "Concider Access level for item filtering"

This reverts commit 5b0ce3c0f2de67e0c43788190cfff45614706129.

* Disable fpsSoftwareDecimation for framegrabber, as not supported yet

* JSON-Editor- Add updated schema for validation on dynamic elements

* added V4L2 color IDs

* LGTM findings fix

* destroy SR callback only on exit

* Grabber.js - Hide elements not supported by platform

* Fixed freezing start effect

* Grabber UI - Hardware controls - Show current values and allow to reset to defaults

* Grabber - Discovery - Add current values to properties

* Small things

* Clean-up Effects and have ENDLESS consistently defined

* Fix on/off/on priority during startup, by initializing _prevVisComp in line with background priority

* Add missing translation mappings

* DirectX Grabber reactivated/ QT Grabber size decimation fixed

* typo in push-master workflow

* Use PreciseTimer for Grabber to ensure stable FPS timing

* Set default Screencapture rate consistently

* Fix libjpeg-turbo download

* Remove Zero character from file

* docker-compile Add PLATFORM parameter, only copy output file after successful compile

* Framebuffer, Dispmanx, OSX, AML Grabber discovery, various clean-up and consistencies across grabbers

* Fix merge problem - on docker-compile Add PLATFORM parameter, only copy output file after successful compile

* Fix definition

* OSXFRameGrabber - Revert cast

* Clean-ups nach Feedback

* Disable certain libraries when building armlogic via standard stretch image as developer

* Add CEC availability to ServerInfo to have it platform independent

* Grabber UI - Fix problem that crop values are not populated when refining editor rage

* Preserve value when updating json-editor range

* LEDVisualisation - Clear image when source changes

* Fix - Preserve value when updating json-editor range

* LEDVisualisation - Clear image when no component is active

* Allow to have password handled by Password-Manager (#1263)

* Update default signal detection area to green assuming rainbow grabber

* LED Visualisation - Handle empty priority update

* Fix yuv420 in v4l2 grabber

* V4L2-Grabber discovery - Only report grabbers with valid video input information

* Grabber - Update static variables to have them working in release build

* LED Visualisation - ClearImage when no priorities

* LED Visualisation - Fix Logo resizing issue

* LED Visualisation - Have nearly black background and negative logo

Co-authored-by: LordGrey <lordgrey.emmel@gmail.com>
Co-authored-by: LordGrey <48840279+Lord-Grey@users.noreply.github.com>
This commit is contained in:
Markus 2021-07-14 20:48:33 +02:00 committed by GitHub
parent b0e1510a78
commit c135d91986
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
163 changed files with 10756 additions and 5953 deletions

View File

@ -1,14 +1,8 @@
#!/bin/bash #!/bin/bash
# detect CI # detect CI
if [ "$SYSTEM_COLLECTIONID" != "" ]; then if [ "$HOME" != "" ]; then
# Azure Pipelines
echo "Azure detected"
CI_NAME="$(echo "$AGENT_OS" | tr '[:upper:]' '[:lower:]')"
CI_BUILD_DIR="$BUILD_SOURCESDIRECTORY"
elif [ "$HOME" != "" ]; then
# GitHub Actions # GitHub Actions
echo "Github Actions detected"
CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')"
CI_BUILD_DIR="$GITHUB_WORKSPACE" CI_BUILD_DIR="$GITHUB_WORKSPACE"
else else

View File

@ -1,11 +1,7 @@
#!/bin/bash #!/bin/bash
# detect CI # detect CI
if [ "$SYSTEM_COLLECTIONID" != "" ]; then if [ "$HOME" != "" ]; then
# Azure Pipelines
CI_NAME="$(echo "$AGENT_OS" | tr '[:upper:]' '[:lower:]')"
CI_BUILD_DIR="$BUILD_SOURCESDIRECTORY"
elif [ "$HOME" != "" ]; then
# GitHub Actions # GitHub Actions
CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')"
CI_BUILD_DIR="$GITHUB_WORKSPACE" CI_BUILD_DIR="$GITHUB_WORKSPACE"

View File

@ -159,10 +159,21 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
key: ${{ runner.os }}-chocolatey key: ${{ runner.os }}-chocolatey
- name: Install Python, NSIS, OpenSSL - name: "Remove Redistributable"
shell: cmd
run: |
MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}
MsiExec.exe /passive /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7}
- name: Install Python, NSIS, OpenSSL, DirectX SDK
shell: powershell shell: powershell
run: | run: |
choco install --no-progress python nsis openssl -y choco install --no-progress python nsis openssl directx-sdk -y
- name: Install libjpeg-turbo
run: |
Invoke-WebRequest https://netcologne.dl.sourceforge.net/project/libjpeg-turbo/2.0.6/libjpeg-turbo-2.0.6-vc64.exe -OutFile libjpeg-turbo.exe -UserAgent NativeHost
.\libjpeg-turbo /S
- name: Set up x64 build architecture environment - name: Set up x64 build architecture environment
shell: cmd shell: cmd

View File

@ -122,10 +122,21 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
key: ${{ runner.os }}-chocolatey key: ${{ runner.os }}-chocolatey
- name: Install Python, NSIS, OpenSSL - name: "Remove Redistributable"
shell: cmd
run: |
MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}
MsiExec.exe /passive /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7}
- name: Install Python, NSIS, OpenSSL, DirectX SDK
shell: powershell shell: powershell
run: | run: |
choco install --no-progress python nsis openssl -y choco install --no-progress python nsis openssl directx-sdk -y
- name: Install libjpeg-turbo
run: |
Invoke-WebRequest https://netcologne.dl.sourceforge.net/project/libjpeg-turbo/2.0.6/libjpeg-turbo-2.0.6-vc64.exe -OutFile libjpeg-turbo.exe -UserAgent NativeHost
.\libjpeg-turbo /S
- name: Set up x64 build architecture environment - name: Set up x64 build architecture environment
shell: cmd shell: cmd

8
.gitignore vendored
View File

@ -27,5 +27,11 @@ libsrc/flatbufserver/hyperion_request_generated.h
*.kdev* *.kdev*
# Visual Studio 2015/2017/2019 cache/options directory # Visual Studio 2015/2017/2019 cache/options directory
.vs/ # Ignore
.vs/*
CMakeSettings.json CMakeSettings.json
# Allow
!.vs/launch.vs.json
# LedDevice 'File' output
NULL

17
.vs/launch.vs.json Normal file
View File

@ -0,0 +1,17 @@
{
"version": "0.2.1",
"defaults": {},
"configurations": [
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "hyperiond.exe (bin\\hyperiond.exe)",
"name": "Run hyperion with debug option and external console",
"args": [
"-d",
"-c"
],
"externalConsole": true
}
]
}

4
.vscode/launch.json vendored
View File

@ -27,12 +27,12 @@
"name": "(Windows) hyperiond", "name": "(Windows) hyperiond",
"type": "cppvsdbg", "type": "cppvsdbg",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/Debug/hyperiond.exe", "program": "${command:cmake.launchTargetDirectory}/hyperiond",
"args": ["-d"], "args": ["-d"],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"environment": [], "environment": [],
"externalConsole": false "console": "internalConsole"
} }
] ]
} }

View File

@ -4,10 +4,15 @@ message( STATUS "CMake Version: ${CMAKE_VERSION}" )
PROJECT(hyperion) PROJECT(hyperion)
# Parse semantic version of version file # Parse semantic version of version file and write version to config
include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cmake) include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cmake)
file (STRINGS "version" HYPERION_VERSION) file (STRINGS "version" HYPERION_VERSION)
SetVersionNumber(HYPERION ${HYPERION_VERSION}) SetVersionNumber(HYPERION ${HYPERION_VERSION})
set(DEFAULT_JSON_CONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/config/hyperion.config.json.default)
file(READ ${DEFAULT_JSON_CONFIG_FILE} DEFAULT_JSON_CONFIG_VAR)
string(REPLACE "configVersionValue" ${HYPERION_VERSION} DEFAULT_JSON_CONFIG_VAR "${DEFAULT_JSON_CONFIG_VAR}")
string(REPLACE "previousVersionValue" ${HYPERION_VERSION} DEFAULT_JSON_CONFIG_VAR "${DEFAULT_JSON_CONFIG_VAR}")
file(WRITE ${CMAKE_BINARY_DIR}/config/hyperion.config.json.default "${DEFAULT_JSON_CONFIG_VAR}")
# Instruct CMake to run moc automatically when needed. # Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
@ -50,6 +55,7 @@ SET ( DEFAULT_USE_SYSTEM_PROTO_LIBS OFF )
SET ( DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF ) SET ( DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF )
SET ( DEFAULT_TESTS OFF ) SET ( DEFAULT_TESTS OFF )
SET ( DEFAULT_EXPERIMENTAL OFF ) SET ( DEFAULT_EXPERIMENTAL OFF )
SET ( DEFAULT_MF OFF )
SET ( DEFAULT_DEPLOY_DEPENDENCIES ON ) SET ( DEFAULT_DEPLOY_DEPENDENCIES ON )
IF ( ${CMAKE_SYSTEM} MATCHES "Linux" ) IF ( ${CMAKE_SYSTEM} MATCHES "Linux" )
@ -60,7 +66,8 @@ IF ( ${CMAKE_SYSTEM} MATCHES "Linux" )
SET ( DEFAULT_USB_HID ON ) SET ( DEFAULT_USB_HID ON )
SET ( DEFAULT_CEC ON ) SET ( DEFAULT_CEC ON )
ELSEIF ( WIN32 ) ELSEIF ( WIN32 )
SET ( DEFAULT_DX OFF ) SET ( DEFAULT_DX ON )
SET ( DEFAULT_MF ON )
ELSE() ELSE()
SET ( DEFAULT_V4L2 OFF ) SET ( DEFAULT_V4L2 OFF )
SET ( DEFAULT_FB OFF ) SET ( DEFAULT_FB OFF )
@ -122,6 +129,11 @@ elseif ( "${PLATFORM}" MATCHES "rpi" )
SET ( DEFAULT_WS281XPWM ON ) SET ( DEFAULT_WS281XPWM ON )
elseif ( "${PLATFORM}" STREQUAL "amlogic" ) elseif ( "${PLATFORM}" STREQUAL "amlogic" )
SET ( DEFAULT_AMLOGIC ON ) SET ( DEFAULT_AMLOGIC ON )
elseif ( "${PLATFORM}" STREQUAL "amlogic-dev" )
SET ( DEFAULT_AMLOGIC ON )
SET ( DEFAULT_DISPMANX OFF )
SET ( DEFAULT_QT OFF )
SET ( DEFAULT_CEC OFF )
elseif ( "${PLATFORM}" STREQUAL "amlogic64" ) elseif ( "${PLATFORM}" STREQUAL "amlogic64" )
SET ( DEFAULT_AMLOGIC ON ) SET ( DEFAULT_AMLOGIC ON )
elseif ( "${PLATFORM}" MATCHES "x11" ) elseif ( "${PLATFORM}" MATCHES "x11" )
@ -150,17 +162,18 @@ ADD_DEFINITIONS( ${PLATFORM_DEFINE} )
option(ENABLE_AMLOGIC "Enable the AMLOGIC video grabber" ${DEFAULT_AMLOGIC} ) option(ENABLE_AMLOGIC "Enable the AMLOGIC video grabber" ${DEFAULT_AMLOGIC} )
message(STATUS "ENABLE_AMLOGIC = ${ENABLE_AMLOGIC}") message(STATUS "ENABLE_AMLOGIC = ${ENABLE_AMLOGIC}")
option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ${DEFAULT_DISPMANX} )
message(STATUS "ENABLE_DISPMANX = ${ENABLE_DISPMANX}")
if (ENABLE_AMLOGIC) if (ENABLE_AMLOGIC)
SET(ENABLE_FB ON) SET(ENABLE_FB ON)
else() else()
option(ENABLE_FB "Enable the framebuffer grabber" ${DEFAULT_FB} ) option(ENABLE_FB "Enable the framebuffer grabber" ${DEFAULT_FB} )
endif() endif()
message(STATUS "ENABLE_FB = ${ENABLE_FB}") message(STATUS "ENABLE_FB = ${ENABLE_FB}")
option(ENABLE_OSX "Enable the osx grabber" ${DEFAULT_OSX} ) option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ${DEFAULT_DISPMANX} )
message(STATUS "ENABLE_DISPMANX = ${ENABLE_DISPMANX}")
option(ENABLE_OSX "Enable the OSX grabber" ${DEFAULT_OSX} )
message(STATUS "ENABLE_OSX = ${ENABLE_OSX}") message(STATUS "ENABLE_OSX = ${ENABLE_OSX}")
option(ENABLE_SPIDEV "Enable the SPIDEV device" ${DEFAULT_SPIDEV} ) option(ENABLE_SPIDEV "Enable the SPIDEV device" ${DEFAULT_SPIDEV} )
@ -172,6 +185,9 @@ message(STATUS "ENABLE_TINKERFORGE = ${ENABLE_TINKERFORGE}")
option(ENABLE_V4L2 "Enable the V4L2 grabber" ${DEFAULT_V4L2}) option(ENABLE_V4L2 "Enable the V4L2 grabber" ${DEFAULT_V4L2})
message(STATUS "ENABLE_V4L2 = ${ENABLE_V4L2}") message(STATUS "ENABLE_V4L2 = ${ENABLE_V4L2}")
option(ENABLE_MF "Enable the Media Foundation grabber" ${DEFAULT_MF})
message(STATUS "ENABLE_MF = ${ENABLE_MF}")
option(ENABLE_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_WS281XPWM} ) option(ENABLE_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_WS281XPWM} )
message(STATUS "ENABLE_WS281XPWM = ${ENABLE_WS281XPWM}") message(STATUS "ENABLE_WS281XPWM = ${ENABLE_WS281XPWM}")
@ -190,7 +206,7 @@ message(STATUS "ENABLE_X11 = ${ENABLE_X11}")
option(ENABLE_XCB "Enable the XCB grabber" ${DEFAULT_XCB}) option(ENABLE_XCB "Enable the XCB grabber" ${DEFAULT_XCB})
message(STATUS "ENABLE_XCB = ${ENABLE_XCB}") message(STATUS "ENABLE_XCB = ${ENABLE_XCB}")
option(ENABLE_QT "Enable the qt grabber" ${DEFAULT_QT}) option(ENABLE_QT "Enable the Qt grabber" ${DEFAULT_QT})
message(STATUS "ENABLE_QT = ${ENABLE_QT}") message(STATUS "ENABLE_QT = ${ENABLE_QT}")
option(ENABLE_DX "Enable the DirectX grabber" ${DEFAULT_DX}) option(ENABLE_DX "Enable the DirectX grabber" ${DEFAULT_DX})
@ -216,10 +232,7 @@ SET ( PROTOBUF_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/proto )
# check all json files # check all json files
FILE ( GLOB_RECURSE HYPERION_SCHEMAS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libsrc/*schema*.json ) FILE ( GLOB_RECURSE HYPERION_SCHEMAS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libsrc/*schema*.json )
SET( JSON_FILES SET( JSON_FILES ${CMAKE_BINARY_DIR}/config/hyperion.config.json.default ${HYPERION_SCHEMAS})
config/hyperion.config.json.default
${HYPERION_SCHEMAS}
)
EXECUTE_PROCESS ( EXECUTE_PROCESS (
COMMAND ${PYTHON_EXECUTABLE} test/jsonchecks/checkjson.py ${JSON_FILES} COMMAND ${PYTHON_EXECUTABLE} test/jsonchecks/checkjson.py ${JSON_FILES}
@ -243,7 +256,7 @@ ENDIF ()
# TODO on windows it can't resolve the path inside the file (Das System kann den angegebenen Pfad nicht finden: '\\schema\\schema-general.json') # TODO on windows it can't resolve the path inside the file (Das System kann den angegebenen Pfad nicht finden: '\\schema\\schema-general.json')
IF (NOT WIN32) IF (NOT WIN32)
EXECUTE_PROCESS ( EXECUTE_PROCESS (
COMMAND python test/jsonchecks/checkschema.py config/hyperion.config.json.default libsrc/hyperion/hyperion.schema.json COMMAND python test/jsonchecks/checkschema.py ${CMAKE_BINARY_DIR}/config/hyperion.config.json.default libsrc/hyperion/hyperion.schema.json
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE CHECK_CONFIG_FAILED RESULT_VARIABLE CHECK_CONFIG_FAILED
) )
@ -399,31 +412,6 @@ find_package(libusb-1.0 REQUIRED)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
add_definitions(${QT_DEFINITIONS}) add_definitions(${QT_DEFINITIONS})
# Add JPEG library
if (ENABLE_V4L2)
# Turbo JPEG
find_package(TurboJPEG)
if (TURBOJPEG_FOUND)
add_definitions(-DHAVE_TURBO_JPEG)
message( STATUS "Using Turbo JPEG library: ${TurboJPEG_LIBRARY}")
include_directories(${TurboJPEG_INCLUDE_DIRS})
else()
# System JPEG
find_package(JPEG)
if (JPEG_FOUND)
add_definitions(-DHAVE_JPEG)
message( STATUS "Using system JPEG library: ${JPEG_LIBRARIES}")
include_directories(${JPEG_INCLUDE_DIR})
else()
message( STATUS "JPEG library not found, MJPEG camera format won't work in V4L2 grabber.")
endif()
endif (TURBOJPEG_FOUND)
if (TURBOJPEG_FOUND OR JPEG_FOUND)
add_definitions(-DHAVE_JPEG_DECODER)
endif()
endif()
if(APPLE) if(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "-framework CoreGraphics") set(CMAKE_EXE_LINKER_FLAGS "-framework CoreGraphics")
endif() endif()

View File

@ -1,48 +1,51 @@
// Generated config file // Generated config file
// Define to enable the dispmanx grabber // Define to enable the DispmanX grabber
#cmakedefine ENABLE_DISPMANX #cmakedefine ENABLE_DISPMANX
// Define to enable the v4l2 grabber // Define to enable the V4L2 grabber
#cmakedefine ENABLE_V4L2 #cmakedefine ENABLE_V4L2
// Define to enable the framebuffer grabber // Define to enable the Media Foundation grabber
#cmakedefine ENABLE_MF
// Define to enable the Framebuffer grabber
#cmakedefine ENABLE_FB #cmakedefine ENABLE_FB
// Define to enable the amlogic grabber // Define to enable the AMLogic grabber
#cmakedefine ENABLE_AMLOGIC #cmakedefine ENABLE_AMLOGIC
// Define to enable the osx grabber // Define to enable the OSX grabber
#cmakedefine ENABLE_OSX #cmakedefine ENABLE_OSX
// Define to enable the x11 grabber // Define to enable the X11 grabber
#cmakedefine ENABLE_X11 #cmakedefine ENABLE_X11
// Define to enable the xcb grabber // Define to enable the XCB grabber
#cmakedefine ENABLE_XCB #cmakedefine ENABLE_XCB
// Define to enable the qt grabber // Define to enable the Qt grabber
#cmakedefine ENABLE_QT #cmakedefine ENABLE_QT
// Define to enable the DirectX grabber // Define to enable the DirectX grabber
#cmakedefine ENABLE_DX #cmakedefine ENABLE_DX
// Define to enable the spi-device // Define to enable the SPI-Device
#cmakedefine ENABLE_SPIDEV #cmakedefine ENABLE_SPIDEV
// Define to enable the ws281x-pwm-via-dma-device using jgarff's library // Define to enable the WS281x-PWM-via-DMA-device using jgarff's library
#cmakedefine ENABLE_WS281XPWM #cmakedefine ENABLE_WS281XPWM
// Define to enable the tinkerforge device // Define to enable the Tinkerforge device
#cmakedefine ENABLE_TINKERFORGE #cmakedefine ENABLE_TINKERFORGE
// Define to enable avahi // Define to enable AVAHI
#cmakedefine ENABLE_AVAHI #cmakedefine ENABLE_AVAHI
// Define to enable cec // Define to enable CEC
#cmakedefine ENABLE_CEC #cmakedefine ENABLE_CEC
// Define to enable the usb / hid devices // Define to enable the USB / HID devices
#cmakedefine ENABLE_USB_HID #cmakedefine ENABLE_USB_HID
// Define to enable profiler for development purpose // Define to enable profiler for development purpose

View File

@ -0,0 +1,24 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h3 class="page-header"><i class="fa fa-camera fa-fw"></i><span data-i18n="main_menu_grabber_conf_token">Capturing Hardware</span></h3>
<div class="panel panel-default" style="border:0px;">
<div class="panel-heading panel-instance" style="border-radius:3px; border-bottom:0px;">
<div class="dropdown">
<a id="active_instance_dropdown" class="dropdown-toggle" data-toggle="dropdown" href="#" style="text-decoration:none;display:flex;align-items:center;">
<div id="active_instance_friendly_name"></div>
<div id="btn_hypinstanceswitch" style="white-space:nowrap;"><span class="mdi mdi-lightbulb-group mdi-24px" style="margin-right:0; margin-left:5px;"></span><span class="mdi mdi-menu-down mdi-24px"></span></div>
</a>
<ul id="hyp_inst_listing" class="dropdown-menu dropdown-alerts" style="cursor:pointer;"></ul>
</div>
</div>
</div>
<div id="conf_cont"></div>
</div>
</div>
</div>
<script src="/js/content_instcapture.js"></script>

View File

@ -37,10 +37,10 @@
<div class="panel-body"> <div class="panel-body">
<div id="btn_wiz_holder"></div> <div id="btn_wiz_holder"></div>
<div id='editor_container_leddevice'></div> <div id='editor_container_leddevice'></div>
<div class="bs-callout bs-callout-info" style="margin-top:0px"><h4 data-i18n="dashboard_infobox_label_title">Information</h4><span data-i18n="conf_leds_device_info_log"> In case your LEDs do not work, check here for errors: </span> <a onclick="SwitchToMenuItem('MenuItemLogging')" href="#" data-i18n="main_menu_logging_token"></a></div> <div class="bs-callout bs-callout-info" style="margin-top:0px"><h4 data-i18n="dashboard_infobox_label_title">Information</h4><span data-i18n="conf_leds_device_info_log"> In case your LEDs do not work, check here for errors: </span> <a onclick="SwitchToMenuItem('MenuItemLogging')" data-i18n="main_menu_logging_token" style="cursor:pointer"></a></div>
</div> </div>
<div class="panel-footer" style="text-align:right"> <div class="panel-footer" style="text-align:right">
<button id='btn_test_controller' class="btn btn-primary hidden" disabled data-toggle="tooltip" data-placement="top" title="Identify configured device by lighting it up"> <button id='btn_test_controller' class="btn btn-primary" disabled data-toggle="tooltip" data-placement="top" title="Identify configured device by lighting it up">
<i class="fa fa-fw fa-save"></i><span data-i18n="wiz_identify">Identify/Test</span> <i class="fa fa-fw fa-save"></i><span data-i18n="wiz_identify">Identify/Test</span>
</button> </button>
<button id='btn_submit_controller' class="btn btn-primary" data-toggle="tooltip" data-placement="top" title="Save the device's connectivity configuration"> <button id='btn_submit_controller' class="btn btn-primary" data-toggle="tooltip" data-placement="top" title="Save the device's connectivity configuration">

View File

@ -16,6 +16,34 @@
<span id="dash_config_status">Status</span> <span id="dash_config_status">Status</span>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="table borderless">
<thead>
<tr>
<th colspan="3">
<i class="mdi mdi-camera"></i>
<span data-i18n="main_menu_grabber_conf_token">Capturing Hardware</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td data-i18n="edt_conf_fg_heading_title">Screen-Grabber</td>
<td style="text-align: right; padding-right: 0">
<span id="dash_screen_grabber">disabled</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemGrabber', 'editor_container_screengrabber')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr>
<tr>
<td></td>
<td data-i18n="edt_conf_v4l2_heading_title">Video-Grabber</td>
<td style="text-align: right; padding-right: 0">
<span id="dash_video_grabber">disabled</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemGrabber', 'editor_container_videograbber')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr>
</tbody>
</table>
<table class="table borderless"> <table class="table borderless">
<thead> <thead>
<tr> <tr>
@ -29,22 +57,34 @@
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_port_proto">proto</td> <td data-i18n="dashboard_infobox_label_port_proto">proto</td>
<td id="dash_pbPort" style="text-align:right">unknown</td> <td style="text-align: right; padding-right: 0">
<span id="dash_pbPort">unknown</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemNetwork', 'editor_container_protoserver')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_port_flat">flat</td> <td data-i18n="dashboard_infobox_label_port_flat">flat</td>
<td id="dash_fbPort" style="text-align:right">unknown</td> <td style="text-align: right; padding-right: 0">
<span id="dash_fbPort">unknown</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemNetwork', 'editor_container_fbserver')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_port_json">json</td> <td data-i18n="dashboard_infobox_label_port_json">json</td>
<td id="dash_jsonPort" style="text-align:right">unknown</td> <td style="text-align: right; padding-right: 0">
<span id="dash_jsonPort">unknown</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemNetwork', 'editor_container_jsonserver')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_ports_websocket">websocket</td> <td data-i18n="dashboard_infobox_label_ports_websocket">websocket</td>
<td id="dash_wsPorts" style="text-align:right">unknown</td> <td style="text-align: right; padding-right: 0">
<span id="dash_wsPorts">unknown</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemWeb')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -61,17 +101,17 @@
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_currenthyp">Hyperion version:</td> <td data-i18n="dashboard_infobox_label_currenthyp">Hyperion version:</td>
<td id="dash_currv" style="text-align:right">unknown</td> <td id="dash_currv" style="text-align: right">unknown</td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_watchedversionbranch">Watched version branch:</td> <td data-i18n="dashboard_infobox_label_watchedversionbranch">Watched version branch:</td>
<td id="dash_watchedversionbranch" style="text-align:right">unknown</td> <td id="dash_watchedversionbranch" style="text-align: right">unknown</td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_latesthyp">Latest version:</td> <td data-i18n="dashboard_infobox_label_latesthyp">Latest version:</td>
<td id="dash_latev" style="text-align:right">unknown</td> <td id="dash_latev" style="text-align: right">unknown</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -8,6 +8,7 @@
<div class="panel-body"> <div class="panel-body">
<form> <form>
<div class="form-group"> <div class="form-group">
<input type="hidden" name="username" class="form-control" type="text" id="username" value="hyperion"/>
<input name="password" class="form-control" type="password" id="password" placeholder="Password" autocomplete="off" /> <input name="password" class="form-control" type="password" id="password" placeholder="Password" autocomplete="off" />
<input name="show_pw" type="checkbox" id="show_pw" /><label for="show_pw">Show/Hide Password</label> <input name="show_pw" type="checkbox" id="show_pw" /><label for="show_pw">Show/Hide Password</label>
</div> </div>

View File

@ -24,14 +24,34 @@ body{
*/ */
/* fixed brand icon */ /* fixed brand icon */
@media (min-width: 768px){ @media (min-width: 768px){
.navbar-brand{position:fixed} #wrapper{
#main-nav{position:absolute !important;} padding-left: 10px;
padding-bottom: 10px;
padding-right: 10px;
}
.navbar-brand{
position:fixed;
height: 80px;
}
#main-nav{
position:absolute !important;
}
.navbar-top-links li a > .fa{
font-size: 22px;
}
} }
/*mobile nav*/ /*mobile nav*/
@media (max-width: 768px){ @media (max-width: 768px){
.navbar-toggle{position:fixed;right:0px;} #wrapper{
#main-nav{position:fixed;right:-200px;} padding: 0px;
}
.navbar-toggle{
position:fixed;right:0px;
}
#main-nav{
position:fixed;right:-200px;
}
} }
.navbar-toggle .icon-bar { .navbar-toggle .icon-bar {
background-color:#4c4c4c !important; background-color:#4c4c4c !important;
@ -72,23 +92,25 @@ table.input-group{width:100%}
@media (max-width: 767px) {.ltd{white-space:normal;}} @media (max-width: 767px) {.ltd{white-space:normal;}}
/*icon spacing*/ /*icon spacing*/
.fa-fw,.mdi-24px{margin-right:5px;} .fa-fw, .mdi-24px{margin-right:5px;}
/*table*/ /*table*/
table.borderless td,table.borderless th{border: none !important;} table.borderless td,table.borderless th{border: none !important;}
table.borderless td:first-child{width: 25px !important;}
.borderless {margin-bottom:0px} .borderless {margin-bottom:0px}
table label{margin:0} table label{margin:0}
table.first_cell_borderless td:first-child,table.first_cell_borderless th:first-child{border: none !important;} table.first_cell_borderless td:first-child,table.first_cell_borderless th:first-child{border: none !important;}
table.first_cell_borderless td:first-child{width: 25px !important;}
/*Header*/ /*Header*/
.navbar-brand{padding-top:4px;padding-bottom:0px;padding-left:2;} .navbar-brand{top:0px;left:17px;padding-top:0;}
.sidebar{margin-top:65px;} .sidebar{margin-top:85px;}
.dropdown{font-size:18px;} .dropdown{font-size:18px;}
@media (max-width: 767px) {.sidebar{margin-top:0px;padding-top:0px !important;}} @media (max-width: 767px) {.sidebar{margin-top:0px;padding-top:0px !important;}}
.page-header{margin-top:0px;} .page-header{margin-top:0px;}
.navbar-top-links li a { .navbar-top-links li a {
min-height: 64px; min-height: 84px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
@ -216,6 +238,7 @@ table.first_cell_borderless td:first-child,table.first_cell_borderless th:first-
.btn-transparent { .btn-transparent {
background-color: transparent; background-color: transparent;
border-style: none; border-style: none;
padding-left: 0;
} }
/*general instance management button*/ /*general instance management button*/

View File

@ -274,6 +274,9 @@
"edt_conf_enum_NTSC": "NTSC", "edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_PAL": "PAL", "edt_conf_enum_PAL": "PAL",
"edt_conf_enum_SECAM": "SECAM", "edt_conf_enum_SECAM": "SECAM",
"edt_conf_enum_HORIZONTAL": "Horizontal",
"edt_conf_enum_VERTICAL": "Vertikal",
"edt_conf_enum_BOTH": "Horizontal & Vertikal",
"edt_conf_enum_automatic": "Automatisch", "edt_conf_enum_automatic": "Automatisch",
"edt_conf_enum_bbclassic": "Klassisch", "edt_conf_enum_bbclassic": "Klassisch",
"edt_conf_enum_bbdefault": "Standard", "edt_conf_enum_bbdefault": "Standard",
@ -442,6 +445,8 @@
"edt_conf_v4l2_sizeDecimation_title": "Bildverkleinerung Faktor", "edt_conf_v4l2_sizeDecimation_title": "Bildverkleinerung Faktor",
"edt_conf_v4l2_standard_expl": "Wähle das passende Videoformat deiner Region. Auf 'Automatisch' wird der gewählte Modus vom v4l interface beibehalten.", "edt_conf_v4l2_standard_expl": "Wähle das passende Videoformat deiner Region. Auf 'Automatisch' wird der gewählte Modus vom v4l interface beibehalten.",
"edt_conf_v4l2_standard_title": "Videoformat", "edt_conf_v4l2_standard_title": "Videoformat",
"edt_conf_v4l2_flip_expl": "Hiermit kannst du das Bild in horizontaler, vertikaler oder beiden Richtung spiegeln.",
"edt_conf_v4l2_flip_title": "Spiegelung",
"edt_conf_webc_crtPath_expl": "Pfad zur Zertifikats-Datei (Format sollte PEM sein)", "edt_conf_webc_crtPath_expl": "Pfad zur Zertifikats-Datei (Format sollte PEM sein)",
"edt_conf_webc_crtPath_title": "Zertifikats-Pfad", "edt_conf_webc_crtPath_title": "Zertifikats-Pfad",
"edt_conf_webc_docroot_expl": "Lokaler Pfad zum WebUI Wurzelverzeichnis (Nur für WebUI Entwickler)", "edt_conf_webc_docroot_expl": "Lokaler Pfad zum WebUI Wurzelverzeichnis (Nur für WebUI Entwickler)",

View File

@ -5,11 +5,13 @@
"InfoDialog_changePassword_title": "Change Password", "InfoDialog_changePassword_title": "Change Password",
"InfoDialog_iswitch_text": "If you run Hyperion more than once in your local network, you could switch between the web configurations. Select the Hyperion instance below and switch!", "InfoDialog_iswitch_text": "If you run Hyperion more than once in your local network, you could switch between the web configurations. Select the Hyperion instance below and switch!",
"InfoDialog_iswitch_title": "Hyperion switcher", "InfoDialog_iswitch_title": "Hyperion switcher",
"InfoDialog_lang_text": "If you don't like the result of the automatic language detection you could overwrite it here.", "InfoDialog_nostorage_text": "Your browser does not support localStorage. You cannot save a specific language setting (fallback to 'auto detection') and access level (fallback to 'default'). Some wizards may be hidden. You could still use the webinterface without further issues",
"InfoDialog_lang_title": "Language setting", "InfoDialog_nostorage_title": "Cannot store settings",
"InfoDialog_nowrite_foottext": "The WebUI will be unlocked automatically after you solved the problem!", "InfoDialog_nowrite_foottext": "The WebUI will be unlocked automatically after you solved the problem!",
"InfoDialog_nowrite_text": "Hyperion can't write to your current loaded configuration file. Please repair the file permissions to proceed.", "InfoDialog_nowrite_text": "Hyperion can't write to your current loaded configuration file. Please repair the file permissions to proceed.",
"InfoDialog_nowrite_title": "write permission error!", "InfoDialog_nowrite_title": "write permission error!",
"infoDialog_password_current_text": "Current password",
"infoDialog_password_new_text": "New password",
"about_3rd_party_licenses": "3rd party licenses", "about_3rd_party_licenses": "3rd party licenses",
"about_3rd_party_licenses_error": "We had trouble collecting 3rd party licenses information from web. <br />Please follow this link to the GitHub Resource.", "about_3rd_party_licenses_error": "We had trouble collecting 3rd party licenses information from web. <br />Please follow this link to the GitHub Resource.",
"about_build": "Build", "about_build": "Build",
@ -42,7 +44,8 @@
"conf_general_intro": "Basic settings around Hyperion and WebUI that don't fit into another category.", "conf_general_intro": "Basic settings around Hyperion and WebUI that don't fit into another category.",
"conf_general_label_title": "General settings", "conf_general_label_title": "General settings",
"conf_grabber_fg_intro": "Screen capture is your local system capture as input source, Hyperion is installed on.", "conf_grabber_fg_intro": "Screen capture is your local system capture as input source, Hyperion is installed on.",
"conf_grabber_v4l_intro": "USB capture is a (capture)device connected via USB which is used to input source pictures for processing.", "conf_grabber_inst_grabber_config_info": "Configure your capturing hardware devices to be used by the instance in advance",
"conf_grabber_v4l_intro": "USB capture is a (capture) device connected via USB which is used to input source pictures for processing.",
"conf_helptable_expl": "Explanation", "conf_helptable_expl": "Explanation",
"conf_helptable_option": "Option", "conf_helptable_option": "Option",
"conf_leds_config_error": "Error in LED/LED layout configuration", "conf_leds_config_error": "Error in LED/LED layout configuration",
@ -127,10 +130,12 @@
"conf_leds_optgroup_RPiGPIO": "RPi GPIO", "conf_leds_optgroup_RPiGPIO": "RPi GPIO",
"conf_leds_optgroup_RPiPWM": "RPi PWM", "conf_leds_optgroup_RPiPWM": "RPi PWM",
"conf_leds_optgroup_RPiSPI": "RPi SPI", "conf_leds_optgroup_RPiSPI": "RPi SPI",
"conf_leds_optgroup_debug": "Debug",
"conf_leds_optgroup_network": "Network", "conf_leds_optgroup_network": "Network",
"conf_leds_optgroup_other": "Other", "conf_leds_optgroup_other": "Other",
"conf_leds_optgroup_usb": "USB/Serial", "conf_leds_optgroup_usb": "USB/Serial",
"conf_logging_btn_autoscroll": "Auto scrolling", "conf_logging_btn_autoscroll": "Auto scrolling",
"conf_logging_btn_clipboard": "Copy Log to Clipboard",
"conf_logging_btn_pbupload": "Upload a report for support requests", "conf_logging_btn_pbupload": "Upload a report for support requests",
"conf_logging_contpolicy": "Report Privacy Policy", "conf_logging_contpolicy": "Report Privacy Policy",
"conf_logging_label_intro": "Area to check log messages, you will see more or less information depending on the logging level set.", "conf_logging_label_intro": "Area to check log messages, you will see more or less information depending on the logging level set.",
@ -274,6 +279,9 @@
"edt_conf_enum_NTSC": "NTSC", "edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_PAL": "PAL", "edt_conf_enum_PAL": "PAL",
"edt_conf_enum_SECAM": "SECAM", "edt_conf_enum_SECAM": "SECAM",
"edt_conf_enum_HORIZONTAL": "Horizontal",
"edt_conf_enum_VERTICAL": "Vertical",
"edt_conf_enum_BOTH": "Horizontal & Vertical",
"edt_conf_enum_automatic": "Automatic", "edt_conf_enum_automatic": "Automatic",
"edt_conf_enum_bbclassic": "Classic", "edt_conf_enum_bbclassic": "Classic",
"edt_conf_enum_bbdefault": "Default", "edt_conf_enum_bbdefault": "Default",
@ -358,11 +366,20 @@
"edt_conf_general_port_title": "Port", "edt_conf_general_port_title": "Port",
"edt_conf_general_priority_expl": "The priority of this component", "edt_conf_general_priority_expl": "The priority of this component",
"edt_conf_general_priority_title": "Priority channel", "edt_conf_general_priority_title": "Priority channel",
"edt_conf_instC_systemEnable_expl": "Enables the screen capture for this LED hardware instance", "edt_conf_grabber_discovered_expl": "Select your capture device discovered",
"edt_conf_grabber_discovered_none": "No capture device discovered",
"edt_conf_grabber_discovered_title": "Device discovered",
"edt_conf_grabber_discovered_title_info": "Select your capture device discovered",
"edt_conf_grabber_discovery_inprogress": "Discovery in progress",
"edt_conf_instC_screen_grabber_device_expl": "The screen capture device used",
"edt_conf_instC_screen_grabber_device_title": "Screen capture device",
"edt_conf_instC_systemEnable_expl": "Enables the screen capture for this led hardware instance",
"edt_conf_instC_systemEnable_title": "Enable screen capture", "edt_conf_instC_systemEnable_title": "Enable screen capture",
"edt_conf_instC_v4lEnable_expl": "Enables the USB capture for this LED hardware instance", "edt_conf_instC_v4lEnable_expl": "Enables the USB capture for this LED hardware instance",
"edt_conf_instC_v4lEnable_title": "Enable USB capture", "edt_conf_instC_v4lEnable_title": "Enable USB capture",
"edt_conf_instCapture_heading_title": "Instance Capture", "edt_conf_instC_video_grabber_device_expl": "The video capture device used",
"edt_conf_instC_video_grabber_device_title": "Video capture device",
"edt_conf_instCapture_heading_title": "Capture Devices",
"edt_conf_js_heading_title": "JSON Server", "edt_conf_js_heading_title": "JSON Server",
"edt_conf_log_heading_title": "Logging", "edt_conf_log_heading_title": "Logging",
"edt_conf_log_level_expl": "Depending on loglevel you see less or more messages in your log.", "edt_conf_log_level_expl": "Depending on loglevel you see less or more messages in your log.",
@ -415,6 +432,8 @@
"edt_conf_v4l2_cropRight_title": "Crop right", "edt_conf_v4l2_cropRight_title": "Crop right",
"edt_conf_v4l2_cropTop_expl": "Count of pixels on the top side that are removed from the picture.", "edt_conf_v4l2_cropTop_expl": "Count of pixels on the top side that are removed from the picture.",
"edt_conf_v4l2_cropTop_title": "Crop top", "edt_conf_v4l2_cropTop_title": "Crop top",
"edt_conf_v4l2_cropWidthValidation_error": "Crop left + Crop right cannot be greater than Width ($1)",
"edt_conf_v4l2_cropHeightValidation_error": "Crop top + Crop bottom cannot be greater than Height ($1)",
"edt_conf_v4l2_device_expl": "The path to the USB capture interface. Set to 'Automatic' for automatic detection. Example: '/dev/video0'", "edt_conf_v4l2_device_expl": "The path to the USB capture interface. Set to 'Automatic' for automatic detection. Example: '/dev/video0'",
"edt_conf_v4l2_device_title": "Device", "edt_conf_v4l2_device_title": "Device",
"edt_conf_v4l2_framerate_expl": "The supported frames per second of the active device", "edt_conf_v4l2_framerate_expl": "The supported frames per second of the active device",
@ -442,6 +461,24 @@
"edt_conf_v4l2_sizeDecimation_title": "Size decimation", "edt_conf_v4l2_sizeDecimation_title": "Size decimation",
"edt_conf_v4l2_standard_expl": "Select the video standard for your region. 'Automatic' keeps the value chosen by the v4l2 interface.", "edt_conf_v4l2_standard_expl": "Select the video standard for your region. 'Automatic' keeps the value chosen by the v4l2 interface.",
"edt_conf_v4l2_standard_title": "Video standard", "edt_conf_v4l2_standard_title": "Video standard",
"edt_conf_v4l2_flip_expl": "This allows you to flip the image horizontally, vertically, or both.",
"edt_conf_v4l2_flip_title": "Image flip",
"edt_conf_v4l2_fpsSoftwareDecimation_title": "Software frame skipping",
"edt_conf_v4l2_fpsSoftwareDecimation_expl": "To save resources every n'th frame will be processed only. For ex. if grabber is set to 30FPS with this option set to 5 the final result will be around 6FPS (1 - disabled)",
"edt_conf_v4l2_encoding_title": "Encoding format",
"edt_conf_v4l2_encoding_expl": "Force video encoding for multiformat capable grabbers",
"edt_conf_v4l2_hardware_brightness_title": "Hardware brightness control",
"edt_conf_v4l2_hardware_brightness_expl": "Set hardware brightness if device supports it, check logs (0=disabled)",
"edt_conf_v4l2_hardware_contrast_title": "Hardware contrast control",
"edt_conf_v4l2_hardware_contrast_expl": "Set hardware contrast if device supports it, check logs (0=disabled)",
"edt_conf_v4l2_hardware_hue_title": "Hardware hue control",
"edt_conf_v4l2_hardware_hue_expl": "Set hardware hue if device supports it, check logs (0=disabled)",
"edt_conf_v4l2_hardware_saturation_title": "Hardware saturation control",
"edt_conf_v4l2_hardware_saturation_expl": "Set hardware saturation if device supports it, check logs (0=disabled)",
"edt_conf_v4l2_hardware_set_defaults": "Default hardware controls",
"edt_conf_v4l2_hardware_set_defaults_tip": "Set device's default values for brightness, contrast, hue and saturation",
"edt_conf_v4l2_noSignalCounterThreshold_title": "Signal Counter Threshold",
"edt_conf_v4l2_noSignalCounterThreshold_expl": "Count of frames (check that with grabber's current FPS mode) after which the no signal is triggered",
"edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)", "edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)",
"edt_conf_webc_crtPath_title": "Certificate path", "edt_conf_webc_crtPath_title": "Certificate path",
"edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)", "edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)",
@ -607,6 +644,7 @@
"edt_eff_height": "Height", "edt_eff_height": "Height",
"edt_eff_huechange": "Color change", "edt_eff_huechange": "Color change",
"edt_eff_image": "Image file", "edt_eff_image": "Image file",
"edt_eff_initial_blink" : "Flash for attention",
"edt_eff_interval": "Interval", "edt_eff_interval": "Interval",
"edt_eff_knightrider_header": "Knight Rider", "edt_eff_knightrider_header": "Knight Rider",
"edt_eff_knightrider_header_desc": "K.I.T.T is back! The front-scanner of the well known car, this time not just in red.", "edt_eff_knightrider_header_desc": "K.I.T.T is back! The front-scanner of the well known car, this time not just in red.",
@ -644,6 +682,7 @@
"edt_eff_reversedirection": "Reverse direction", "edt_eff_reversedirection": "Reverse direction",
"edt_eff_rotationtime": "Rotation time", "edt_eff_rotationtime": "Rotation time",
"edt_eff_saturation": "Saturation", "edt_eff_saturation": "Saturation",
"edt_eff_set_post_color" : "Set post color after alam",
"edt_eff_showseconds": "Show seconds", "edt_eff_showseconds": "Show seconds",
"edt_eff_sleeptime": "Sleep time", "edt_eff_sleeptime": "Sleep time",
"edt_eff_smooth_custom": "Enable smoothing", "edt_eff_smooth_custom": "Enable smoothing",
@ -748,11 +787,11 @@
"general_comp_BOBLIGHTSERVER": "Boblight Server", "general_comp_BOBLIGHTSERVER": "Boblight Server",
"general_comp_FLATBUFSERVER": "Flatbuffers Server", "general_comp_FLATBUFSERVER": "Flatbuffers Server",
"general_comp_FORWARDER": "Forwarder", "general_comp_FORWARDER": "Forwarder",
"general_comp_GRABBER": "Screen Capture", "general_comp_GRABBER": "Capture Screen",
"general_comp_LEDDEVICE": "LED device", "general_comp_LEDDEVICE": "LED Output",
"general_comp_PROTOSERVER": "Protocol Buffers Server", "general_comp_PROTOSERVER": "Protocol Buffers Server",
"general_comp_SMOOTHING": "Smoothing", "general_comp_SMOOTHING": "Smoothing",
"general_comp_V4L": "USB Capture", "general_comp_V4L": "Capture USB-Input",
"general_country_cn": "China", "general_country_cn": "China",
"general_country_de": "Germany", "general_country_de": "Germany",
"general_country_es": "Spain", "general_country_es": "Spain",
@ -812,6 +851,7 @@
"main_ledsim_btn_togglelednumber": "LED numbers", "main_ledsim_btn_togglelednumber": "LED numbers",
"main_ledsim_btn_toggleleds": "Show LEDs", "main_ledsim_btn_toggleleds": "Show LEDs",
"main_ledsim_btn_togglelivevideo": "Live video", "main_ledsim_btn_togglelivevideo": "Live video",
"main_ledsim_btn_togglesigdetect": "Signal detection area",
"main_ledsim_text": "Live visualization of LED colors and optional the current video stream of your capture device.", "main_ledsim_text": "Live visualization of LED colors and optional the current video stream of your capture device.",
"main_ledsim_title": "LED Visualization", "main_ledsim_title": "LED Visualization",
"main_menu_about_token": "About Hyperion", "main_menu_about_token": "About Hyperion",
@ -823,7 +863,8 @@
"main_menu_general_conf_token": "General", "main_menu_general_conf_token": "General",
"main_menu_grabber_conf_token": "Capturing Hardware", "main_menu_grabber_conf_token": "Capturing Hardware",
"main_menu_input_selection_token": "Input Selection", "main_menu_input_selection_token": "Input Selection",
"main_menu_leds_conf_token": "LED Hardware", "main_menu_instcapture_conf_token": "Sources",
"main_menu_leds_conf_token": "LED Output",
"main_menu_logging_token": "Log", "main_menu_logging_token": "Log",
"main_menu_network_conf_token": "Network Services", "main_menu_network_conf_token": "Network Services",
"main_menu_remotecontrol_token": "Remote Control", "main_menu_remotecontrol_token": "Remote Control",
@ -956,6 +997,7 @@
"wiz_hue_title": "Philips Hue Wizard", "wiz_hue_title": "Philips Hue Wizard",
"wiz_hue_username": "User ID:", "wiz_hue_username": "User ID:",
"wiz_identify": "Identify", "wiz_identify": "Identify",
"wiz_identify_tip": "Identify configured device by lighting it up",
"wiz_identify_light": "Identify $1", "wiz_identify_light": "Identify $1",
"wiz_ids_disabled": "Deactivated", "wiz_ids_disabled": "Deactivated",
"wiz_ids_entire": "Whole picture", "wiz_ids_entire": "Whole picture",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -3,8 +3,7 @@
<head> <head>
<script> <script>
if (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent)) if (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent)) {
{
window.location.pathname = '/content/ie_not_supported.html'; window.location.pathname = '/content/ie_not_supported.html';
} }
</script> </script>
@ -52,6 +51,8 @@
<script src="js/lib/jquery.i18n/jquery.i18n.language.js"></script> <script src="js/lib/jquery.i18n/jquery.i18n.language.js"></script>
<script src="js/lib/jquery.i18n/CLDRPluralRuleParser.js"></script> <script src="js/lib/jquery.i18n/CLDRPluralRuleParser.js"></script>
<script src="js/languages.js"></script>
<!-- Bootstrap Core CSS --> <!-- Bootstrap Core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/bootstrap.min.css" rel="stylesheet">
@ -83,21 +84,24 @@
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
<noscript> <noscript>
<div style="color:red;margin: 40px 0;text-align:center"> <div style="color:red;margin: 40px 0;text-align:center">
<img src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!"> <img src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!">
<h3>Hyperion Web Configuration requires Javascript. Please enable Javascript in your browser for this page in order to use it!</h3> <h3>Hyperion Web Configuration requires Javascript. Please enable Javascript in your browser for this page in order to use it!</h3>
</div> </div>
<style type="text/css"> #loading_overlay, #wrapper{ display: none } </style> <style type="text/css">
#loading_overlay, #wrapper {
display: none
}
</style>
</noscript> </noscript>
<div id="loading_overlay" class="overlay"></div> <div id="loading_overlay" class="overlay"></div>
<div id="wrapper"> <div id="wrapper">
<!-- Navigation --> <!-- Navigation -->
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0; min-height: 65px"> <nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0; min-height: 85px">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle closed" aria-controls="navbar"> <button type="button" class="navbar-toggle closed" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">Toggle navigation</span>
@ -105,7 +109,7 @@
<span class="icon-bar middle-bar"></span> <span class="icon-bar middle-bar"></span>
<span class="icon-bar bottom-bar"></span> <span class="icon-bar bottom-bar"></span>
</button> </button>
<a class="navbar-brand" href="https://www.hyperion-project.org" target="_blank"><img id="navbar_brand_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" height="60"></a> <a class="navbar-brand" href="https://www.hyperion-project.org" target="_blank"><img id="navbar_brand_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" height="80"></a>
</div> </div>
<!-- /.navbar-header --> <!-- /.navbar-header -->
@ -166,8 +170,8 @@
<li id="btn_setlang"> <li id="btn_setlang">
<a> <a>
<div> <div>
<i class="fa fa-globe"></i> <i class="fa fa-globe fa-fw"></i>
<select id="language-select" class="selectpicker" data-width="fit" data-style="btn-transparent" > </select> <select id="language-select" class="selectpicker" data-width="fit" data-style="btn-transparent"> </select>
</div> </div>
</a> </a>
</li> </li>
@ -207,22 +211,23 @@
<li> <a class="inactive mnava" href="#dashboard"><i class="fa fa-dashboard fa-fw"></i><span data-i18n="main_menu_dashboard_token">Dashboard</span></a> </li> <li> <a class="inactive mnava" href="#dashboard"><i class="fa fa-dashboard fa-fw"></i><span data-i18n="main_menu_dashboard_token">Dashboard</span></a> </li>
<li> <a class="inactive mnava" href="#conf_general"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_general_conf_token">General</span></a> </li> <li> <a class="inactive mnava" href="#conf_general"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_general_conf_token">General</span></a> </li>
<li> <li>
<a class="inactive"><i class="fa fa-cog fa-fw"></i><span data-i18n="main_menu_configuration_token">Configuration</span><span class="fa arrow"></span></a> <a class="inactive"><i class="fa fa-cog fa-fw"></i><span data-i18n="main_menu_configuration_token">LED-Instances</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li> <a class="inactive mnava" href="#conf_leds"><i class="mdi mdi-lightbulb-on fa-fw"></i><span data-i18n="main_menu_leds_conf_token">LED Hardware</span></a> </li> <li> <a class="inactive mnava" id="MenuItemLeds" href="#conf_leds"><i class="mdi mdi-lightbulb-on fa-fw"></i><span data-i18n="main_menu_leds_conf_token">LED Hardware</span></a> </li>
<li> <a class="inactive mnava" id="MenuItemInstCapture" href="#conf_instcapture"><i class="fa fa-camera fa-fw"></i><span data-i18n="main_menu_instcapture_conf_token">Sources</span></a> </li>
<li> <a class="inactive mnava" href="#conf_effect"><i class="fa fa-spinner fa-fw"></i><span data-i18n="main_menu_effect_conf_token">Effects</span></a> </li> <li> <a class="inactive mnava" href="#conf_effect"><i class="fa fa-spinner fa-fw"></i><span data-i18n="main_menu_effect_conf_token">Effects</span></a> </li>
<li> <a class="inactive mnava" href="#conf_colors"><i class="fa fa-photo fa-fw"></i><span data-i18n="main_menu_colors_conf_token">Image Processing</span></a> </li> <li> <a class="inactive mnava" href="#conf_colors"><i class="fa fa-photo fa-fw"></i><span data-i18n="main_menu_colors_conf_token">Image Processing</span></a> </li>
</ul> </ul>
</li> </li>
<li> <a class="inactive mnava" href="#conf_grabber"><i class="fa fa-camera fa-fw"></i><span data-i18n="main_menu_grabber_conf_token">Capturing Hardware</span></a> </li> <li> <a class="inactive mnava" id="MenuItemGrabber" href="#conf_grabber"><i class="fa fa-camera fa-fw"></i><span data-i18n="main_menu_grabber_conf_token">Capturing Hardware</span></a> </li>
<li> <a class="inactive mnava" href="#conf_network"><i class="fa fa-sitemap fa-fw"></i><span data-i18n="main_menu_network_conf_token">Network</span></a> </li> <li> <a class="inactive mnava" id="MenuItemNetwork" href="#conf_network"><i class="fa fa-sitemap fa-fw"></i><span data-i18n="main_menu_network_conf_token">Network</span></a> </li>
<li> <a class="inactive mnava" href="#remote"><i class="fa fa-wifi fa-fw"></i><span data-i18n="main_menu_remotecontrol_token">Remote Control</span></a> </li> <li> <a class="inactive mnava" href="#remote"><i class="fa fa-wifi fa-fw"></i><span data-i18n="main_menu_remotecontrol_token">Remote Control</span></a> </li>
<li> <a class="inactive mnava" href="#effects_configurator"><i class="fa fa-cogs fa-fw"></i><span data-i18n="main_menu_effectsconfigurator_token">Effects Configurator</span></a> </li> <li> <a class="inactive mnava" href="#effects_configurator"><i class="fa fa-cogs fa-fw"></i><span data-i18n="main_menu_effectsconfigurator_token">Effects Configurator</span></a> </li>
<li> <a class="inactive mnava" href="#support"><i class="fa fa-info fa-fw"></i><span data-i18n="main_menu_support_token">Support</span></a> </li> <li> <a class="inactive mnava" href="#support"><i class="fa fa-info fa-fw"></i><span data-i18n="main_menu_support_token">Support</span></a> </li>
<li> <li>
<a class="inactive"><i class="fa fa-industry fa-fw"></i><span data-i18n="main_menu_system_token">System</span><span class="fa arrow"></span></a> <a class="inactive"><i class="fa fa-industry fa-fw"></i><span data-i18n="main_menu_system_token">System</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li> <a class="inactive mnava" href="#conf_webconfig" id="load_webconfig"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_webconfig_token">Webconfiguration</span></a> </li> <li> <a class="inactive mnava" id="MenuItemWeb" href="#conf_webconfig" id="load_webconfig"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_webconfig_token">Webconfiguration</span></a> </li>
<li> <a class="inactive mnava" id="MenuItemLogging" href="#conf_logging"><i class="fa fa-reorder fa-fw"></i><span data-i18n="main_menu_logging_token">Log</span></a> </li> <li> <a class="inactive mnava" id="MenuItemLogging" href="#conf_logging"><i class="fa fa-reorder fa-fw"></i><span data-i18n="main_menu_logging_token">Log</span></a> </li>
<li> <a class="inactive mnava" href="#update"><i class="fa fa-download fa-fw"></i><span data-i18n="main_menu_update_token">Update</span></a> </li> <li> <a class="inactive mnava" href="#update"><i class="fa fa-download fa-fw"></i><span data-i18n="main_menu_update_token">Update</span></a> </li>
<li> <a class="inactive mnava" href="#about"><i class="fa fa-info-circle fa-fw"></i><span data-i18n="main_menu_about_token">About</span></a> </li> <li> <a class="inactive mnava" href="#about"><i class="fa fa-info-circle fa-fw"></i><span data-i18n="main_menu_about_token">About</span></a> </li>
@ -285,7 +290,7 @@
<div id="wizp1"> <div id="wizp1">
<div class="modal-body" style="text-align:center"> <div class="modal-body" style="text-align:center">
<img id="wizard_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" style="margin-bottom:15px"> <img id="wizard_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" style="margin-bottom:15px">
<div id="wizp1_body" ></div> <div id="wizp1_body"></div>
</div> </div>
<div id="wizp1_footer" class="modal-footer" style="text-align:center"></div> <div id="wizp1_footer" class="modal-footer" style="text-align:center"></div>
</div> </div>
@ -320,6 +325,7 @@
<div data-role="footer" style="text-align:center"> <div data-role="footer" style="text-align:center">
<button type="button" class="btn btn-success" id="leds_toggle"><i class="fa fa-fw fa-lightbulb-o"></i><span data-i18n="main_ledsim_btn_toggleleds">leds</span></button> <button type="button" class="btn btn-success" id="leds_toggle"><i class="fa fa-fw fa-lightbulb-o"></i><span data-i18n="main_ledsim_btn_toggleleds">leds</span></button>
<button type="button" class="btn btn-danger" id="leds_toggle_num"> <i class="fa fa-fw fa-info"></i><span data-i18n="main_ledsim_btn_togglelednumber">led numbers</span></button> <button type="button" class="btn btn-danger" id="leds_toggle_num"> <i class="fa fa-fw fa-info"></i><span data-i18n="main_ledsim_btn_togglelednumber">led numbers</span></button>
<button type="button" class="btn btn-danger" id="sigDetectArea_toggle"><i class="fa fa-fw fa-info"></i><span data-i18n="main_ledsim_btn_togglesigdetect">signal detection area</span></button>
<button type="button" class="btn btn-danger" id="leds_toggle_live_video"><i class="fa fa-fw fa-television"></i><span data-i18n="main_ledsim_btn_togglelivevideo">live video</span></button> <button type="button" class="btn btn-danger" id="leds_toggle_live_video"><i class="fa fa-fw fa-television"></i><span data-i18n="main_ledsim_btn_togglelivevideo">live video</span></button>
</div> </div>
</div> </div>

View File

@ -13,7 +13,6 @@ $(document).ready(function () {
instances_html += '</a><ul id="hyp_inst_listing" class="dropdown-menu dropdown-alerts" style="cursor:pointer;"></ul>' instances_html += '</a><ul id="hyp_inst_listing" class="dropdown-menu dropdown-alerts" style="cursor:pointer;"></ul>'
instances_html += '</div></div>'; instances_html += '</div></div>';
instances_html += '<div class="panel-body">'; instances_html += '<div class="panel-body">';
instances_html += '<table class="table borderless">'; instances_html += '<table class="table borderless">';
instances_html += '<thead><tr><th style="vertical-align:middle"><i class="mdi mdi-lightbulb-on fa-fw"></i>'; instances_html += '<thead><tr><th style="vertical-align:middle"><i class="mdi mdi-lightbulb-on fa-fw"></i>';
@ -39,10 +38,13 @@ $(document).ready(function () {
instances_html += '<i class="fa fa-info-circle fa-fw"></i>'; instances_html += '<i class="fa fa-info-circle fa-fw"></i>';
instances_html += '<span>' + $.i18n('dashboard_infobox_label_title') + '</span>'; instances_html += '<span>' + $.i18n('dashboard_infobox_label_title') + '</span>';
instances_html += '</th></tr></thead>'; instances_html += '</th></tr></thead>';
instances_html += '<tbody><tr><td></td>'; instances_html += '<tbody>';
instances_html += '<td>' + $.i18n('conf_leds_contr_label_contrtype') + '</td>'; instances_html += '<tr><td></td><td>' + $.i18n('conf_leds_contr_label_contrtype') + '</td>';
instances_html += '<td style="text-align:right">' + window.serverConfig.device.type + '</td>'; instances_html += '<td style="text-align:right; padding-right:0">';
instances_html += '</tr><tr></tbody></table>'; instances_html += '<span>' + window.serverConfig.device.type + '</span>';
instances_html += '<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem(\'MenuItemLeds\')" style="text-decoration:none;cursor:pointer"></a>';
instances_html += '</td></tr>';
instances_html += '</tbody></table>';
instances_html += '<table class="table first_cell_borderless">'; instances_html += '<table class="table first_cell_borderless">';
instances_html += '<thead><tr><th colspan="3">'; instances_html += '<thead><tr><th colspan="3">';
@ -50,12 +52,18 @@ $(document).ready(function () {
instances_html += '<span>' + $.i18n('dashboard_componentbox_label_title') + '</span>'; instances_html += '<span>' + $.i18n('dashboard_componentbox_label_title') + '</span>';
instances_html += '</th></tr></thead>'; instances_html += '</th></tr></thead>';
var tab_components = ""; var componentBtn = "";
var instance_components = "";
for (var idx = 0; idx < components.length; idx++) { for (var idx = 0; idx < components.length; idx++) {
if (components[idx].name != "ALL") { if (components[idx].name != "ALL") {
if ((components[idx].name === "FORWARDER" && window.currentHyperionInstance != 0) ||
(components[idx].name === "GRABBER" && !window.serverConfig.framegrabber.enable) ||
(components[idx].name === "V4L" && !window.serverConfig.grabberV4L2.enable))
continue;
var comp_enabled = components[idx].enabled ? "checked" : ""; var comp_enabled = components[idx].enabled ? "checked" : "";
const general_comp = "general_comp_" + components[idx].name; const general_comp = "general_comp_" + components[idx].name;
var componentBtn = '<input ' + componentBtn = '<input ' +
'id="' + general_comp + '" ' + comp_enabled + 'id="' + general_comp + '" ' + comp_enabled +
' type="checkbox" ' + ' type="checkbox" ' +
'data-toggle="toggle" ' + 'data-toggle="toggle" ' +
@ -64,14 +72,15 @@ $(document).ready(function () {
'data-on="' + $.i18n('general_btn_on') + '" ' + 'data-on="' + $.i18n('general_btn_on') + '" ' +
'data-off="' + $.i18n('general_btn_off') + '">'; 'data-off="' + $.i18n('general_btn_off') + '">';
tab_components += '<tr><td></td><td>' + $.i18n('general_comp_' + components[idx].name) + '</td><td style="text-align:right">' + componentBtn + '</td></tr>'; instance_components += '<tr><td></td><td>' + $.i18n('general_comp_' + components[idx].name) + '</td><td style="text-align:right">' + componentBtn + '</td></tr>';
} }
} }
instances_html += '<tbody>' + tab_components + '</tbody></table>'; instances_html += '<tbody>' + instance_components + '</tbody></table>';
instances_html += '</div></div></div>'; instances_html += '</div></div></div>';
$('.instances').prepend(instances_html); $('.instances').prepend(instances_html);
updateUiOnInstance(window.currentHyperionInstance); updateUiOnInstance(window.currentHyperionInstance);
updateHyperionInstanceListing(); updateHyperionInstanceListing();
@ -83,7 +92,7 @@ $(document).ready(function () {
for (var idx = 0; idx < components.length; idx++) { for (var idx = 0; idx < components.length; idx++) {
if (components[idx].name != "ALL") { if (components[idx].name != "ALL") {
$("#general_comp_" + components[idx].name).bootstrapToggle(); $("#general_comp_" + components[idx].name).bootstrapToggle();
$("#general_comp_" + components[idx].name).bootstrapToggle(hyperion_enabled ? "enable" : "disable") $("#general_comp_" + components[idx].name).bootstrapToggle(hyperion_enabled ? "enable" : "disable");
$("#general_comp_" + components[idx].name).change(e => { $("#general_comp_" + components[idx].name).change(e => {
requestSetComponentState(e.currentTarget.id.split('_')[2], e.currentTarget.checked); requestSetComponentState(e.currentTarget.id.split('_')[2], e.currentTarget.checked);
}); });
@ -92,6 +101,12 @@ $(document).ready(function () {
} }
// add more info // add more info
var screenGrabber = window.serverConfig.framegrabber.enable ? $.i18n('general_enabled') : $.i18n('general_disabled');
$('#dash_screen_grabber').html(screenGrabber);
var videoGrabber = window.serverConfig.grabberV4L2.enable ? $.i18n('general_enabled') : $.i18n('general_disabled');
$('#dash_video_grabber').html(videoGrabber);
var fbPort = window.serverConfig.flatbufServer.enable ? window.serverConfig.flatbufServer.port : $.i18n('general_disabled'); var fbPort = window.serverConfig.flatbufServer.enable ? window.serverConfig.flatbufServer.port : $.i18n('general_disabled');
$('#dash_fbPort').html(fbPort); $('#dash_fbPort').html(fbPort);
var pbPort = window.serverConfig.protoServer.enable ? window.serverConfig.protoServer.port : $.i18n('general_disabled'); var pbPort = window.serverConfig.protoServer.enable ? window.serverConfig.protoServer.port : $.i18n('general_disabled');

View File

@ -1,4 +1,4 @@
$(document).ready( function() { $(document).ready(function () {
performTranslation(); performTranslation();
var importedConf; var importedConf;
@ -6,8 +6,7 @@ $(document).ready( function() {
var conf_editor = null; var conf_editor = null;
$('#conf_cont').append(createOptPanel('fa-wrench', $.i18n("edt_conf_gen_heading_title"), 'editor_container', 'btn_submit', 'panel-system')); $('#conf_cont').append(createOptPanel('fa-wrench', $.i18n("edt_conf_gen_heading_title"), 'editor_container', 'btn_submit', 'panel-system'));
if(window.showOptHelp) if (window.showOptHelp) {
{
$('#conf_cont').append(createHelpTable(window.schema.general.properties, $.i18n("edt_conf_gen_heading_title"))); $('#conf_cont').append(createHelpTable(window.schema.general.properties, $.i18n("edt_conf_gen_heading_title")));
} }
else else
@ -17,19 +16,19 @@ $(document).ready( function() {
general: window.schema.general general: window.schema.general
}, true, true); }, true, true);
conf_editor.on('change',function() { conf_editor.on('change', function () {
conf_editor.validate().length || window.readOnlyMode ? $('#btn_submit').attr('disabled', true) : $('#btn_submit').attr('disabled', false); conf_editor.validate().length || window.readOnlyMode ? $('#btn_submit').attr('disabled', true) : $('#btn_submit').attr('disabled', false);
}); });
$('#btn_submit').off().on('click',function() { $('#btn_submit').off().on('click', function () {
window.showOptHelp = conf_editor.getEditor("root.general.showOptHelp").getValue();
requestWriteConfig(conf_editor.getValue()); requestWriteConfig(conf_editor.getValue());
}); });
// Instance handling // Instance handling
function handleInstanceRename(e) function handleInstanceRename(e) {
{
conf_editor.on('change',function() { conf_editor.on('change', function () {
window.readOnlyMode ? $('#btn_cl_save').attr('disabled', true) : $('#btn_submit').attr('disabled', false); window.readOnlyMode ? $('#btn_cl_save').attr('disabled', true) : $('#btn_submit').attr('disabled', false);
window.readOnlyMode ? $('#btn_ma_save').attr('disabled', true) : $('#btn_submit').attr('disabled', false); window.readOnlyMode ? $('#btn_ma_save').attr('disabled', true) : $('#btn_submit').attr('disabled', false);
}); });
@ -37,52 +36,48 @@ $(document).ready( function() {
var inst = e.currentTarget.id.split("_")[1]; var inst = e.currentTarget.id.split("_")[1];
showInfoDialog('renInst', $.i18n('conf_general_inst_renreq_t'), getInstanceNameByIndex(inst)); showInfoDialog('renInst', $.i18n('conf_general_inst_renreq_t'), getInstanceNameByIndex(inst));
$("#id_btn_ok").off().on('click', function(){ $("#id_btn_ok").off().on('click', function () {
requestInstanceRename(inst, $('#renInst_name').val()) requestInstanceRename(inst, $('#renInst_name').val())
}); });
$('#renInst_name').off().on('input',function(e) { $('#renInst_name').off().on('input', function (e) {
(e.currentTarget.value.length >= 5 && e.currentTarget.value != getInstanceNameByIndex(inst)) ? $('#id_btn_ok').attr('disabled', false) : $('#id_btn_ok').attr('disabled', true); (e.currentTarget.value.length >= 5 && e.currentTarget.value != getInstanceNameByIndex(inst)) ? $('#id_btn_ok').attr('disabled', false) : $('#id_btn_ok').attr('disabled', true);
}); });
} }
function handleInstanceDelete(e) function handleInstanceDelete(e) {
{
var inst = e.currentTarget.id.split("_")[1]; var inst = e.currentTarget.id.split("_")[1];
showInfoDialog('delInst',$.i18n('conf_general_inst_delreq_h'),$.i18n('conf_general_inst_delreq_t',getInstanceNameByIndex(inst))); showInfoDialog('delInst', $.i18n('conf_general_inst_delreq_h'), $.i18n('conf_general_inst_delreq_t', getInstanceNameByIndex(inst)));
$("#id_btn_yes").off().on('click', function(){ $("#id_btn_yes").off().on('click', function () {
requestInstanceDelete(inst) requestInstanceDelete(inst)
}); });
} }
function buildInstanceList() function buildInstanceList() {
{
var inst = serverInfo.instance var inst = serverInfo.instance
$('.itbody').html(""); $('.itbody').html("");
for(var key in inst) for (var key in inst) {
{
var enable_style = inst[key].running ? "checked" : ""; var enable_style = inst[key].running ? "checked" : "";
var renameBtn = '<button id="instren_'+inst[key].instance+'" type="button" class="btn btn-primary"><i class="mdi mdi-lead-pencil""></i></button>'; var renameBtn = '<button id="instren_' + inst[key].instance + '" type="button" class="btn btn-primary"><i class="mdi mdi-lead-pencil""></i></button>';
var startBtn = "" var startBtn = ""
var delBtn = ""; var delBtn = "";
if(inst[key].instance > 0) if (inst[key].instance > 0) {
{ delBtn = '<button id="instdel_' + inst[key].instance + '" type="button" class="btn btn-danger"><i class="mdi mdi-delete-forever""></i></button>';
delBtn = '<button id="instdel_'+inst[key].instance+'" type="button" class="btn btn-danger"><i class="mdi mdi-delete-forever""></i></button>'; startBtn = '<input id="inst_' + inst[key].instance + '"' + enable_style + ' type="checkbox" data-toggle="toggle" data-onstyle="success font-weight-bold" data-on="' + $.i18n('general_btn_on') + '" data-offstyle="default font-weight-bold" data-off="' + $.i18n('general_btn_off') + '">';
startBtn = '<input id="inst_'+inst[key].instance+'"'+enable_style+' type="checkbox" data-toggle="toggle" data-onstyle="success font-weight-bold" data-on="'+$.i18n('general_btn_on')+'" data-offstyle="default font-weight-bold" data-off="'+$.i18n('general_btn_off')+'">';
} }
$('.itbody').append(createTableRow([inst[key].friendly_name, startBtn, renameBtn, delBtn], false, true)); $('.itbody').append(createTableRow([inst[key].friendly_name, startBtn, renameBtn, delBtn], false, true));
$('#instren_'+inst[key].instance).off().on('click', handleInstanceRename); $('#instren_' + inst[key].instance).off().on('click', handleInstanceRename);
$('#inst_'+inst[key].instance).bootstrapToggle(); $('#inst_' + inst[key].instance).bootstrapToggle();
$('#inst_'+inst[key].instance).change(e => { $('#inst_' + inst[key].instance).change(e => {
requestInstanceStartStop(e.currentTarget.id.split('_').pop(), e.currentTarget.checked); requestInstanceStartStop(e.currentTarget.id.split('_').pop(), e.currentTarget.checked);
}); });
$('#instdel_'+inst[key].instance).off().on('click', handleInstanceDelete); $('#instdel_' + inst[key].instance).off().on('click', handleInstanceDelete);
window.readOnlyMode ? $('#instren_'+inst[key].instance).attr('disabled', true) : $('#btn_submit').attr('disabled', false); window.readOnlyMode ? $('#instren_' + inst[key].instance).attr('disabled', true) : $('#btn_submit').attr('disabled', false);
window.readOnlyMode ? $('#inst_'+inst[key].instance).attr('disabled', true) : $('#btn_submit').attr('disabled', false); window.readOnlyMode ? $('#inst_' + inst[key].instance).attr('disabled', true) : $('#btn_submit').attr('disabled', false);
window.readOnlyMode ? $('#instdel_'+inst[key].instance).attr('disabled', true) : $('#btn_submit').attr('disabled', false); window.readOnlyMode ? $('#instdel_' + inst[key].instance).attr('disabled', true) : $('#btn_submit').attr('disabled', false);
} }
} }
@ -90,59 +85,51 @@ $(document).ready( function() {
$('.ithead').html(createTableRow([$.i18n('conf_general_inst_namehead'), "", $.i18n('conf_general_inst_actionhead'), ""], true, true)); $('.ithead').html(createTableRow([$.i18n('conf_general_inst_namehead'), "", $.i18n('conf_general_inst_actionhead'), ""], true, true));
buildInstanceList(); buildInstanceList();
$('#inst_name').off().on('input',function(e) { $('#inst_name').off().on('input', function (e) {
(e.currentTarget.value.length >= 5) && !window.readOnlyMode ? $('#btn_create_inst').attr('disabled', false) : $('#btn_create_inst').attr('disabled', true); (e.currentTarget.value.length >= 5) && !window.readOnlyMode ? $('#btn_create_inst').attr('disabled', false) : $('#btn_create_inst').attr('disabled', true);
if(5-e.currentTarget.value.length >= 1 && 5-e.currentTarget.value.length <= 4) if (5 - e.currentTarget.value.length >= 1 && 5 - e.currentTarget.value.length <= 4)
$('#inst_chars_needed').html(5-e.currentTarget.value.length + " " + $.i18n('general_chars_needed')) $('#inst_chars_needed').html(5 - e.currentTarget.value.length + " " + $.i18n('general_chars_needed'))
else else
$('#inst_chars_needed').html("<br />") $('#inst_chars_needed').html("<br />")
}); });
$('#btn_create_inst').off().on('click',function(e) { $('#btn_create_inst').off().on('click', function (e) {
requestInstanceCreate($('#inst_name').val()); requestInstanceCreate($('#inst_name').val());
$('#inst_name').val(""); $('#inst_name').val("");
$('#btn_create_inst').attr('disabled', true) $('#btn_create_inst').attr('disabled', true)
}); });
$(hyperion).off("instance-updated").on("instance-updated", function(event) { $(hyperion).off("instance-updated").on("instance-updated", function (event) {
buildInstanceList() buildInstanceList()
}); });
//import //import
function dis_imp_btn(state) function dis_imp_btn(state) {
{
state || window.readOnlyMode ? $('#btn_import_conf').attr('disabled', true) : $('#btn_import_conf').attr('disabled', false); state || window.readOnlyMode ? $('#btn_import_conf').attr('disabled', true) : $('#btn_import_conf').attr('disabled', false);
} }
function readFile(evt) function readFile(evt) {
{
var f = evt.target.files[0]; var f = evt.target.files[0];
if (f) if (f) {
{
var r = new FileReader(); var r = new FileReader();
r.onload = function(e) r.onload = function (e) {
{
var content = e.target.result.replace(/[^:]?\/\/.*/g, ''); //remove Comments var content = e.target.result.replace(/[^:]?\/\/.*/g, ''); //remove Comments
//check file is json //check file is json
var check = isJsonString(content); var check = isJsonString(content);
if(check.length != 0) if (check.length != 0) {
{
showInfoDialog('error', "", $.i18n('infoDialog_import_jsonerror_text', f.name, JSON.stringify(check))); showInfoDialog('error', "", $.i18n('infoDialog_import_jsonerror_text', f.name, JSON.stringify(check)));
dis_imp_btn(true); dis_imp_btn(true);
} }
else else {
{
content = JSON.parse(content); content = JSON.parse(content);
//check for hyperion json //check for hyperion json
if(typeof content.leds === 'undefined' || typeof content.general === 'undefined') if (typeof content.leds === 'undefined' || typeof content.general === 'undefined') {
{
showInfoDialog('error', "", $.i18n('infoDialog_import_hyperror_text', f.name)); showInfoDialog('error', "", $.i18n('infoDialog_import_hyperror_text', f.name));
dis_imp_btn(true); dis_imp_btn(true);
} }
else else {
{
dis_imp_btn(false); dis_imp_btn(false);
importedConf = content; importedConf = content;
confName = f.name; confName = f.name;
@ -153,16 +140,16 @@ $(document).ready( function() {
} }
} }
$('#btn_import_conf').off().on('click', function(){ $('#btn_import_conf').off().on('click', function () {
showInfoDialog('import', $.i18n('infoDialog_import_confirm_title'), $.i18n('infoDialog_import_confirm_text', confName)); showInfoDialog('import', $.i18n('infoDialog_import_confirm_title'), $.i18n('infoDialog_import_confirm_text', confName));
$('#id_btn_import').off().on('click', function(){ $('#id_btn_import').off().on('click', function () {
requestWriteConfig(importedConf, true); requestWriteConfig(importedConf, true);
setTimeout(initRestart, 100); setTimeout(initRestart, 100);
}); });
}); });
$('#select_import_conf').off().on('change', function(e){ $('#select_import_conf').off().on('change', function (e) {
if (window.File && window.FileReader && window.FileList && window.Blob) if (window.File && window.FileReader && window.FileList && window.Blob)
readFile(e); readFile(e);
else else
@ -170,23 +157,22 @@ $(document).ready( function() {
}); });
//export //export
$('#btn_export_conf').off().on('click', function(){ $('#btn_export_conf').off().on('click', function () {
var name = window.serverConfig.general.name; var name = window.serverConfig.general.name;
var d = new Date(); var d = new Date();
var month = d.getMonth()+1; var month = d.getMonth() + 1;
var day = d.getDate(); var day = d.getDate();
var timestamp = d.getFullYear() + '.' + var timestamp = d.getFullYear() + '.' +
(month<10 ? '0' : '') + month + '.' + (month < 10 ? '0' : '') + month + '.' +
(day<10 ? '0' : '') + day; (day < 10 ? '0' : '') + day;
download(JSON.stringify(window.serverConfig, null, "\t"), 'Hyperion-'+window.currentVersion+'-Backup ('+name+') '+timestamp+'.json', "application/json"); download(JSON.stringify(window.serverConfig, null, "\t"), 'Hyperion-' + window.currentVersion + '-Backup (' + name + ') ' + timestamp + '.json', "application/json");
}); });
//create introduction //create introduction
if(window.showOptHelp) if (window.showOptHelp) {
{
createHint("intro", $.i18n('conf_general_intro'), "editor_container"); createHint("intro", $.i18n('conf_general_intro'), "editor_container");
createHint("intro", $.i18n('conf_general_tok_desc'), "tok_desc_cont"); createHint("intro", $.i18n('conf_general_tok_desc'), "tok_desc_cont");
createHint("intro", $.i18n('conf_general_inst_desc'), "inst_desc_cont"); createHint("intro", $.i18n('conf_general_inst_desc'), "inst_desc_cont");

1085
assets/webconfig/js/content_grabber.js Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@ -307,12 +307,17 @@ $(document).ready(function () {
window.scrollTo(0, 0); window.scrollTo(0, 0);
}); });
$(window).scroll(function(){ $(window).scroll(function() {
if ($(window).scrollTop() > 65) if ($(window).scrollTop() > 65)
$("#navbar_brand_logo").css("display", "none"); $("#navbar_brand_logo").css("display", "none");
else else
$("#navbar_brand_logo").css("display", ""); $("#navbar_brand_logo").css("display", "");
}); });
$('#side-menu li a, #side-menu li ul li a').click(function() {
$('#side-menu').find('.active').toggleClass('inactive'); // find all active classes and set inactive;
$(this).addClass('active');
});
}); });
function suppressDefaultPwWarning() { function suppressDefaultPwWarning() {
@ -349,7 +354,7 @@ $("#btn_darkmode").off().on("click", function (e) {
}); });
// Menuitem toggle; // Menuitem toggle;
function SwitchToMenuItem(target) { function SwitchToMenuItem(target, item) {
document.getElementById(target).click(); // Get <a href menu item; document.getElementById(target).click(); // Get <a href menu item;
let sidebar = $('#side-menu'); // Get sidebar menu; let sidebar = $('#side-menu'); // Get sidebar menu;
sidebar.find('.active').toggleClass('inactive'); // find all active classes and set inactive; sidebar.find('.active').toggleClass('inactive'); // find all active classes and set inactive;
@ -357,6 +362,21 @@ function SwitchToMenuItem(target) {
$('#' + target).removeClass('inactive'); // Remove inactive state by classname; $('#' + target).removeClass('inactive'); // Remove inactive state by classname;
$('#' + target).addClass('active'); // Add active state by classname; $('#' + target).addClass('active'); // Add active state by classname;
let cl_object = $('#' + target).closest('ul'); // Find closest ul sidemenu header; let cl_object = $('#' + target).closest('ul'); // Find closest ul sidemenu header;
cl_object.addClass('in'); // add class "in" to expand header in sidebar menu; cl_object.addClass('in'); // Add class "in" to expand header in sidebar menu;
if (item) { // Jump to div "item" if available. Time limit 3 seconds
function scrollTo(counter) {
if(counter < 30) {
setTimeout(function() {
counter++;
if ($('#' + item).length)
$('#' + item)[0].scrollIntoView();
else
scrollTo(counter);
}, 100);
}
}
scrollTo(0);
}
}; };

View File

@ -0,0 +1,99 @@
$(document).ready(function () {
performTranslation();
// update instance listing
updateHyperionInstanceListing();
var conf_editor_instCapt = null;
// Instance Capture
$('#conf_cont').append(createRow('conf_cont_instCapt'));
$('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
if (window.showOptHelp) {
$('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title")));
}
// Instance Capture
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
instCapture: window.schema.instCapture
}, true, true);
var grabber_config_info_html = '<div class="bs-callout bs-callout-info" style="margin-top:0px"><h4>' + $.i18n('dashboard_infobox_label_title') + '</h4 >';
grabber_config_info_html += '<span>' + $.i18n('conf_grabber_inst_grabber_config_info') + '</span>';
grabber_config_info_html += '<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem(\'MenuItemGrabber\')" style="text-decoration:none;cursor:pointer"></a>';
grabber_config_info_html += '</div>';
$('#editor_container_instCapt').append(grabber_config_info_html);
conf_editor_instCapt.on('ready', function () {
if (!window.serverConfig.framegrabber.enable) {
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(false);
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").disable();
}
else {
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(window.serverConfig.instCapture.systemEnable);
}
if (!window.serverConfig.grabberV4L2.enable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(false);
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable();
}
else {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(window.serverConfig.instCapture.v4lEnable);
}
});
conf_editor_instCapt.on('change', function () {
if (!conf_editor_instCapt.validate().length) {
if (!window.serverConfig.framegrabber.enable && !window.serverConfig.grabberV4L2.enable) {
$('#btn_submit_instCapt').attr('disabled', true);
} else {
window.readOnlyMode ? $('#btn_submit_instCapt').attr('disabled', true) : $('#btn_submit_instCapt').attr('disabled', false);
}
}
else {
$('#btn_submit_instCapt').attr('disabled', true);
}
});
conf_editor_instCapt.watch('root.instCapture.systemEnable', () => {
var screenEnable = conf_editor_instCapt.getEditor("root.instCapture.systemEnable").getValue();
if (screenEnable) {
conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").setValue(window.serverConfig.framegrabber.available_devices);
conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").disable();
showInputOptions("instCapture", ["systemGrabberDevice"], true);
showInputOptions("instCapture", ["systemPriority"], true);
} else {
showInputOptions("instCapture", ["systemGrabberDevice"], false);
showInputOptions("instCapture", ["systemPriority"], false);
}
});
conf_editor_instCapt.watch('root.instCapture.v4lEnable', () => {
var videoEnable = conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").getValue();
if (videoEnable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").setValue(window.serverConfig.grabberV4L2.available_devices);
conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").disable();
showInputOptions("instCapture", ["v4lGrabberDevice"], true);
showInputOptions("instCapture", ["v4lPriority"], true);
}
else {
if (!window.serverConfig.grabberV4L2.enable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable();
}
showInputOptions("instCapture", ["v4lGrabberDevice"], false);
showInputOptions("instCapture", ["v4lPriority"], false);
}
});
$('#btn_submit_instCapt').off().on('click', function () {
requestWriteConfig(conf_editor_instCapt.getValue());
});
removeOverlay();
});

View File

@ -701,6 +701,8 @@ $(document).ready(function () {
var hwLedCountDefault = 1; var hwLedCountDefault = 1;
var colorOrderDefault = "rgb"; var colorOrderDefault = "rgb";
$('#btn_test_controller').hide();
switch (ledType) { switch (ledType) {
case "cololight": case "cololight":
case "wled": case "wled":
@ -769,11 +771,38 @@ $(document).ready(function () {
}); });
conf_editor.on('change', function () { conf_editor.on('change', function () {
//Check, if device can be identified/tested and/or saved // //Check, if device can be identified/tested and/or saved
var canIdentify = false; var canIdentify = false;
var canSave = false; var canSave = false;
switch (ledType) { switch (ledType) {
case "atmoorb":
case "fadecandy":
case "tinkerforge":
case "tpm2net":
case "udpe131":
case "udpartnet":
case "udph801":
case "udpraw":
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
if (host !== "") {
canSave = true;
}
break;
case "philipshue":
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
var username = conf_editor.getEditor("root.specificOptions.username").getValue();
if (host !== "" && username != "") {
var useEntertainmentAPI = conf_editor.getEditor("root.specificOptions.useEntertainmentAPI").getValue();
var clientkey = conf_editor.getEditor("root.specificOptions.clientkey").getValue();
if (!useEntertainmentAPI || clientkey !== "") {
canSave = true;
}
}
break;
case "cololight": case "cololight":
case "wled": case "wled":
var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue(); var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue();
@ -797,48 +826,20 @@ $(document).ready(function () {
} }
} }
break; break;
case "adalight":
var output = conf_editor.getEditor("root.specificOptions.output").getValue();
if (output !== "NONE" && output !== "SELECT" && output !== "") {
canIdentify = true;
}
case "atmo":
case "dmx":
case "karate":
case "sedu":
case "tpm2":
case "apa102":
case "apa104":
case "ws2801":
case "lpd6803":
case "lpd8806":
case "p9813":
case "sk6812spi":
case "sk6822spi":
case "sk9822":
case "ws2812spi":
case "piblaster":
var output = conf_editor.getEditor("root.specificOptions.output").getValue();
if (output !== "NONE" && output !== "SELECT" && output !== "") {
canSave = true;
}
break;
default: default:
canIdentify = false; canIdentify = false;
canSave = true; canSave = true;
} }
if (!conf_editor.validate().length) {
if (canIdentify) { if (canIdentify) {
$("#btn_test_controller").removeClass('hidden'); $("#btn_test_controller").show();
$('#btn_test_controller').attr('disabled', false); $('#btn_test_controller').attr('disabled', false);
} } else {
else { $('#btn_test_controller').hide();
$('#btn_test_controller').attr('disabled', true); $('#btn_test_controller').attr('disabled', true);
} }
} else {
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
if (hardwareLedCount < 1) {
canSave = false; canSave = false;
} }
@ -886,9 +887,9 @@ $(document).ready(function () {
conf_editor.getEditor(specOptPath + "host").setValue(val); conf_editor.getEditor(specOptPath + "host").setValue(val);
break; break;
} }
}
showAllDeviceInputOptions("hostList", showOptions); showAllDeviceInputOptions("hostList", showOptions);
}
}); });
conf_editor.watch('root.specificOptions.host', () => { conf_editor.watch('root.specificOptions.host', () => {
@ -900,8 +901,10 @@ $(document).ready(function () {
else { else {
let params = {}; let params = {};
switch (ledType) { switch (ledType) {
case "cololight": case "cololight":
params = { host: host }; params = { host: host };
getProperties_device(ledType, host, params);
break; break;
case "nanoleaf": case "nanoleaf":
@ -910,33 +913,70 @@ $(document).ready(function () {
return; return;
} }
params = { host: host, token: token }; params = { host: host, token: token };
getProperties_device(ledType, host, params);
break; break;
case "wled": case "wled":
params = { host: host, filter: "info" }; params = { host: host, filter: "info" };
getProperties_device(ledType, host, params);
break; break;
default: default:
} }
getProperties_device(ledType, host, params);
} }
}); });
conf_editor.watch('root.specificOptions.output', () => { conf_editor.watch('root.specificOptions.output', () => {
var output = conf_editor.getEditor("root.specificOptions.output").getValue(); var output = conf_editor.getEditor("root.specificOptions.output").getValue();
if (output === "NONE" || output === "SELECT" || output === "") { if (output === "NONE" || output === "SELECT" || output === "") {
$('#btn_submit_controller').attr('disabled', true);
$('#btn_test_controller').attr('disabled', true);
$('#btn_test_controller').hide();
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(1); conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(1);
showAllDeviceInputOptions("output", false); showAllDeviceInputOptions("output", false);
} }
else { else {
showAllDeviceInputOptions("output", true); showAllDeviceInputOptions("output", true);
let params = {}; let params = {};
var canIdentify = false;
switch (ledType) { switch (ledType) {
case "adalight":
canIdentify = true;
break;
case "atmo": case "atmo":
case "karate": case "karate":
params = { serialPort: output }; params = { serialPort: output };
getProperties_device(ledType, output, params); getProperties_device(ledType, output, params);
break; break;
case "dmx":
case "sedu":
case "tpm2":
case "apa102":
case "apa104":
case "ws2801":
case "lpd6803":
case "lpd8806":
case "p9813":
case "sk6812spi":
case "sk6822spi":
case "sk9822":
case "ws2812spi":
case "piblaster":
default:
}
if (!conf_editor.validate().length) {
if (canIdentify) {
$("#btn_test_controller").show();
$('#btn_test_controller').attr('disabled', false);
} else {
$('#btn_test_controller').hide();
$('#btn_test_controller').attr('disabled', true);
}
if (!window.readOnlyMode) {
$('#btn_submit_controller').attr('disabled', false);
}
} }
} }
}); });
@ -1229,6 +1269,8 @@ function saveLedConfig(genDefLayout = false) {
break; break;
} }
//Rewrite whole LED & Layout configuration, in case changes were done accross tabs and no default layout //Rewrite whole LED & Layout configuration, in case changes were done accross tabs and no default layout
if (genDefLayout !== true) { if (genDefLayout !== true) {
result.ledConfig = getLedConfig(); result.ledConfig = getLedConfig();
@ -1269,8 +1311,6 @@ var updateSelectList = function (ledType, discoveryInfo) {
ledTypeGroup = "devRPiGPIO"; ledTypeGroup = "devRPiGPIO";
} }
var specOpt = conf_editor.getEditor('root.specificOptions'); // get specificOptions of the editor
switch (ledTypeGroup) { switch (ledTypeGroup) {
case "devNET": case "devNET":
key = "hostList"; key = "hostList";
@ -1434,11 +1474,14 @@ var updateSelectList = function (ledType, discoveryInfo) {
} }
if (enumVals.length > 0) { if (enumVals.length > 0) {
updateJsonEditorSelection(specOpt, key, addSchemaElements, enumVals, enumTitelVals, enumDefaultVal, addSelect, addCustom); updateJsonEditorSelection(conf_editor, 'root.specificOptions', key, addSchemaElements, enumVals, enumTitelVals, enumDefaultVal, addSelect, addCustom);
} }
}; };
async function discover_device(ledType, params) { async function discover_device(ledType, params) {
$('#btn_submit_controller').attr('disabled', true);
const result = await requestLedDeviceDiscovery(ledType, params); const result = await requestLedDeviceDiscovery(ledType, params);
var discoveryResult; var discoveryResult;
@ -1479,6 +1522,7 @@ async function getProperties_device(ledType, key, params) {
} }
else { else {
$('#btn_submit_controller').attr('disabled', true); $('#btn_submit_controller').attr('disabled', true);
$('#btn_test_controller').attr('disabled', true);
} }
} }
} }
@ -1544,8 +1588,7 @@ function updateElements(ledType, key) {
if (ledProperties && ledProperties.ledCount) { if (ledProperties && ledProperties.ledCount) {
if (ledProperties.ledCount.length > 0) { if (ledProperties.ledCount.length > 0) {
var configuredLedCount = window.serverConfig.device.hardwareLedCount; var configuredLedCount = window.serverConfig.device.hardwareLedCount;
var generalOpt = conf_editor.getEditor('root.generalOptions'); updateJsonEditorSelection(conf_editor, 'root.generalOptions', "hardwareLedCount", {}, ledProperties.ledCount, [], configuredLedCount);
updateJsonEditorSelection(generalOpt, "hardwareLedCount", {}, ledProperties.ledCount, [], configuredLedCount);
} }
} }
break; break;

View File

@ -25,7 +25,7 @@ $(document).ready(function () {
$('#btn_submit').off().on('click', function () { $('#btn_submit').off().on('click', function () {
var displayedLogLevel = conf_editor.getEditor("root.logger.level").getValue(); var displayedLogLevel = conf_editor.getEditor("root.logger.level").getValue();
var newLogLevel = {logger:{}}; var newLogLevel = { logger: {} };
newLogLevel.logger.level = displayedLogLevel; newLogLevel.logger.level = displayedLogLevel;
requestWriteConfig(newLogLevel); requestWriteConfig(newLogLevel);
@ -103,7 +103,7 @@ $(document).ready(function () {
} }
}); });
$('#log_footer').append('<button class="btn btn-primary pull-right" id="btn_clipboard"><i class="fa fa-fw fa-clipboard"></i>Copy Log to Clipboard</button>'); $('#log_footer').append('<button class="btn btn-primary pull-right" id="btn_clipboard"><i class="fa fa-fw fa-clipboard"></i>' + $.i18n("conf_logging_btn_clipboard") + '</button>');
$('#btn_clipboard').off().on('click', function () { $('#btn_clipboard').off().on('click', function () {
const temp = document.createElement('textarea'); const temp = document.createElement('textarea');

View File

@ -7,7 +7,7 @@ $(document).ready(function () {
var oldEffects = []; var oldEffects = [];
var cpcolor = '#B500FF'; var cpcolor = '#B500FF';
var mappingList = window.serverSchema.properties.color.properties.imageToLedMappingType.enum; var mappingList = window.serverSchema.properties.color.properties.imageToLedMappingType.enum;
var duration = 0; var duration = ENDLESS;
var rgb = { r: 255, g: 0, b: 0 }; var rgb = { r: 255, g: 0, b: 0 };
var lastImgData = ""; var lastImgData = "";
var lastFileName = ""; var lastFileName = "";
@ -201,7 +201,9 @@ $(document).ready(function () {
}); });
for (const comp of components) { for (const comp of components) {
if (comp.name === "ALL") if (comp.name === "ALL" || (comp.name === "FORWARDER" && window.currentHyperionInstance != 0) ||
(comp.name === "GRABBER" && !window.serverConfig.framegrabber.enable) ||
(comp.name === "V4L" && !window.serverConfig.grabberV4L2.enable))
continue; continue;
const enable_style = (comp.enabled ? "checked" : ""); const enable_style = (comp.enabled ? "checked" : "");

View File

@ -33,6 +33,8 @@ window.comps = [];
window.defaultPasswordIsSet = null; window.defaultPasswordIsSet = null;
tokenList = {}; tokenList = {};
const ENDLESS = -1;
function initRestart() function initRestart()
{ {
$(window.hyperion).off(); $(window.hyperion).off();
@ -470,8 +472,14 @@ async function requestLedDeviceProperties(type, params)
function requestLedDeviceIdentification(type, params) function requestLedDeviceIdentification(type, params)
{ {
//sendToHyperion("leddevice", "identify", '"ledDeviceType": "'+type+'","params": '+JSON.stringify(params)+'');
let data = { ledDeviceType: type, params: params }; let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "identify", data, Math.floor(Math.random() * 1000)); return sendAsyncToHyperion("leddevice", "identify", data, Math.floor(Math.random() * 1000));
} }
async function requestInputSourcesDiscovery(type, params) {
let data = { sourceType: type, params: params };
return sendAsyncToHyperion("inputsource", "discover", data, Math.floor(Math.random() * 1000));
}

View File

@ -0,0 +1,33 @@
var storedLang;
var availLang = ['cs', 'de', 'en', 'es', 'fr', 'it', 'nl', 'nb', 'pl', 'pt', 'ro', 'sv', 'vi', 'ru', 'tr', 'zh-CN'];
var availLangText = ['Čeština', 'Deutsch', 'English', 'Español', 'Français', 'Italiano', 'Nederlands', 'Norsk Bokmål', 'Polski', 'Português', 'Română', 'Svenska', 'Tiếng Việt', 'русский', 'Türkçe', '汉语'];
//$.i18n.debug = true;
//i18n
function initTrans(lc) {
$.i18n().load("i18n", lc).done(
function () {
$.i18n().locale = lc;
performTranslation();
});
}
storedLang = getStorage("langcode");
if (storedLang == null || storedLang === "undefined") {
var langLocale = $.i18n().locale.substring(0, 2);
//Test, if language is supported by hyperion
var langIdx = availLang.indexOf(langLocale);
if (langIdx === -1) {
// If language is not supported by hyperion, try fallback language
langLocale = $.i18n().options.fallbackLocale.substring(0, 2);
langIdx = availLang.indexOf(langLocale);
if (langIdx === -1) {
langLocale = 'en';
}
}
storedLang = langLocale;
setStorage("langcode", storedLang);
}
initTrans(storedLang);

View File

@ -1,28 +1,35 @@
$(document).ready(function() { $(document).ready(function () {
var modalOpened = false; var modalOpened = false;
var ledsim_width = 540; var ledsim_width = 540;
var ledsim_height = 489; var ledsim_height = 489;
var dialog; var dialog;
var leds; var leds;
var grabberConfig;
var lC = false; var lC = false;
var imageCanvasNodeCtx; var imageCanvasNodeCtx;
var ledsCanvasNodeCtx; var ledsCanvasNodeCtx;
var canvas_height; var canvas_height;
var canvas_width; var canvas_width;
var twoDPaths = []; var twoDPaths = [];
var toggleLeds, toggleLedsNum = false; var toggleLeds = false;
var toggleLedsNum = false;
var toggleSigDetectArea = false;
var activeComponent = "";
const image = new Image()
image.src = "img/hyperion/logo_negativ.png",
/// add prototype for simple canvas clear() method /// add prototype for simple canvas clear() method
CanvasRenderingContext2D.prototype.clear = function(){ CanvasRenderingContext2D.prototype.clear = function () {
this.clearRect(0, 0, this.canvas.width, this.canvas.height) this.clearRect(0, 0, this.canvas.width, this.canvas.height)
}; };
function create2dPaths(){ function create2dPaths() {
twoDPaths = []; twoDPaths = [];
for(var idx=0; idx<leds.length; idx++) for (var idx = 0; idx < leds.length; idx++) {
{
var led = leds[idx]; var led = leds[idx];
twoDPaths.push( build2DPath(led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax-led.hmin) * canvas_width, (led.vmax-led.vmin) * canvas_height, 5) ); twoDPaths.push(build2DPath(led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax - led.hmin) * canvas_width, (led.vmax - led.vmin) * canvas_height, 5));
} }
} }
@ -44,9 +51,9 @@ $(document).ready(function() {
*/ */
function build2DPath(x, y, width, height, radius) { function build2DPath(x, y, width, height, radius) {
if (typeof radius == 'number') { if (typeof radius == 'number') {
radius = {tl: radius, tr: radius, br: radius, bl: radius}; radius = { tl: radius, tr: radius, br: radius, bl: radius };
} else { } else {
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0}; var defaultRadius = { tl: 0, tr: 0, br: 0, bl: 0 };
for (var side in defaultRadius) { for (var side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side]; radius[side] = radius[side] || defaultRadius[side];
} }
@ -67,18 +74,17 @@ $(document).ready(function() {
return path; return path;
} }
$(window.hyperion).one("ready",function(){ $(window.hyperion).one("ready", function () {
leds = window.serverConfig.leds; leds = window.serverConfig.leds;
grabberConfig = window.serverConfig.grabberV4L2;
if(window.showOptHelp) if (window.showOptHelp) {
{
createHint('intro', $.i18n('main_ledsim_text'), 'ledsim_text'); createHint('intro', $.i18n('main_ledsim_text'), 'ledsim_text');
$('#ledsim_text').css({'margin':'10px 15px 0px 15px'}); $('#ledsim_text').css({ 'margin': '10px 15px 0px 15px' });
$('#ledsim_text .bs-callout').css("margin","0px") $('#ledsim_text .bs-callout').css("margin", "0px")
} }
if(getStorage('ledsim_width') != null) if (getStorage('ledsim_width') != null) {
{
ledsim_width = getStorage('ledsim_width'); ledsim_width = getStorage('ledsim_width');
ledsim_height = getStorage('ledsim_height'); ledsim_height = getStorage('ledsim_height');
} }
@ -98,8 +104,7 @@ $(document).ready(function() {
updateLedLayout(); updateLedLayout();
}, },
opened: function (e) { opened: function (e) {
if(!lC) if (!lC) {
{
updateLedLayout(); updateLedLayout();
lC = true; lC = true;
} }
@ -107,11 +112,12 @@ $(document).ready(function() {
requestLedColorsStart(); requestLedColorsStart();
setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-danger", "btn-success"); setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-danger", "btn-success");
if($('#leds_toggle_live_video').hasClass('btn-success')) if ($('#leds_toggle_live_video').hasClass('btn-success'))
requestLedImageStart(); requestLedImageStart();
}, },
closed: function (e) { closed: function (e) {
modalOpened = false; modalOpened = false;
lC = false;
}, },
resizeStop: function (e) { resizeStop: function (e) {
setStorage("ledsim_width", $("#ledsim_dialog").outerWidth()); setStorage("ledsim_width", $("#ledsim_dialog").outerWidth());
@ -119,48 +125,58 @@ $(document).ready(function() {
} }
}); });
// apply new serverinfos // apply new serverinfos
$(window.hyperion).on("cmd-config-getconfig",function(event){ $(window.hyperion).on("cmd-config-getconfig", function (event) {
leds = event.response.info.leds; leds = event.response.info.leds;
grabberConfig = event.response.info.grabberV4L2;
updateLedLayout(); updateLedLayout();
}); });
}); });
function printLedsToCanvas(colors) function printLedsToCanvas(colors) {
{
if (grabberConfig.enable && grabberConfig.signalDetection && toggleSigDetectArea && storedAccess === 'expert') {
sigDetectAreaCanvasNodeCtx.setLineDash([5, 5]);
sigDetectAreaCanvasNodeCtx.stroke(build2DPath(grabberConfig.sDHOffsetMin * canvas_width,
grabberConfig.sDVOffsetMin * canvas_height,
(grabberConfig.sDHOffsetMax - grabberConfig.sDHOffsetMin) * canvas_width,
(grabberConfig.sDVOffsetMax - grabberConfig.sDVOffsetMin) * canvas_height,
5));
}
// toggle leds, do not print // toggle leds, do not print
if(toggleLeds) if (toggleLeds)
return; return;
var useColor = false; var useColor = false;
var cPos = 0; var cPos = 0;
ledsCanvasNodeCtx.clear(); ledsCanvasNodeCtx.clear();
if(typeof colors != "undefined") if (typeof colors != "undefined")
useColor = true; useColor = true;
// check size of ledcolors with leds length // check size of ledcolors with leds length
if(colors && colors.length/3 < leds.length) if (colors && colors.length / 3 < leds.length)
return; return;
for(var idx=0; idx<leds.length; idx++) for (var idx = 0; idx < leds.length; idx++) {
{
var led = leds[idx]; var led = leds[idx];
// can be used as fallback when Path2D is not available // can be used as fallback when Path2D is not available
//roundRect(ledsCanvasNodeCtx, led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax-led.hmin) * canvas_width, (led.vmax-led.vmin) * canvas_height, 4, true, colors[idx]) //roundRect(ledsCanvasNodeCtx, led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax-led.hmin) * canvas_width, (led.vmax-led.vmin) * canvas_height, 4, true, colors[idx])
//ledsCanvasNodeCtx.fillRect(led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax-led.hmin) * canvas_width, (led.vmax-led.vmin) * canvas_height); //ledsCanvasNodeCtx.fillRect(led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax-led.hmin) * canvas_width, (led.vmax-led.vmin) * canvas_height);
ledsCanvasNodeCtx.fillStyle = (useColor) ? "rgba("+colors[cPos]+","+colors[cPos+1]+","+colors[cPos+2]+",0.75)" : "hsla("+(idx*360/leds.length)+",100%,50%,0.75)"; ledsCanvasNodeCtx.fillStyle = (useColor) ? "rgba(" + colors[cPos] + "," + colors[cPos + 1] + "," + colors[cPos + 2] + ",0.75)" : "hsla(" + (idx * 360 / leds.length) + ",100%,50%,0.75)";
ledsCanvasNodeCtx.fill(twoDPaths[idx]); ledsCanvasNodeCtx.fill(twoDPaths[idx]);
ledsCanvasNodeCtx.strokeStyle = '#323232';
ledsCanvasNodeCtx.stroke(twoDPaths[idx]); ledsCanvasNodeCtx.stroke(twoDPaths[idx]);
if(toggleLedsNum) if (toggleLedsNum) {
{
//ledsCanvasNodeCtx.shadowOffsetX = 1; //ledsCanvasNodeCtx.shadowOffsetX = 1;
//ledsCanvasNodeCtx.shadowOffsetY = 1; //ledsCanvasNodeCtx.shadowOffsetY = 1;
//ledsCanvasNodeCtx.shadowColor = "black"; //ledsCanvasNodeCtx.shadowColor = "black";
//ledsCanvasNodeCtx.shadowBlur = 4; //ledsCanvasNodeCtx.shadowBlur = 4;
ledsCanvasNodeCtx.fillStyle = "white"; ledsCanvasNodeCtx.fillStyle = "white";
ledsCanvasNodeCtx.textAlign = "center"; ledsCanvasNodeCtx.textAlign = "center";
ledsCanvasNodeCtx.fillText(((led.name) ? led.name : idx), (led.hmin * canvas_width) + ( ((led.hmax-led.hmin) * canvas_width) / 2), (led.vmin * canvas_height) + ( ((led.vmax-led.vmin) * canvas_height) / 2)); ledsCanvasNodeCtx.fillText(((led.name) ? led.name : idx), (led.hmin * canvas_width) + (((led.hmax - led.hmin) * canvas_width) / 2), (led.vmin * canvas_height) + (((led.vmax - led.vmin) * canvas_height) / 2));
} }
// increment colorsPosition // increment colorsPosition
@ -168,112 +184,146 @@ $(document).ready(function() {
} }
} }
function updateLedLayout() function updateLedLayout() {
{ if (grabberConfig.enable && grabberConfig.signalDetection && storedAccess === 'expert') {
$("#sigDetectArea_toggle").show();
} else {
$("#sigDetectArea_toggle").hide();
}
//calculate body size //calculate body size
canvas_height = $('#ledsim_dialog').outerHeight()-$('#ledsim_text').outerHeight()-$('[data-role=footer]').outerHeight()-$('[data-role=header]').outerHeight()-40; canvas_height = $('#ledsim_dialog').outerHeight() - $('#ledsim_text').outerHeight() - $('[data-role=footer]').outerHeight() - $('[data-role=header]').outerHeight() - 40;
canvas_width = $('#ledsim_dialog').outerWidth()-30; canvas_width = $('#ledsim_dialog').outerWidth() - 30;
$('#leds_canvas').html(""); $('#leds_canvas').html("");
var leds_html = '<canvas id="image_preview_canv" width="'+canvas_width+'" height="'+canvas_height+'" style="position: absolute; left: 0; top: 0; z-index: 99998;"></canvas>'; var leds_html = '<canvas id="image_preview_canv" width="' + canvas_width + '" height="' + canvas_height + '" style="position: absolute; left: 0; top: 0; z-index: 99998;"></canvas>';
leds_html += '<canvas id="leds_preview_canv" width="'+canvas_width+'" height="'+canvas_height+'" style="position: absolute; left: 0; top: 0; z-index: 99999;"></canvas>'; leds_html += '<canvas id="leds_preview_canv" width="' + canvas_width + '" height="' + canvas_height + '" style="position: absolute; left: 0; top: 0; z-index: 99999;"></canvas>';
leds_html += '<canvas id="grab_preview_canv" width="' + canvas_width + '" height="' + canvas_height + '" style="position: absolute; left: 0; top: 0; z-index: 99999;"></canvas>';
$('#leds_canvas').html(leds_html); $('#leds_canvas').html(leds_html);
imageCanvasNodeCtx = document.getElementById("image_preview_canv").getContext("2d"); imageCanvasNodeCtx = document.getElementById("image_preview_canv").getContext("2d");
ledsCanvasNodeCtx = document.getElementById("leds_preview_canv").getContext("2d"); ledsCanvasNodeCtx = document.getElementById("leds_preview_canv").getContext("2d");
sigDetectAreaCanvasNodeCtx = document.getElementById("grab_preview_canv").getContext("2d");
create2dPaths(); create2dPaths();
printLedsToCanvas(); printLedsToCanvas();
resetImage(); resetImage();
} }
// ------------------------------------------------------------------ // ------------------------------------------------------------------
$('#leds_toggle_num').off().on("click", function() { $('#leds_toggle_num').off().on("click", function () {
toggleLedsNum = !toggleLedsNum toggleLedsNum = !toggleLedsNum
toggleClass('#leds_toggle_num', "btn-danger", "btn-success"); toggleClass('#leds_toggle_num', "btn-danger", "btn-success");
}); });
// ------------------------------------------------------------------ // ------------------------------------------------------------------
$('#leds_toggle').off().on("click", function() { $('#leds_toggle').off().on("click", function () {
toggleLeds = !toggleLeds toggleLeds = !toggleLeds
ledsCanvasNodeCtx.clear(); ledsCanvasNodeCtx.clear();
toggleClass('#leds_toggle', "btn-success", "btn-danger"); toggleClass('#leds_toggle', "btn-success", "btn-danger");
if (!toggleLeds) {
$("#leds_toggle_num").show();
} else {
$("#leds_toggle_num").hide();
}
}); });
// ------------------------------------------------------------------ // ------------------------------------------------------------------
$('#leds_toggle_live_video').off().on("click", function() { $('#leds_toggle_live_video').off().on("click", function () {
setClassByBool('#leds_toggle_live_video',window.imageStreamActive,"btn-success","btn-danger"); setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-success", "btn-danger");
if ( window.imageStreamActive ) if (window.imageStreamActive) {
{
requestLedImageStop(); requestLedImageStop();
resetImage(); resetImage();
} }
else else {
{
requestLedImageStart(); requestLedImageStart();
} }
}); });
$('#sigDetectArea_toggle').off().on("click", function () {
toggleSigDetectArea = !toggleSigDetectArea
sigDetectAreaCanvasNodeCtx.clear();
toggleClass('#sigDetectArea_toggle', "btn-success", "btn-danger");
});
// ------------------------------------------------------------------ // ------------------------------------------------------------------
$(window.hyperion).on("cmd-ledcolors-ledstream-update",function(event){ $(window.hyperion).on("cmd-ledcolors-ledstream-update", function (event) {
if (!modalOpened) if (!modalOpened) {
{
requestLedColorsStop(); requestLedColorsStop();
} }
else else {
{
printLedsToCanvas(event.response.result.leds) printLedsToCanvas(event.response.result.leds)
} }
}); });
// ------------------------------------------------------------------ // ------------------------------------------------------------------
$(window.hyperion).on("cmd-ledcolors-imagestream-update",function(event){ $(window.hyperion).on("cmd-ledcolors-imagestream-update", function (event) {
//console.log("cmd-ledcolors-imagestream-update", event.response);
setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-danger", "btn-success"); setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-danger", "btn-success");
if (!modalOpened) if (!modalOpened) {
{
if ($('#leds_prev_toggle_live_video').length > 0) if ($('#leds_prev_toggle_live_video').length > 0)
return; return;
requestLedImageStop(); requestLedImageStop();
} }
else else {
{
var imageData = (event.response.result.image); var imageData = (event.response.result.image);
var image = new Image(); var image = new Image();
image.onload = function() { image.onload = function () {
imageCanvasNodeCtx.drawImage(image, 0, 0, canvas_width, canvas_height); imageCanvasNodeCtx.drawImage(image, 0, 0, canvas_width, canvas_height);
}; };
image.src = imageData; image.src = imageData;
} }
}); });
$("#btn_open_ledsim").off().on("click", function(event) { $("#btn_open_ledsim").off().on("click", function (event) {
dialog.open(); dialog.open();
}); });
// ------------------------------------------------------------------ // ------------------------------------------------------------------
$(window.hyperion).on("cmd-settings-update",function(event){ $(window.hyperion).on("cmd-settings-update", function (event) {
var obj = event.response.data var obj = event.response.data
if ( obj.leds) { if (obj.leds || obj.grabberV4L2) {
console.log("ledsim: cmd-settings-update", event.response.data); //console.log("ledsim: cmd-settings-update", event.response.data);
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) { Object.getOwnPropertyNames(obj).forEach(function (val, idx, array) {
window.serverInfo[val] = obj[val]; window.serverInfo[val] = obj[val];
}); });
leds = window.serverConfig.leds leds = window.serverConfig.leds;
grabberConfig = window.serverConfig.grabberV4L2;
updateLedLayout(); updateLedLayout();
} }
}); });
function resetImage(){ $(window.hyperion).on("cmd-priorities-update", function (event) {
if (getStorage("darkMode", false) == "on") { //console.log("cmd-priorities-update", event.response.data);
imageCanvasNodeCtx.clear();
} else { var prios = event.response.data.priorities;
imageCanvasNodeCtx.fillStyle = "rgb(225,225,225)" if (prios.length > 0)
imageCanvasNodeCtx.fillRect(0, 0, canvas_width, canvas_height); {
//Clear image when new input
if (prios[0].componentId !== activeComponent) {
resetImage();
activeComponent = prios[0].componentId;
}
else if (!prios[0].active) {
resetImage();
}
}
else {
resetImage();
} }
var image = document.getElementById('navbar_brand_logo'); });
function resetImage() {
if (typeof imageCanvasNodeCtx !== "undefined") {
imageCanvasNodeCtx.fillStyle = "#1c1c1c"; //90% gray
imageCanvasNodeCtx.fillRect(0, 0, canvas_width, canvas_height);
imageCanvasNodeCtx.drawImage(image, canvas_width / 2 - image.width / 2, canvas_height / 2 - image.height / 2, image.width, image.height); imageCanvasNodeCtx.drawImage(image, canvas_width / 2 - image.width / 2, canvas_height / 2 - image.height / 2, image.width, image.height);
} }
}
}); });

View File

@ -1839,7 +1839,14 @@ JSONEditor.AbstractEditor = Class.extend({
this.parent = null; this.parent = null;
}, },
getDefault: function() { getDefault: function() {
if(this.schema["default"]) return this.schema["default"]; var def = this.schema["default"];
if(def) {
if (typeof def === "string") {
return $.i18n(def);
} else {
return def;
}
}
if(this.schema["enum"]) return this.schema["enum"][0]; if(this.schema["enum"]) return this.schema["enum"][0];
var type = this.schema.type || this.schema.oneOf; var type = this.schema.type || this.schema.oneOf;

View File

@ -1,10 +1,5 @@
var storedAccess;
var storedLang;
var availLang = ['cs', 'de', 'en', 'es', 'fr', 'it', 'nl', 'nb', 'pl', 'pt', 'ro', 'sv', 'vi', 'ru', 'tr', 'zh-CN'];
var availLangText = ['Čeština', 'Deutsch', 'English', 'Español', 'Français', 'Italiano', 'Nederlands', 'Norsk Bokmål', 'Polski', 'Português', 'Română', 'Svenska', 'Tiếng Việt', 'русский', 'Türkçe', '汉语'];
var availAccess = ['default', 'advanced', 'expert']; var availAccess = ['default', 'advanced', 'expert'];
var storedAccess;
//$.i18n.debug = true;
//Change Password //Change Password
function changePassword(){ function changePassword(){
@ -26,48 +21,11 @@ function changePassword(){
}); });
} }
$(document).ready( function() { $(document).ready(function () {
//i18n if (!storageComp()) {
function initTrans(lc){
if (lc == 'auto')
{
$.i18n().load().done(
function() {
performTranslation();
});
}
else
{
$.i18n().locale = lc;
$.i18n().load( "i18n", lc ).done(
function() {
performTranslation();
});
}
}
if (storageComp())
{
storedLang = getStorage("langcode");
if (storedLang == null)
{
setStorage("langcode", 'auto');
storedLang = 'auto';
initTrans(storedLang);
}
else
{
initTrans(storedLang);
}
}
else
{
showInfoDialog('warning', "Can't store settings", "Your browser doesn't support localStorage. You can't save a specific language setting (fallback to 'auto detection') and access level (fallback to 'default'). Some wizards may be hidden. You could still use the webinterface without further issues"); showInfoDialog('warning', "Can't store settings", "Your browser doesn't support localStorage. You can't save a specific language setting (fallback to 'auto detection') and access level (fallback to 'default'). Some wizards may be hidden. You could still use the webinterface without further issues");
initTrans('auto'); $('#language-select').attr("disabled", true);
storedLang = 'auto';
storedAccess = "default";
$('#btn_setlang').attr("disabled", true);
$('#btn_setaccess').attr("disabled", true); $('#btn_setaccess').attr("disabled", true);
} }
@ -75,10 +33,14 @@ $(document).ready( function() {
//access //access
storedAccess = getStorage("accesslevel"); storedAccess = getStorage("accesslevel");
if (storedAccess == null) if (storedAccess == null) {
{
setStorage("accesslevel", "default");
storedAccess = "default"; storedAccess = "default";
setStorage("accesslevel", storedAccess);
}
if (!storageComp()) {
showInfoDialog('warning', $.i18n('InfoDialog_nostorage_title'), $.i18n('InfoDialog_nostorage_text'));
$('#btn_setlang').attr("disabled", true);
} }
$('#btn_setaccess').off().on('click',function() { $('#btn_setaccess').off().on('click',function() {

100
assets/webconfig/js/ui_utils.js Executable file → Normal file
View File

@ -67,7 +67,7 @@ function updateSessions() {
function validateDuration(d) { function validateDuration(d) {
if (typeof d === "undefined" || d < 0) if (typeof d === "undefined" || d < 0)
return 0; return ENDLESS;
else else
return d *= 1000; return d *= 1000;
} }
@ -162,25 +162,22 @@ function initLanguageSelection() {
var langLocale = storedLang; var langLocale = storedLang;
// If no language has been set, resolve browser locale
if (langLocale === 'auto') {
langLocale = $.i18n().locale.substring(0, 2);
}
// Resolve text for language code
var langText = 'Please Select';
//Test, if language is supported by hyperion //Test, if language is supported by hyperion
var langIdx = availLang.indexOf(langLocale); var langIdx = availLang.indexOf(langLocale);
if (langIdx > -1) { if (langIdx > -1) {
langText = availLangText[langIdx]; langText = availLangText[langIdx];
} } else {
else {
// If language is not supported by hyperion, try fallback language // If language is not supported by hyperion, try fallback language
langLocale = $.i18n().options.fallbackLocale.substring(0, 2); langLocale = $.i18n().options.fallbackLocale.substring(0, 2);
langIdx = availLang.indexOf(langLocale); langIdx = availLang.indexOf(langLocale);
if (langIdx > -1) { if (langIdx > -1) {
langText = availLangText[langIdx]; langText = availLangText[langIdx];
} else {
langLocale = 'en';
langIdx = availLang.indexOf(langLocale);
if (langIdx > -1) {
langText = availLangText[langIdx];
}
} }
} }
@ -195,10 +192,12 @@ function updateUiOnInstance(inst) {
$('#btn_hypinstanceswitch').toggle(true); $('#btn_hypinstanceswitch').toggle(true);
$('#active_instance_dropdown').prop('disabled', false); $('#active_instance_dropdown').prop('disabled', false);
$('#active_instance_dropdown').css('cursor', 'pointer'); $('#active_instance_dropdown').css('cursor', 'pointer');
$("#active_instance_dropdown").css("pointer-events", "auto");
} else { } else {
$('#btn_hypinstanceswitch').toggle(false); $('#btn_hypinstanceswitch').toggle(false);
$('#active_instance_dropdown').prop('disabled', true); $('#active_instance_dropdown').prop('disabled', true);
$("#active_instance_dropdown").css('cursor', 'default'); $("#active_instance_dropdown").css('cursor', 'default');
$("#active_instance_dropdown").css("pointer-events", "none");
} }
} }
@ -288,10 +287,12 @@ function showInfoDialog(type, header, message) {
} }
else if (type == "changePassword") { else if (type == "changePassword") {
$('#id_body_rename').html('<i style="margin-bottom:20px" class="fa fa-key modal-icon-edit"><br>'); $('#id_body_rename').html('<i style="margin-bottom:20px" class="fa fa-key modal-icon-edit"><br>');
$('#id_body_rename').append('<h4>' + header + '</h4>'); $('#id_body_rename').append('<h4>' + header + '</h4><br>');
$('#id_body_rename').append('<input class="form-control" id="oldPw" placeholder="Old" type="text"> <br />'); $('#id_body_rename').append('<div class="row"><div class="col-md-3"><p class="text-left">' + $.i18n('infoDialog_password_current_text') +
$('#id_body_rename').append('<input class="form-control" id="newPw" placeholder="New" type="password">'); '</p></div><div class="col-md-8"><input class="form-control" id="oldPw" placeholder="Old" type="password"></div></div><br>');
$('#id_footer_rename').html('<button type="button" id="id_btn_ok" class="btn btn-success" data-dismiss-modal="#modal_dialog_rename" disabled><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_ok') + '</button>'); $('#id_body_rename').append('<div class="row"><div class="col-md-3"><p class="text-left">' + $.i18n('infoDialog_password_new_text')+
'</p></div><div class="col-md-8"><input class="form-control" id="newPw" placeholder="New" type="password"></div></div>');
$('#id_footer_rename').html('<button type="button" id="id_btn_ok" class="btn btn-success" data-dismiss-modal="#modal_dialog_rename" disabled><i class="fa fa-fw fa-save"></ul>' + $.i18n('general_btn_ok') + '</button>');
$('#id_footer_rename').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'); $('#id_footer_rename').append('<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>');
} }
else if (type == "checklist") { else if (type == "checklist") {
@ -461,7 +462,8 @@ function createJsonEditor(container, schema, setconfig, usePanel, arrayre) {
return editor; return editor;
} }
function updateJsonEditorSelection(editor, key, addElements, newEnumVals, newTitelVals, newDefaultVal, addSelect, addCustom, addCustomAsFirst, customText) { function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVals, newTitelVals, newDefaultVal, addSelect, addCustom, addCustomAsFirst, customText) {
var editor = rootEditor.getEditor(path);
var orginalProperties = editor.schema.properties[key]; var orginalProperties = editor.schema.properties[key];
var newSchema = []; var newSchema = [];
@ -536,13 +538,15 @@ function updateJsonEditorSelection(editor, key, addElements, newEnumVals, newTit
editor.original_schema.properties[key] = orginalProperties; editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key]; editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
editor.removeObjectProperty(key); editor.removeObjectProperty(key);
delete editor.cached_editors[key]; delete editor.cached_editors[key];
editor.addObjectProperty(key); editor.addObjectProperty(key);
} }
function updateJsonEditorMultiSelection(editor, key, addElements, newEnumVals, newTitelVals, newDefaultVal) { function updateJsonEditorMultiSelection(rootEditor, path, key, addElements, newEnumVals, newTitelVals, newDefaultVal) {
var editor = rootEditor.getEditor(path);
var orginalProperties = editor.schema.properties[key]; var orginalProperties = editor.schema.properties[key];
var newSchema = []; var newSchema = [];
@ -593,36 +597,55 @@ function updateJsonEditorMultiSelection(editor, key, addElements, newEnumVals, n
editor.original_schema.properties[key] = orginalProperties; editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key]; editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
editor.removeObjectProperty(key); editor.removeObjectProperty(key);
delete editor.cached_editors[key]; delete editor.cached_editors[key];
editor.addObjectProperty(key); editor.addObjectProperty(key);
} }
function updateJsonEditorRange(editor, key, minimum, maximum, defaultValue, step) { function updateJsonEditorRange(rootEditor, path, key, minimum, maximum, defaultValue, step, clear) {
var editor = rootEditor.getEditor(path);
//Preserve current value when updating range
var currentValue = rootEditor.getEditor(path + "." + key).getValue();
var orginalProperties = editor.schema.properties[key]; var orginalProperties = editor.schema.properties[key];
var newSchema = []; var newSchema = [];
newSchema[key] = orginalProperties; newSchema[key] = orginalProperties;
if (minimum) { if (clear) {
delete newSchema[key]["minimum"];
delete newSchema[key]["maximum"];
delete newSchema[key]["default"];
delete newSchema[key]["step"];
}
if (typeof minimum !== "undefined") {
newSchema[key]["minimum"] = minimum; newSchema[key]["minimum"] = minimum;
} }
if (maximum) { if (typeof maximum !== "undefined") {
newSchema[key]["maximum"] = maximum; newSchema[key]["maximum"] = maximum;
} }
if (defaultValue) { if (typeof defaultValue !== "undefined") {
newSchema[key]["default"] = defaultValue; newSchema[key]["default"] = defaultValue;
currentValue = defaultValue;
} }
if (step) {
if (typeof step !== "undefined") {
newSchema[key]["step"] = step; newSchema[key]["step"] = step;
} }
editor.original_schema.properties[key] = orginalProperties; editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key]; editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
editor.removeObjectProperty(key); editor.removeObjectProperty(key);
delete editor.cached_editors[key]; delete editor.cached_editors[key];
editor.addObjectProperty(key); editor.addObjectProperty(key);
// Restore current (new default) value for new range
rootEditor.getEditor(path + "." + key).setValue(currentValue);
} }
function buildWL(link, linkt, cl) { function buildWL(link, linkt, cl) {
@ -807,14 +830,15 @@ function createRow(id) {
return el; return el;
} }
function createOptPanel(phicon, phead, bodyid, footerid, css) { function createOptPanel(phicon, phead, bodyid, footerid, css, panelId) {
phead = '<i class="fa ' + phicon + ' fa-fw"></i>' + phead; phead = '<i class="fa ' + phicon + ' fa-fw"></i>' + phead;
var pfooter = document.createElement('button'); var pfooter = document.createElement('button');
pfooter.className = "btn btn-primary"; pfooter.className = "btn btn-primary";
pfooter.setAttribute("id", footerid); pfooter.setAttribute("id", footerid);
pfooter.innerHTML = '<i class="fa fa-fw fa-save"></i>' + $.i18n('general_button_savesettings'); pfooter.innerHTML = '<i class="fa fa-fw fa-save"></i>' + $.i18n('general_button_savesettings');
return createPanel(phead, "", pfooter, "panel-default", bodyid, css); return createPanel(phead, "", pfooter, "panel-default", bodyid, css, panelId);
} }
function compareTwoValues(key1, key2, order = 'asc') { function compareTwoValues(key1, key2, order = 'asc') {
@ -1141,14 +1165,34 @@ function isAccessLevelCompliant(accessLevel) {
function showInputOptions(path, elements, state) { function showInputOptions(path, elements, state) {
for (var i = 0; i < elements.length; i++) { for (var i = 0; i < elements.length; i++) {
$('[data-schemapath="' + path + '.' + elements[i] + '"]').toggle(state); $('[data-schemapath="root.' + path + '.' + elements[i] + '"]').toggle(state);
} }
} }
function showInputOptionsForKey(editor, item, showForKey, state) { function showInputOptionForItem(editor, path, item, state) {
var accessLevel = editor.schema.properties[path].properties[item].access;
// Enable element only, if access level compliant
if (!state || isAccessLevelCompliant(accessLevel)) {
showInputOptions(path, [item], state);
}
}
function showInputOptionsForKey(editor, item, showForKeys, state) {
var elements = []; var elements = [];
var keysToshow = [];
if (Array.isArray(showForKeys)) {
keysToshow = showForKeys;
} else {
if (typeof showForKeys === 'string') {
keysToshow.push(showForKeys);
} else {
return
}
}
for (var key in editor.schema.properties[item].properties) { for (var key in editor.schema.properties[item].properties) {
if (showForKey !== key) { if ($.inArray(key, keysToshow) === -1) {
var accessLevel = editor.schema.properties[item].properties[key].access; var accessLevel = editor.schema.properties[item].properties[key].access;
//Always disable all elements, but only enable elements, if access level compliant //Always disable all elements, but only enable elements, if access level compliant
@ -1157,5 +1201,5 @@ function showInputOptionsForKey(editor, item, showForKey, state) {
} }
} }
} }
showInputOptions("root." + item, elements, state); showInputOptions(item, elements, state);
} }

1
bin/compile.sh Executable file → Normal file
View File

@ -25,7 +25,6 @@ sudo apt-get install \
libavahi-core-dev \ libavahi-core-dev \
libavahi-compat-libdnssd-dev \ libavahi-compat-libdnssd-dev \
libssl-dev \ libssl-dev \
libjpeg-dev \
libqt5sql5-sqlite \ libqt5sql5-sqlite \
libqt5svg5-dev \ libqt5svg5-dev \
zlib1g-dev \ zlib1g-dev \

17
bin/scripts/docker-compile.sh Normal file → Executable file
View File

@ -15,6 +15,8 @@ BUILD_TAG="buster"
BUILD_PACKAGES=true BUILD_PACKAGES=true
# packages string inserted to cmake cmd # packages string inserted to cmake cmd
PACKAGES="" PACKAGES=""
# platform string inserted to cmake cmd
BUILD_PLATFORM=""
#Run build using GitHub code files #Run build using GitHub code files
BUILD_LOCAL=0 BUILD_LOCAL=0
#Build from scratch #Build from scratch
@ -73,6 +75,7 @@ echo "########################################################
# docker-compile.sh -p true # If true, build packages with CPack # docker-compile.sh -p true # If true, build packages with CPack
# docker-compile.sh -l # Run build using local code files # docker-compile.sh -l # Run build using local code files
# docker-compile.sh -c # Run incremental build, i.e. do not delete files created during previous build # docker-compile.sh -c # Run incremental build, i.e. do not delete files created during previous build
# docker-compile.sh -f x11 # cmake PLATFORM parameter
# More informations to docker tags at: https://github.com/Hyperion-Project/hyperion.docker-ci" # More informations to docker tags at: https://github.com/Hyperion-Project/hyperion.docker-ci"
} }
@ -84,7 +87,7 @@ function log () {
echo "Compile Hyperion using a Docker container" echo "Compile Hyperion using a Docker container"
while getopts i:t:b:p:lcvh option while getopts i:t:b:p:f:lcvh option
do do
case "${option}" case "${option}"
in in
@ -92,6 +95,7 @@ do
t) BUILD_TAG=${OPTARG};; t) BUILD_TAG=${OPTARG};;
b) BUILD_TYPE=${OPTARG};; b) BUILD_TYPE=${OPTARG};;
p) BUILD_PACKAGES=${OPTARG};; p) BUILD_PACKAGES=${OPTARG};;
f) BUILD_PLATFORM=${OPTARG,,};;
l) BUILD_LOCAL=1;; l) BUILD_LOCAL=1;;
c) BUILD_INCREMENTAL=1;; c) BUILD_INCREMENTAL=1;;
v) _VERBOSE=1;; v) _VERBOSE=1;;
@ -104,7 +108,12 @@ if [ ${BUILD_PACKAGES} == "true" ]; then
PACKAGES="package" PACKAGES="package"
fi fi
echo "---> Initialize with IMAGE:TAG=${BUILD_IMAGE}:${BUILD_TAG}, BUILD_TYPE=${BUILD_TYPE}, BUILD_PACKAGES=${BUILD_PACKAGES}, BUILD_LOCAL=${BUILD_LOCAL}, BUILD_INCREMENTAL=${BUILD_INCREMENTAL}" # determine platform cmake parameter
if [[ ! -z ${BUILD_PLATFORM} ]]; then
PLATFORM="-DPLATFORM=${BUILD_PLATFORM}"
fi
echo "---> Initialize with IMAGE:TAG=${BUILD_IMAGE}:${BUILD_TAG}, BUILD_TYPE=${BUILD_TYPE}, BUILD_PACKAGES=${BUILD_PACKAGES}, PLATFORM=${BUILD_PLATFORM}, BUILD_LOCAL=${BUILD_LOCAL}, BUILD_INCREMENTAL=${BUILD_INCREMENTAL}"
log "---> BASE_PATH = ${BASE_PATH}" log "---> BASE_PATH = ${BASE_PATH}"
CODE_PATH=${BASE_PATH}; CODE_PATH=${BASE_PATH};
@ -155,7 +164,7 @@ $DOCKER run --rm \
-v "${CODE_PATH}/:/source:rw" \ -v "${CODE_PATH}/:/source:rw" \
${REGISTRY_URL}/${BUILD_IMAGE}:${BUILD_TAG} \ ${REGISTRY_URL}/${BUILD_IMAGE}:${BUILD_TAG} \
/bin/bash -c "mkdir -p /source/${BUILD_DIR} && cd /source/${BUILD_DIR} && /bin/bash -c "mkdir -p /source/${BUILD_DIR} && cd /source/${BUILD_DIR} &&
cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. || exit 2 && cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${PLATFORM} .. || exit 2 &&
make -j $(nproc) ${PACKAGES} || exit 3 || : && make -j $(nproc) ${PACKAGES} || exit 3 || : &&
exit 0; exit 0;
exit 1 " || { echo "---> Hyperion compilation failed! Abort"; exit 4; } exit 1 " || { echo "---> Hyperion compilation failed! Abort"; exit 4; }
@ -164,7 +173,6 @@ DOCKERRC=${?}
# overwrite file owner to current user # overwrite file owner to current user
sudo chown -fR $(stat -c "%U:%G" ${BASE_PATH}) ${BUILD_PATH} sudo chown -fR $(stat -c "%U:%G" ${BASE_PATH}) ${BUILD_PATH}
sudo chown -fR $(stat -c "%U:%G" ${BASE_PATH}) ${DEPLOY_PATH}
if [ ${DOCKERRC} == 0 ]; then if [ ${DOCKERRC} == 0 ]; then
if [ ${BUILD_LOCAL} == 1 ]; then if [ ${BUILD_LOCAL} == 1 ]; then
@ -175,6 +183,7 @@ if [ ${DOCKERRC} == 0 ]; then
echo "---> Copying packages to host folder: ${DEPLOY_PATH}" && echo "---> Copying packages to host folder: ${DEPLOY_PATH}" &&
cp -v ${BUILD_PATH}/Hyperion-* ${DEPLOY_PATH} 2>/dev/null cp -v ${BUILD_PATH}/Hyperion-* ${DEPLOY_PATH} 2>/dev/null
echo "---> Find deployment packages in: ${DEPLOY_PATH}" echo "---> Find deployment packages in: ${DEPLOY_PATH}"
sudo chown -fR $(stat -c "%U:%G" ${BASE_PATH}) ${DEPLOY_PATH}
fi fi
fi fi
echo "---> Script finished [${DOCKERRC}]" echo "---> Script finished [${DOCKERRC}]"

View File

@ -241,7 +241,7 @@ macro(DeployWindows TARGET)
list(REMOVE_AT DEPENDENCIES 0 1) list(REMOVE_AT DEPENDENCIES 0 1)
endwhile() endwhile()
# Copy OpenSSL Libs # Copy libssl/libcrypto to 'hyperion'
if (OPENSSL_FOUND) if (OPENSSL_FOUND)
string(REGEX MATCHALL "[0-9]+" openssl_versions "${OPENSSL_VERSION}") string(REGEX MATCHALL "[0-9]+" openssl_versions "${OPENSSL_VERSION}")
list(GET openssl_versions 0 openssl_version_major) list(GET openssl_versions 0 openssl_version_major)
@ -271,6 +271,27 @@ macro(DeployWindows TARGET)
) )
endif(OPENSSL_FOUND) endif(OPENSSL_FOUND)
# Copy libjpeg-turbo to 'hyperion'
if (ENABLE_MF AND TURBOJPEG_FOUND)
find_file(TURBOJPEG_DLL
NAMES "turbojpeg.dll"
PATHS ${TurboJPEG_INCLUDE_DIRS}/.. ${TurboJPEG_INCLUDE_DIRS}/../bin
NO_DEFAULT_PATH
)
find_file(JPEG_DLL
NAMES "jpeg62.dll"
PATHS ${TurboJPEG_INCLUDE_DIRS}/.. ${TurboJPEG_INCLUDE_DIRS}/../bin
NO_DEFAULT_PATH
)
install(
FILES ${TURBOJPEG_DLL} ${JPEG_DLL}
DESTINATION "bin"
COMPONENT "Hyperion"
)
endif()
# Create a qt.conf file in 'bin' to override hard-coded search paths in Qt plugins # Create a qt.conf file in 'bin' to override hard-coded search paths in Qt plugins
file(WRITE "${CMAKE_BINARY_DIR}/qt.conf" "[Paths]\nPlugins=../lib/\n") file(WRITE "${CMAKE_BINARY_DIR}/qt.conf" "[Paths]\nPlugins=../lib/\n")
install( install(
@ -318,6 +339,30 @@ macro(DeployWindows TARGET)
) )
endforeach() endforeach()
if(ENABLE_DX)
# Download DirectX End-User Runtimes (June 2010)
set(url "https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe")
if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/dx_redist.exe")
file(DOWNLOAD "${url}" "${CMAKE_CURRENT_BINARY_DIR}/dx_redist.exe"
STATUS result
)
# Check if the download is successful
list(GET result 0 result_code)
if(NOT result_code EQUAL 0)
list(GET result 1 reason)
message(FATAL_ERROR "Could not download DirectX End-User Runtimes: ${reason}")
endif()
endif()
# Copy DirectX End-User Runtimes to 'hyperion'
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/dx_redist.exe
DESTINATION "bin"
COMPONENT "Hyperion"
)
endif (ENABLE_DX)
else() else()
# Run CMake after target was built # Run CMake after target was built
add_custom_command( add_custom_command(

View File

@ -3,15 +3,31 @@
# TurboJPEG_INCLUDE_DIRS # TurboJPEG_INCLUDE_DIRS
# TurboJPEG_LIBRARY # TurboJPEG_LIBRARY
find_path(TurboJPEG_INCLUDE_DIRS if (ENABLE_MF)
find_path(TurboJPEG_INCLUDE_DIRS
NAMES turbojpeg.h
PATHS
"C:/libjpeg-turbo64"
PATH_SUFFIXES include
)
find_library(TurboJPEG_LIBRARY
NAMES turbojpeg turbojpeg-static
PATHS
"C:/libjpeg-turbo64"
PATH_SUFFIXES bin lib
)
else()
find_path(TurboJPEG_INCLUDE_DIRS
NAMES turbojpeg.h NAMES turbojpeg.h
PATH_SUFFIXES include PATH_SUFFIXES include
) )
find_library(TurboJPEG_LIBRARY find_library(TurboJPEG_LIBRARY
NAMES turbojpeg turbojpeg-static NAMES turbojpeg turbojpeg-static
PATH_SUFFIXES bin lib PATH_SUFFIXES bin lib
) )
endif()
if(TurboJPEG_INCLUDE_DIRS AND TurboJPEG_LIBRARY) if(TurboJPEG_INCLUDE_DIRS AND TurboJPEG_LIBRARY)
include(CheckCSourceCompiles) include(CheckCSourceCompiles)
@ -26,7 +42,7 @@ if(TurboJPEG_INCLUDE_DIRS AND TurboJPEG_LIBRARY)
endif() endif()
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(TurboJpeg find_package_handle_standard_args(TurboJPEG
FOUND_VAR TURBOJPEG_FOUND FOUND_VAR TURBOJPEG_FOUND
REQUIRED_VARS TurboJPEG_LIBRARY TurboJPEG_INCLUDE_DIRS TURBOJPEG_WORKS REQUIRED_VARS TurboJPEG_LIBRARY TurboJPEG_INCLUDE_DIRS TURBOJPEG_WORKS
TurboJPEG_INCLUDE_DIRS TurboJPEG_LIBRARY TurboJPEG_INCLUDE_DIRS TurboJPEG_LIBRARY

View File

@ -16,7 +16,7 @@ install_file()
} }
echo "---Hyperion ambient light postinstall ---" echo "--- Hyperion ambient light postinstall ---"
#check system #check system
CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835\|BCM2836\|BCM2837\|BCM2711' /proc/cpuinfo` CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835\|BCM2836\|BCM2837\|BCM2711' /proc/cpuinfo`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -790,7 +790,13 @@ Section "-Core installation"
;End: ;End:
; Install Visual c++ Redistributable ; Install Visual c++ Redistributable
ExecWait '"$INSTDIR\bin\vc_redist.x64.exe" /install /quiet' ExecWait '"$INSTDIR\bin\vc_redist.x64.exe" /install /quiet /norestart'
; Install DirectX 9 Redistributable
ExecWait '"$INSTDIR\bin\dx_redist.exe" /q /t:"$INSTDIR\tmp"'
ExecWait '"$INSTDIR\tmp\DXSETUP.exe" /silent'
Delete '$INSTDIR\tmp\*.*'
RMDir '$INSTDIR\tmp'
SectionEnd SectionEnd

View File

@ -1,535 +0,0 @@
// This is a example config (hyperion.config.json) with comments, in any case you need to create your own one with HyperCon!
// location of all configs: /etc/hyperion
// Webpage: https://www.hyperion-project.org
{
/// general Settings
/// * 'name' : The user friendly name of the hyperion instance (used for network things)
/// * 'versionBranch' : Which branch should be used for hyperion version
/// * 'showOptHelp' : Show option expanations at the webui. Highly recommended for beginners.
"general" :
{
"name" : "MyHyperionConfig",
"watchedVersionBranch" : "Stable",
"showOptHelp" : true
},
/// set log level: silent warn verbose debug
"logger" :
{
"level" : "warn"
},
/// Device configuration contains the following fields:
/// * 'name' : The user friendly name of the device (only used for display purposes)
/// * 'type' : The type of the device
/// * [device type specific configuration]
/// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.).
/// * 'rewriteTime': in ms. Data is resend to leds, if no new data is available in thistime. 0 means no refresh
"device" :
{
"type" : "file",
"hardwareLedCount" : 1,
"output" : "/dev/null",
"rate" : 1000000,
"colorOrder" : "rgb",
"rewriteTime": 5000
},
/// Color manipulation configuration used to tune the output colors to specific surroundings.
/// The configuration contains a list of color-transforms. Each transform contains the
/// following fields:
/// * 'imageToLedMappingType' : multicolor_mean - every led has it's own calculatedmean color
/// unicolor_mean - every led has same color, color is the mean of whole image
/// * 'channelAdjustment'
/// * 'id' : The unique identifier of the channel adjustments (eg 'device_1')
/// * 'leds' : The indices (or index ranges) of the leds to which this channel adjustment applies
/// (eg '0-5, 9, 11, 12-17'). The indices are zero based.
/// * 'white'/'red'/'green'/'blue'/'cyan'/'magenta'/'yellow' : Array of RGB to adjust the output color
/// * 'gammaRed'/'gammaGreen'/'gammaBlue' : Gamma value for each channel
/// * 'id' : The unique identifier of the channel adjustments (eg 'device_1')
/// * 'id' : The unique identifier of the channel adjustments (eg 'device_1')
/// * 'backlightThreshold' : Minimum brightness (backlight)
/// * 'backlightColored' : backlight with color, instead of white
/// * 'brightness' : overall brightness
/// * 'brightnessCompensation' : 100 means brightness differences are compensated (white is as bright as red, is as bright as yellow.
/// 0 means white is 3x brighter than red, yellow is 2x brighter than red
"color" :
{
"imageToLedMappingType" : "multicolor_mean",
"channelAdjustment" :
[
{
"id" : "default",
"leds" : "*",
"white" : [255,255,255],
"red" : [255,0,0],
"green" : [0,255,0],
"blue" : [0,0,255],
"cyan" : [0,255,255],
"magenta" : [255,0,255],
"yellow" : [255,255,0],
"gammaRed" : 1.5,
"gammaGreen" : 1.5,
"gammaBlue" : 1.5,
"backlightThreshold" : 0,
"backlightColored" : false,
"brightness" : 100,
"brightnessCompensation" : 80
}
]
},
/// smoothing
/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning
/// parameters:
/// - 'enable' Enable or disable the smoothing (true/false)
/// - 'type' The type of smoothing algorithm ('linear' or 'none')
/// - 'time_ms' The time constant for smoothing algorithm in milliseconds
/// - 'updateFrequency' The update frequency of the leds in Hz
/// - 'updateDelay' The delay of the output to leds (in periods of smoothing)
/// - 'continuousOutput' Flag for enabling continuous output to Leds regardless of new input or not
"smoothing" :
{
"enable" : true,
"type" : "linear",
"time_ms" : 200,
"updateFrequency" : 25.0000,
"updateDelay" : 0,
"continuousOutput" : true
},
/// Configuration for the embedded V4L2 grabber
/// * device : V4L2 Device to use [default="auto"] (Auto detection)
/// * width : The width of the grabbed frames (pixels) [default=0]
/// * height : The height of the grabbed frames (pixels) [default=0]
/// * standard : Video standard (PAL/NTSC/SECAM/NO_CHANGE) [default="NO_CHANGE"]
/// * sizeDecimation : Size decimation factor [default=8]
/// * cropLeft : Cropping from the left [default=0]
/// * cropRight : Cropping from the right [default=0]
/// * cropTop : Cropping from the top [default=0]
/// * cropBottom : Cropping from the bottom [default=0]
/// * signalDetection : enable/disable signal detection [default=false]
/// * cecDetection : enable/disable cec detection [default=false]
/// * redSignalThreshold : Signal threshold for the red channel between 0 and 100 [default=5]
/// * greenSignalThreshold : Signal threshold for the green channel between 0 and 100 [default=5]
/// * blueSignalThreshold : Signal threshold for the blue channel between 0 and 100 [default=5]
/// * sDHOffsetMin : area for signal detection - horizontal minimum offset value. Values between 0.0 and 1.0
/// * sDVOffsetMin : area for signal detection - vertical minimum offset value. Values between 0.0 and 1.0
/// * sDHOffsetMax : area for signal detection - horizontal maximum offset value. Values between 0.0 and 1.0
/// * sDVOffsetMax : area for signal detection - vertical maximum offset value. Values between 0.0 and 1.0
"grabberV4L2" :
{
"device" : "auto",
"width" : 0,
"height" : 0,
"standard" : "NO_CHANGE",
"sizeDecimation" : 8,
"priority" : 240,
"cropLeft" : 0,
"cropRight" : 0,
"cropTop" : 0,
"cropBottom" : 0,
"redSignalThreshold" : 5,
"greenSignalThreshold" : 5,
"blueSignalThreshold" : 5,
"signalDetection" : false,
"cecDetection" : false,
"sDVOffsetMin" : 0.25,
"sDHOffsetMin" : 0.25,
"sDVOffsetMax" : 0.75,
"sDHOffsetMax" : 0.75
},
/// The configuration for the frame-grabber, contains the following items:
/// * type : type of grabber. (auto|osx|dispmanx|amlogic|x11|xcb|framebuffer|qt) [auto]
/// * width : The width of the grabbed frames [pixels]
/// * height : The height of the grabbed frames [pixels]
/// * frequency_Hz : The frequency of the frame grab [Hz]
/// * ATTENTION : Power-of-Two resolution is not supported and leads to unexpected behaviour!
"framegrabber" :
{
// for all type of grabbers
"type" : "framebuffer",
"frequency_Hz" : 10,
"cropLeft" : 0,
"cropRight" : 0,
"cropTop" : 0,
"cropBottom" : 0,
// valid for grabber: osx|dispmanx|amlogic|framebuffer
"width" : 96,
"height" : 96,
// valid for x11|xcb|qt
"pixelDecimation" : 8,
// valid for qt
"display" 0
},
/// The black border configuration, contains the following items:
/// * enable : true if the detector should be activated
/// * threshold : Value below which a pixel is regarded as black (value between 0 and 100 [%])
/// * unknownFrameCnt : Number of frames without any detection before the border is set to 0 (default 600)
/// * borderFrameCnt : Number of frames before a consistent detected border gets set (default 50)
/// * maxInconsistentCnt : Number of inconsistent frames that are ignored before a new border gets a chance to proof consistency
/// * blurRemoveCnt : Number of pixels that get removed from the detected border to cut away blur (default 1)
/// * mode : Border detection mode (values=default,classic,osd,letterbox)
"blackborderdetector" :
{
"enable" : true,
"threshold" : 5,
"unknownFrameCnt" : 600,
"borderFrameCnt" : 50,
"maxInconsistentCnt" : 10,
"blurRemoveCnt" : 1,
"mode" : "default"
},
/// foregroundEffect sets a "booteffect" or "bootcolor" during startup for a given period in ms (duration_ms)
/// * enable : if true, foreground effect is enabled
/// * type : choose between "color" or "effect"
/// * color : if type is color, a color is used (RGB) (example: [0,0,255])
/// * effect : if type is effect a effect is used (example: "Rainbow swirl fast")
/// * duration_ms : The duration of the selected effect or color (0=endless)
/// HINT: "foregroundEffect" starts always with priority 0, so it blocks all remotes and grabbers if the duration_ms is endless (0)
"foregroundEffect" :
{
"enable" : true,
"type" : "effect",
"color" : [0,0,255],
"effect" : "Rainbow swirl fast",
"duration_ms" : 3000
},
/// backgroundEffect sets a background effect or color. It is used when all capture devices are stopped (manual via remote). Could be also selected via priorities selection.
/// * enable : if true, background effect is enabled
/// * type : choose between "color" or "effect"
/// * color : if type is color, a color is used (RGB) (example: [255,134,0])
/// * effect : if type is effect a effect is used (example: "Rainbow swirl fast")
"backgroundEffect" :
{
"enable" : true,
"type" : "effect",
"color" : [255,138,0],
"effect" : "Warm mood blobs"
},
/// The configuration of the Json/Proto forwarder. Forward messages to multiple instances of Hyperion on same and/or other hosts
/// 'proto' is mostly used for video streams and 'json' for effects
/// * enable : Enable or disable the forwarder (true/false)
/// * proto : Proto server adress and port of your target. Syntax:[IP:PORT] -> ["127.0.0.1:19401"] or more instances to forward ["127.0.0.1:19401","192.168.0.24:19403"]
/// * json : Json server adress and port of your target. Syntax:[IP:PORT] -> ["127.0.0.1:19446"] or more instances to forward ["127.0.0.1:19446","192.168.0.24:19448"]
/// HINT:If you redirect to "127.0.0.1" (localhost) you could start a second hyperion with another device/led config!
/// Be sure your client(s) is/are listening on the configured ports. The second Hyperion (if used) also needs to be configured! (WebUI -> Settings Level (Expert) -> Configuration -> Network Services -> Forwarder)
"forwarder" :
{
"enable" : false,
"flat" : ["127.0.0.1:19401"],
"json" : ["127.0.0.1:19446"]
},
/// The configuration of the Json server which enables the json remote interface
/// * port : Port at which the json server is started
"jsonServer" :
{
"port" : 19444
},
/// The configuration of the Flatbuffer server which enables the Flatbuffer remote interface
/// * port : Port at which the flatbuffer server is started
"flatbufServer" :
{
"enable" : true,
"port" : 19400,
"timeout" : 5
},
/// The configuration of the Protobuffer server which enables the Protobuffer remote interface
/// * port : Port at which the protobuffer server is started
"protoServer" :
{
"enable" : true,
"port" : 19445,
"timeout" : 5
},
/// The configuration of the boblight server which enables the boblight remote interface
/// * enable : Enable or disable the boblight server (true/false)
/// * port : Port at which the boblight server is started
/// * priority : Priority of the boblight server (Default=128) HINT: lower value result in HIGHER priority!
"boblightServer" :
{
"enable" : false,
"port" : 19333,
"priority" : 128
},
/// Configuration of the Hyperion webserver
/// * document_root : path to hyperion webapp files (webconfig developer only)
/// * port : the port where hyperion webapp is accasible
/// * sslPort : the secure (HTTPS) port of the hyperion webapp
/// * crtPath : the path to a certificate file to allow HTTPS connections. Should be in PEM format
/// * keyPath : the path to a private key file to allow HTTPS connections. Should be in PEM format and RSA encrypted
/// * keyPassPhrase : optional: If the key file requires a password add it here
"webConfig" :
{
"document_root" : "/path/to/files",
"port" : 8090,
"sslPort" : 8092,
"crtPath" : "/path/to/mycert.crt",
"keyPath" : "/path/to/mykey.key",
"keyPassPhrase" : ""
},
/// The configuration of the effect engine, contains the following items:
/// * paths : An array with absolute location(s) of directories with effects,
/// $ROOT is a keyword which will be replaced with the current rootPath that can be specified on startup from the commandline (defaults to your home directory)
/// * disable : An array with effect names that shouldn't be loaded
"effects" :
{
"paths" :
[
"$ROOT/custom-effects",
"/usr/share/hyperion/effects"
],
"disable" :
[
"Rainbow swirl",
"X-Mas"
]
},
"instCapture" :
{
"systemEnable" : true,
"systemPriority" : 250,
"v4lEnable" : false,
"v4lPriority" : 240
},
/// The configuration of the network security restrictions, contains the following items:
/// * internetAccessAPI : When true allows connection from internet to the API. When false it blocks all outside connections
/// * restirctedInternetAccessAPI : webui voodoo only - ignore it
/// * ipWhitelist : Whitelist ip addresses from the internet to allow access to the API
/// * apiAuth : When true the API requires authentication through tokens to use the API. Read also "localApiAuth"
/// * localApiAuth : When false connections from the local network don't require an API authentification.
/// * localAdminApiAuth : When false connections from the local network don't require an authentification for administration access.
"network" :
{
"internetAccessAPI" : false,
"restirctedInternetAccessAPI" : false,
"ipWhitelist" : [],
"apiAuth" : true,
"localApiAuth" : false,
"localAdminAuth": true
},
/// Recreate and save led layouts made with web config. These values are just helpers for ui, not for Hyperion.
"ledConfig" :
{
"classic":
{
"top" : 8,
"bottom" : 8,
"left" : 5,
"right" : 5,
"glength" : 0,
"gpos" : 0,
"position" : 0,
"reverse" : false,
"hdepth" : 8,
"vdepth" : 5,
"overlap" : 0,
"edgegap" : 0,
"ptlh" : 0,
"ptlv" : 0,
"ptrh" : 100,
"ptrv" : 0,
"pblh" : 0,
"pblv" : 100,
"pbrh" : 100,
"pbrv" : 100
},
"matrix":
{
"ledshoriz": 10,
"ledsvert" : 10,
"cabling" : "snake",
"start" : "top-left"
}
},
/// The configuration for each individual led. This contains the specification of the area
/// averaged of an input image for each led to determine its color. Each item in the list
/// contains the following fields:
/// * hmin: The fractional part of the image along the horizontal used for the averaging (minimum)
/// * hmax: The fractional part of the image along the horizontal used for the averaging (maximum)
/// * vmin: The fractional part of the image along the vertical used for the averaging (minimum)
/// * vmax: The fractional part of the image along the vertical used for the averaging (maximum)
/// * colorOrder: Usually the global colorOrder is set at the device section, you can overwrite it here per led
"leds":
[
{
"hmax": 0.125,
"hmin": 0,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 0.25,
"hmin": 0.125,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 0.375,
"hmin": 0.25,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 0.5,
"hmin": 0.375,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 0.625,
"hmin": 0.5,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 0.75,
"hmin": 0.625,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 0.875,
"hmin": 0.75,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 1,
"hmin": 0.875,
"vmax": 0.08,
"vmin": 0
},
{
"hmax": 1,
"hmin": 0.95,
"vmax": 0.2,
"vmin": 0
},
{
"hmax": 1,
"hmin": 0.95,
"vmax": 0.4,
"vmin": 0.2
},
{
"hmax": 1,
"hmin": 0.95,
"vmax": 0.6,
"vmin": 0.4
},
{
"hmax": 1,
"hmin": 0.95,
"vmax": 0.8,
"vmin": 0.6
},
{
"hmax": 1,
"hmin": 0.95,
"vmax": 1,
"vmin": 0.8
},
{
"hmax": 1,
"hmin": 0.875,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.875,
"hmin": 0.75,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.75,
"hmin": 0.625,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.625,
"hmin": 0.5,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.5,
"hmin": 0.375,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.375,
"hmin": 0.25,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.25,
"hmin": 0.125,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.125,
"hmin": 0,
"vmax": 1,
"vmin": 0.92
},
{
"hmax": 0.05,
"hmin": 0,
"vmax": 1,
"vmin": 0.8
},
{
"hmax": 0.05,
"hmin": 0,
"vmax": 0.8,
"vmin": 0.6
},
{
"hmax": 0.05,
"hmin": 0,
"vmax": 0.6,
"vmin": 0.4
},
{
"hmax": 0.05,
"hmin": 0,
"vmax": 0.4,
"vmin": 0.2
},
{
"hmax": 0.05,
"hmin": 0,
"vmax": 0.2,
"vmin": 0
}
]
}

View File

@ -2,6 +2,8 @@
"general" : "general" :
{ {
"name" : "My Hyperion Config", "name" : "My Hyperion Config",
"configVersion": "configVersionValue",
"previousVersion": "previousVersionValue",
"watchedVersionBranch" : "Stable", "watchedVersionBranch" : "Stable",
"showOptHelp" : true "showOptHelp" : true
}, },
@ -62,40 +64,49 @@
"grabberV4L2" : "grabberV4L2" :
{ {
"device" : "auto", "enable" : false,
"device" : "none",
"input" : 0, "input" : 0,
"encoding" : "NO_CHANGE",
"width" : 0, "width" : 0,
"height" : 0, "height" : 0,
"fps" : 15, "fps" : 15,
"standard" : "NO_CHANGE", "flip" : "NO_CHANGE",
"fpsSoftwareDecimation" : 0,
"sizeDecimation" : 8, "sizeDecimation" : 8,
"cropLeft" : 0, "cropLeft" : 0,
"cropRight" : 0, "cropRight" : 0,
"cropTop" : 0, "cropTop" : 0,
"cropBottom" : 0, "cropBottom" : 0,
"redSignalThreshold" : 5, "redSignalThreshold" : 0,
"greenSignalThreshold" : 5, "greenSignalThreshold" : 100,
"blueSignalThreshold" : 5, "blueSignalThreshold" : 0,
"signalDetection" : false, "signalDetection" : false,
"noSignalCounterThreshold" : 200,
"cecDetection" : false, "cecDetection" : false,
"sDVOffsetMin" : 0.25, "sDVOffsetMin" : 0.1,
"sDHOffsetMin" : 0.25, "sDVOffsetMax" : 0.9,
"sDVOffsetMax" : 0.75, "sDHOffsetMin" : 0.4,
"sDHOffsetMax" : 0.75 "sDHOffsetMax" : 0.46,
"hardware_brightness" : 0,
"hardware_contrast" : 0,
"hardware_saturation" : 0,
"hardware_hue" : 0
}, },
"framegrabber" : "framegrabber" :
{ {
"type" : "auto", "enable" : false,
"device" : "auto",
"input" : 0,
"width" : 80, "width" : 80,
"height" : 45, "height" : 45,
"frequency_Hz" : 10, "fps" : 10,
"pixelDecimation" : 8, "pixelDecimation" : 8,
"cropLeft" : 0, "cropLeft" : 0,
"cropRight" : 0, "cropRight" : 0,
"cropTop" : 0, "cropTop" : 0,
"cropBottom" : 0, "cropBottom" : 0
"display" : 0
}, },
"blackborderdetector" : "blackborderdetector" :
@ -177,9 +188,11 @@
"instCapture" : "instCapture" :
{ {
"systemEnable" : true, "systemEnable" : false,
"systemGrabberDevice" : "NONE",
"systemPriority" : 250, "systemPriority" : 250,
"v4lEnable" : false, "v4lEnable" : false,
"v4lGrabberDevice" : "NONE",
"v4lPriority" : 240 "v4lPriority" : 240
}, },

View File

@ -11,7 +11,7 @@ Note: call the script with `./docker-compile.sh -h` for more options.
```console ```console
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian
``` ```
**Raspbian Buster** **Raspbian Buster/Raspberry Pi OS**
```console ```console
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian -t buster wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian -t buster
``` ```
@ -58,7 +58,7 @@ cd $HYPERION_HOME
```console ```console
sudo apt-get update sudo apt-get update
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libssl-dev zlib1g-dev sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libturbojpeg0-dev libssl-dev zlib1g-dev
``` ```
**on RPI you need the videocore IV headers** **on RPI you need the videocore IV headers**
@ -83,7 +83,7 @@ See [AUR](https://aur.archlinux.org/packages/?O=0&SeB=nd&K=hyperion&outdated=&SB
The following dependencies are needed to build hyperion.ng on fedora. The following dependencies are needed to build hyperion.ng on fedora.
```console ```console
sudo dnf -y groupinstall "Development Tools" sudo dnf -y groupinstall "Development Tools"
sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel libjpeg-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel turbojpeg-devel libusb-devel avahi-libs avahi-compat-libdns_sd-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel turbojpeg-devel libusb-devel avahi-libs avahi-compat-libdns_sd-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel
``` ```
After installing the dependencies, you can continue with the compile instructions later on this page (the more detailed way..). After installing the dependencies, you can continue with the compile instructions later on this page (the more detailed way..).
@ -98,18 +98,20 @@ brew install qt5 python3 cmake libusb doxygen zlib
## Windows (WIP) ## Windows (WIP)
We assume a 64bit Windows 10. Install the following; We assume a 64bit Windows 10. Install the following;
- [Git](https://git-scm.com/downloads) (Check: Add to PATH) - [Git](https://git-scm.com/downloads) (Check: Add to PATH)
- [CMake (Windows win64-x64 Installer)](https://cmake.org/download/) (Check: Add to PATH) - [CMake (Windows win64-x64 installer)](https://cmake.org/download/) (Check: Add to PATH)
- [Visual Studio 2019 Build Tools](https://go.microsoft.com/fwlink/?linkid=840931) ([direct link](https://aka.ms/vs/16/release/vs_buildtools.exe)) - [Visual Studio 2019 Build Tools](https://go.microsoft.com/fwlink/?linkid=840931) ([direct link](https://aka.ms/vs/16/release/vs_buildtools.exe))
- Select C++ Buildtools - Select C++ Buildtools
- On the right, just select `MSVC v142 VS 2019 C++ x64/x86-Buildtools` and latest `Windows 10 SDK`. Everything else is not needed. - On the right, just select `MSVC v142 VS 2019 C++ x64/x86-Buildtools` and latest `Windows 10 SDK`. Everything else is not needed.
- [Win64 OpenSSL v1.1.1h](https://slproweb.com/products/Win32OpenSSL.html) ([direct link](https://slproweb.com/download/Win64OpenSSL-1_1_1h.exe)) - [Win64 OpenSSL v1.1.1k](https://slproweb.com/products/Win32OpenSSL.html) ([direct link](https://slproweb.com/download/Win64OpenSSL-1_1_1k.exe))
- [Python 3 (Windows x86-64 executable installer)](https://www.python.org/downloads/windows/) (Check: Add to PATH and Debug Symbols) - [Python 3 (Windows x86-64 executable installer)](https://www.python.org/downloads/windows/) (Check: Add to PATH and Debug Symbols)
- Open a console window and execute `pip install aqtinstall`. - Open a console window and execute `pip install aqtinstall`.
- Now we can download Qt to _C:\Qt_ `mkdir c:\Qt && aqt install -O c:\Qt 5.15.0 windows desktop win64_msvc2019_64` - Now we can download Qt to _C:\Qt_ `mkdir c:\Qt && aqt install -O c:\Qt 5.15.0 windows desktop win64_msvc2019_64`
- [libjpeg-turbo SDK for Visual C++](https://sourceforge.net/projects/libjpeg-turbo/files/)
- Download the latest 64bit installer (currently `libjpeg-turbo-2.1.0-vc64.exe`) and install to its default location `C:\libjpeg-turbo64`.
### Optional: ### Optional:
- For DirectX9 grabber: - For DirectX9 grabber:
- DirectX Software Development Kit. The download link is no longer available, so you will have to search for it yourself. - [DirectX Software Development Kit](https://www.microsoft.com/en-us/download/details.aspx?id=6812) ([direct link](https://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe))
- For package creation: - For package creation:
- [NSIS 3.x](https://sourceforge.net/projects/nsis/files/NSIS%203/) ([direct link](https://sourceforge.net/projects/nsis/files/latest/download)) - [NSIS 3.x](https://sourceforge.net/projects/nsis/files/NSIS%203/) ([direct link](https://sourceforge.net/projects/nsis/files/latest/download))

View File

@ -278,6 +278,12 @@ private:
/// ///
void handleLedDeviceCommand(const QJsonObject &message, const QString &command, int tan); void handleLedDeviceCommand(const QJsonObject &message, const QString &command, int tan);
/// Handle an incoming JSON message regarding Input Sources (Grabbers)
///
/// @param message the incoming message
///
void handleInputSourceCommand(const QJsonObject& message, const QString& command, int tan);
/// ///
/// Handle an incoming JSON message of unknown type /// Handle an incoming JSON message of unknown type
/// ///

View File

@ -67,6 +67,7 @@ public:
QString getName() const { return _name; } QString getName() const { return _name; }
int getTimeout() const {return _timeout; } int getTimeout() const {return _timeout; }
bool isEndless() const { return _isEndless; }
QJsonObject getArgs() const { return _args; } QJsonObject getArgs() const { return _args; }
@ -83,6 +84,7 @@ private:
const int _priority; const int _priority;
const int _timeout; const int _timeout;
bool _isEndless;
const QString _script; const QString _script;
const QString _name; const QString _name;

View File

@ -14,12 +14,16 @@ public:
/// ///
/// Construct a AmlogicGrabber that will capture snapshots with specified dimensions. /// Construct a AmlogicGrabber that will capture snapshots with specified dimensions.
/// ///
/// @param[in] width The width of the captured screenshot
/// @param[in] height The heigth of the captured screenshot
/// ///
AmlogicGrabber(unsigned width, unsigned height); AmlogicGrabber();
~AmlogicGrabber() override; ~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();
/// ///
/// Captures a single snapshot of the display and writes the data to the given image. The /// Captures a single snapshot of the display and writes the data to the given image. The
/// provided image should have the same dimensions as the configured values (_width and /// provided image should have the same dimensions as the configured values (_width and
@ -31,14 +35,51 @@ public:
/// ///
int grabFrame(Image<ColorRgb> & image); int grabFrame(Image<ColorRgb> & image);
///
/// @brief Discover AmLogic screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
///
/// Set the video mode (2D/3D)
/// @param[in] mode The new video mode
///
void setVideoMode(VideoMode mode) override;
///
/// @brief Apply new crop values, on errors reject the values
///
void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) override;
///
/// @brief Apply new width/height values, on errors (collide with cropping) reject the values
/// @return True on success else false
///
bool setWidthHeight(int width, int height) override;
///
/// @brief Apply new framerate
/// @param fps framesPerSecond
///
bool setFramerate(int fps) override;
///
/// @brief Apply new pixelDecimation
///
bool setPixelDecimation(int pixelDecimation) override;
private: private:
/** /**
* Returns true if video is playing over the amlogic chip * Returns true if video is playing over the amlogic chip
* @return True if video is playing else false * @return True if video is playing else false
*/ */
bool isVideoPlaying(); bool isVideoPlaying();
void closeDev(int &fd); void closeDevice(int &fd);
bool openDev(int &fd, const char* dev); bool openDevice(int &fd, const char* dev);
int grabFrame_amvideocap(Image<ColorRgb> & image); int grabFrame_amvideocap(Image<ColorRgb> & image);

View File

@ -4,8 +4,8 @@
#include <grabber/AmlogicGrabber.h> #include <grabber/AmlogicGrabber.h>
/// ///
/// The DispmanxWrapper uses an instance of the DispmanxFrameGrabber to obtain ImageRgb's from the /// The Amlogic uses an instance of the AmlogicGrabber to obtain ImageRgb's from the
/// displayed content. This ImageRgb is processed to a ColorRgb for each led and commmited to the /// displayed content. This ImageRgb is processed to a ColorRgb for each led and committed to the
/// attached Hyperion. /// attached Hyperion.
/// ///
class AmlogicWrapper : public GrabberWrapper class AmlogicWrapper : public GrabberWrapper
@ -13,12 +13,14 @@ class AmlogicWrapper : public GrabberWrapper
Q_OBJECT Q_OBJECT
public: public:
/// ///
/// Constructs the dispmanx frame grabber with a specified grab size and update rate. /// Constructs the Amlogic frame grabber
/// ///
/// @param[in] grabWidth The width of the grabbed image [pixels] /// @param[in] grabWidth The width of the grabbed image [pixels]
/// @param[in] grabHeight The height of the grabbed images [pixels] /// @param[in] grabHeight The height of the grabbed images [pixels]
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// ///
AmlogicWrapper(unsigned grabWidth, unsigned grabHeight); AmlogicWrapper(int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION,
int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ);
public slots: public slots:
/// ///

View File

@ -8,8 +8,14 @@
// Hyperion-utils includes // Hyperion-utils includes
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <hyperion/GrabberWrapper.h>
#include <hyperion/Grabber.h> #include <hyperion/Grabber.h>
// qt includes
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
/// ///
/// @brief The DirectX9 capture implementation /// @brief The DirectX9 capture implementation
/// ///
@ -17,33 +23,32 @@ class DirectXGrabber : public Grabber
{ {
public: public:
DirectXGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display); DirectXGrabber(int display=0, int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
virtual ~DirectXGrabber(); virtual ~DirectXGrabber();
/// ///
/// Captures a single snapshot of the display and writes the data to the given image. The /// Captures a single snapshot of the display and writes the data to the given image. The
/// provided image should have the same dimensions as the configured values (_width and /// provided image should have the same dimensions as the configured values (_width and _height)
/// _height)
/// ///
/// @param[out] image The snapped screenshot /// @param[out] image The snapped screenshot
/// ///
virtual int grabFrame(Image<ColorRgb> & image); int grabFrame(Image<ColorRgb> & image);
/// ///
/// @brief Set a new video mode /// @brief Set a new video mode
/// ///
virtual void setVideoMode(VideoMode mode); void setVideoMode(VideoMode mode) override;
/// ///
/// @brief Apply new width/height values, overwrite Grabber.h implementation /// @brief Apply new width/height values, overwrite Grabber.h implementation
/// ///
virtual bool setWidthHeight(int width, int height) { return true; }; bool setWidthHeight(int /* width */, int /*height*/) override { return true; }
/// ///
/// @brief Apply new pixelDecimation /// @brief Apply new pixelDecimation
/// ///
virtual void setPixelDecimation(int pixelDecimation); bool setPixelDecimation(int pixelDecimation) override;
/// ///
/// Set the crop values /// Set the crop values
@ -52,12 +57,20 @@ public:
/// @param cropTop Top pixel crop /// @param cropTop Top pixel crop
/// @param cropBottom Bottom pixel crop /// @param cropBottom Bottom pixel crop
/// ///
virtual void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom); void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom);
/// ///
/// @brief Apply display index /// @brief Apply display index
/// ///
void setDisplayIndex(int index) override; bool setDisplayIndex(int index) override;
/// @brief Discover QT screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
private: private:
/// ///
@ -72,7 +85,6 @@ private:
void freeResources(); void freeResources();
private: private:
int _pixelDecimation;
unsigned _display; unsigned _display;
unsigned _displayWidth; unsigned _displayWidth;
unsigned _displayHeight; unsigned _displayHeight;

View File

@ -9,15 +9,21 @@ public:
/// ///
/// Constructs the DirectX grabber with a specified grab size and update rate. /// Constructs the DirectX grabber with a specified grab size and update rate.
/// ///
/// @param[in] updateRate_Hz The image grab rate [Hz]
/// @param[in] display Display to be grabbed
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// @param[in] cropLeft Remove from left [pixels] /// @param[in] cropLeft Remove from left [pixels]
/// @param[in] cropRight Remove from right [pixels] /// @param[in] cropRight Remove from right [pixels]
/// @param[in] cropTop Remove from top [pixels] /// @param[in] cropTop Remove from top [pixels]
/// @param[in] cropBottom Remove from bottom [pixels] /// @param[in] cropBottom Remove from bottom [pixels]
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// @param[in] display The display used[index]
/// @param[in] updateRate_Hz The image grab rate [Hz]
/// ///
DirectXWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, const unsigned updateRate_Hz); DirectXWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ,
int display=0,
int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION,
int cropLeft=0, int cropRight=0,
int cropTop=0, int cropBottom=0
);
/// ///
/// Destructor of this DirectX grabber. Releases any claimed resources. /// Destructor of this DirectX grabber. Releases any claimed resources.

View File

@ -14,8 +14,7 @@
#include <hyperion/Grabber.h> #include <hyperion/Grabber.h>
/// ///
/// The DispmanxFrameGrabber is used for creating snapshots of the display (screenshots) with a /// The DispmanxFrameGrabber is used for creating snapshots of the display (screenshots) with a downsized and scaled resolution.
/// downsized and scaled resolution.
/// ///
class DispmanxFrameGrabber : public Grabber class DispmanxFrameGrabber : public Grabber
{ {
@ -23,12 +22,16 @@ public:
/// ///
/// Construct a DispmanxFrameGrabber that will capture snapshots with specified dimensions. /// Construct a DispmanxFrameGrabber that will capture snapshots with specified dimensions.
/// ///
/// @param[in] width The width of the captured screenshot DispmanxFrameGrabber();
/// @param[in] height The heigth of the captured screenshot
///
DispmanxFrameGrabber(unsigned width, unsigned height);
~DispmanxFrameGrabber() override; ~DispmanxFrameGrabber() override;
bool open();
///
/// @brief Setup a new capture screen, will free the previous one
/// @return True on success, false if no screen is found
///
bool setupScreen();
/// ///
/// Captures a single snapshot of the display and writes the data to the given image. The /// Captures a single snapshot of the display and writes the data to the given image. The
@ -44,13 +47,24 @@ public:
///@brief Set new width and height for dispmanx, overwrite Grabber.h impl ///@brief Set new width and height for dispmanx, overwrite Grabber.h impl
bool setWidthHeight(int width, int height) override; bool setWidthHeight(int width, int height) override;
QSize getScreenSize(int display=0) const;
///
/// @brief Discover DispmanX screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
private: private:
/// ///
/// Updates the frame-grab flags as used by the VC library for frame grabbing /// Updates the frame-grab flags as used by the VC library for frame grabbing
/// ///
/// @param vc_flags The snapshot grabbing mask /// @param vc_flags The snapshot grabbing mask
/// ///
void setFlags(int vc_flags); void setFlags(DISPMANX_TRANSFORM_T vc_flags);
/// ///
/// @brief free _vc_resource and captureBuffer /// @brief free _vc_resource and captureBuffer
@ -63,11 +77,11 @@ private:
/// Handle to the resource for storing the captured snapshot /// Handle to the resource for storing the captured snapshot
DISPMANX_RESOURCE_HANDLE_T _vc_resource; DISPMANX_RESOURCE_HANDLE_T _vc_resource;
/// Rectangle of the captured resource that is transfered to user space /// Rectangle of the captured resource that is transferred to user space
VC_RECT_T _rectangle; VC_RECT_T _rectangle;
/// Flags (transforms) for creating snapshots /// Flags (transforms) for creating snapshots
int _vc_flags; DISPMANX_TRANSFORM_T _vc_flags;
// temp buffer when capturing with unsupported pitch size or // temp buffer when capturing with unsupported pitch size or
// when we need to crop the image // when we need to crop the image
@ -78,5 +92,4 @@ private:
// rgba output buffer // rgba output buffer
Image<ColorRgba> _image_rgba; Image<ColorRgba> _image_rgba;
}; };

View File

@ -16,6 +16,7 @@ typedef int DISPMANX_TRANSFORM_T;
struct DISPMANX_MODEINFO_T { struct DISPMANX_MODEINFO_T {
int width; int width;
int height; int height;
uint32_t display_num;
}; };
struct VC_RECT_T { struct VC_RECT_T {
@ -34,6 +35,6 @@ DISPMANX_RESOURCE_HANDLE_T vc_dispmanx_resource_create(int,int width,int height,
void vc_dispmanx_resource_delete(DISPMANX_RESOURCE_HANDLE_T resource); void vc_dispmanx_resource_delete(DISPMANX_RESOURCE_HANDLE_T resource);
int vc_dispmanx_resource_read_data(DISPMANX_RESOURCE_HANDLE_T vc_resource, VC_RECT_T *rectangle, void* capturePtr, unsigned capturePitch); int vc_dispmanx_resource_read_data(DISPMANX_RESOURCE_HANDLE_T vc_resource, VC_RECT_T *rectangle, void* capturePtr, unsigned capturePitch);
void vc_dispmanx_rect_set(VC_RECT_T *rectangle, int left, int top, int width, int height); void vc_dispmanx_rect_set(VC_RECT_T *rectangle, int left, int top, int width, int height);
int vc_dispmanx_snapshot(int, DISPMANX_RESOURCE_HANDLE_T resource, int vc_flags); int vc_dispmanx_snapshot(int, DISPMANX_RESOURCE_HANDLE_T resource, DISPMANX_TRANSFORM_T vc_flags);
#endif #endif

View File

@ -16,11 +16,14 @@ public:
/// ///
/// Constructs the dispmanx frame grabber with a specified grab size and update rate. /// Constructs the dispmanx frame grabber with a specified grab size and update rate.
/// ///
/// @param[in] grabWidth The width of the grabbed image [pixels] /// @param[in] pixelDecimation Decimation factor for image [pixels]
/// @param[in] grabHeight The height of the grabbed images [pixels]
/// @param[in] updateRate_Hz The image grab rate [Hz] /// @param[in] updateRate_Hz The image grab rate [Hz]
/// ///
DispmanxWrapper(unsigned grabWidth, unsigned grabHeight, unsigned updateRate_Hz); DispmanxWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ,
int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION
);
bool screenInit();
public slots: public slots:
/// ///

View File

@ -0,0 +1,176 @@
#pragma once
// Qt includes
#include <QThread>
// util includes
#include <utils/PixelFormat.h>
#include <utils/ImageResampler.h>
// Determine the cmake options
#include <HyperionConfig.h>
// Turbo JPEG decoder
#ifdef HAVE_TURBO_JPEG
#include <turbojpeg.h>
#endif
/// Encoder thread for USB devices
class EncoderThread : public QObject
{
Q_OBJECT
public:
explicit EncoderThread();
~EncoderThread();
void setup(
PixelFormat pixelFormat, uint8_t* sharedData,
int size, int width, int height, int lineLength,
unsigned cropLeft, unsigned cropTop, unsigned cropBottom, unsigned cropRight,
VideoMode videoMode, FlipMode flipMode, int pixelDecimation);
void process();
bool isBusy() { return _busy; }
QAtomicInt _busy = false;
signals:
void newFrame(const Image<ColorRgb>& data);
private:
PixelFormat _pixelFormat;
uint8_t* _localData,
*_flipBuffer;
int _scalingFactorsCount,
_width,
_height,
_lineLength,
_currentFrame,
_pixelDecimation;
unsigned long _size;
unsigned _cropLeft,
_cropTop,
_cropBottom,
_cropRight;
FlipMode _flipMode;
ImageResampler _imageResampler;
#ifdef HAVE_TURBO_JPEG
tjhandle _transform, _decompress;
tjscalingfactor* _scalingFactors;
tjtransform* _xform;
void processImageMjpeg();
#endif
};
template <typename TThread> class Thread : public QThread
{
public:
TThread *_thread;
explicit Thread(TThread *thread, QObject *parent = nullptr)
: QThread(parent)
, _thread(thread)
{
_thread->moveToThread(this);
start();
}
~Thread()
{
quit();
wait();
}
EncoderThread* thread() const { return qobject_cast<EncoderThread*>(_thread); }
void setup(
PixelFormat pixelFormat, uint8_t* sharedData,
int size, int width, int height, int lineLength,
unsigned cropLeft, unsigned cropTop, unsigned cropBottom, unsigned cropRight,
VideoMode videoMode, FlipMode flipMode, int pixelDecimation)
{
auto encThread = qobject_cast<EncoderThread*>(_thread);
if (encThread != nullptr)
encThread->setup(pixelFormat, sharedData,
size, width, height, lineLength,
cropLeft, cropTop, cropBottom, cropRight,
videoMode, flipMode, pixelDecimation);
}
bool isBusy()
{
auto encThread = qobject_cast<EncoderThread*>(_thread);
if (encThread != nullptr)
return encThread->isBusy();
return true;
}
void process()
{
auto encThread = qobject_cast<EncoderThread*>(_thread);
if (encThread != nullptr)
encThread->process();
}
protected:
void run() override
{
QThread::run();
delete _thread;
}
};
class EncoderThreadManager : public QObject
{
Q_OBJECT
public:
explicit EncoderThreadManager(QObject *parent = nullptr)
: QObject(parent)
, _threadCount(qMax(QThread::idealThreadCount(), 1))
, _threads(nullptr)
{
_threads = new Thread<EncoderThread>*[_threadCount];
for (int i = 0; i < _threadCount; i++)
{
_threads[i] = new Thread<EncoderThread>(new EncoderThread, this);
_threads[i]->setObjectName("Encoder " + i);
}
}
~EncoderThreadManager()
{
if (_threads != nullptr)
{
for(int i = 0; i < _threadCount; i++)
{
_threads[i]->deleteLater();
_threads[i] = nullptr;
}
delete[] _threads;
_threads = nullptr;
}
}
void start()
{
if (_threads != nullptr)
for (int i = 0; i < _threadCount; i++)
connect(_threads[i]->thread(), &EncoderThread::newFrame, this, &EncoderThreadManager::newFrame);
}
void stop()
{
if (_threads != nullptr)
for(int i = 0; i < _threadCount; i++)
disconnect(_threads[i]->thread(), nullptr, nullptr, nullptr);
}
int _threadCount;
Thread<EncoderThread>** _threads;
signals:
void newFrame(const Image<ColorRgb>& data);
};

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <linux/fb.h>
// Utils includes // Utils includes
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <hyperion/Grabber.h> #include <hyperion/Grabber.h>
@ -14,10 +16,10 @@ public:
/// Construct a FramebufferFrameGrabber that will capture snapshots with specified dimensions. /// Construct a FramebufferFrameGrabber that will capture snapshots with specified dimensions.
/// ///
/// @param[in] device The framebuffer device name/path /// @param[in] device The framebuffer device name/path
/// @param[in] width The width of the captured screenshot
/// @param[in] height The heigth of the captured screenshot
/// ///
FramebufferFrameGrabber(const QString & device, unsigned width, unsigned height); FramebufferFrameGrabber(const QString & device="/dev/fb0");
~FramebufferFrameGrabber() override;
/// ///
/// Captures a single snapshot of the display and writes the data to the given image. The /// Captures a single snapshot of the display and writes the data to the given image. The
@ -30,11 +32,42 @@ public:
int grabFrame(Image<ColorRgb> & image); int grabFrame(Image<ColorRgb> & image);
/// ///
/// @brief Overwrite Grabber.h implememtation /// @brief Setup a new capture screen, will free the previous one
/// @return True on success, false if no screen is found
/// ///
void setDevicePath(const QString& path) override; bool setupScreen();
QSize getScreenSize() const;
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 _fbDevice;}
///
/// @brief Discover Framebuffer screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
private: private:
bool openDevice();
bool closeDevice();
bool getScreenInfo();
/// Framebuffer device e.g. /dev/fb0 /// Framebuffer device e.g. /dev/fb0
QString _fbDevice; QString _fbDevice;
int _fbfd;
struct fb_var_screeninfo _varInfo;
struct fb_fix_screeninfo _fixInfo;
PixelFormat _pixelFormat;
}; };

View File

@ -5,7 +5,7 @@
/// ///
/// The FramebufferWrapper uses an instance of the FramebufferFrameGrabber to obtain ImageRgb's from the /// The FramebufferWrapper uses an instance of the FramebufferFrameGrabber to obtain ImageRgb's from the
/// displayed content. This ImageRgb is processed to a ColorRgb for each led and commmited to the /// displayed content. This ImageRgb is processed to a ColorRgb for each led and committed to the
/// attached Hyperion. /// attached Hyperion.
/// ///
class FramebufferWrapper: public GrabberWrapper class FramebufferWrapper: public GrabberWrapper
@ -15,12 +15,14 @@ public:
/// ///
/// Constructs the framebuffer frame grabber with a specified grab size and update rate. /// Constructs the framebuffer frame grabber with a specified grab size and update rate.
/// ///
/// @param[in] device Framebuffer device name/path
/// @param[in] grabWidth The width of the grabbed image [pixels]
/// @param[in] grabHeight The height of the grabbed images [pixels]
/// @param[in] updateRate_Hz The image grab rate [Hz] /// @param[in] updateRate_Hz The image grab rate [Hz]
/// @param[in] device Framebuffer device name/path
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// ///
FramebufferWrapper(const QString & device, unsigned grabWidth, unsigned grabHeight, unsigned updateRate_Hz); FramebufferWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ,
const QString & device = "/dev/fb0",
int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION
);
public slots: public slots:
/// ///

129
include/grabber/MFGrabber.h Normal file
View File

@ -0,0 +1,129 @@
#pragma once
// Windows include
#include <Windows.h>
// COM includes
#include <Guiddef.h>
// Qt includes
#include <QObject>
#include <QRectF>
#include <QMap>
#include <QMultiMap>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
// utils includes
#include <utils/PixelFormat.h>
#include <utils/Components.h>
#include <hyperion/Grabber.h>
// decoder thread includes
#include <grabber/EncoderThread.h>
/// Forward class declaration
class SourceReaderCB;
/// Forward struct declaration
struct IMFSourceReader;
///
/// Media Foundation capture class
///
class MFGrabber : public Grabber
{
Q_OBJECT
friend class SourceReaderCB;
public:
struct DeviceProperties
{
QString symlink = QString();
int width = 0;
int height = 0;
int fps = 0;
int numerator = 0;
int denominator = 0;
PixelFormat pf = PixelFormat::NO_CHANGE;
GUID guid = GUID_NULL;
};
struct DeviceControls
{
QString property = QString();
int minValue = 0;
int maxValue = 0;
int step = 0;
int default = 0;
int currentValue = 0;
};
MFGrabber();
~MFGrabber() override;
void receive_image(const void *frameImageBuffer, int size);
void setDevice(const QString& device);
bool setInput(int input) override;
bool setWidthHeight(int width, int height) override;
void setEncoding(QString enc);
void setBrightnessContrastSaturationHue(int brightness, int contrast, int saturation, int hue);
void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold);
void setSignalDetectionOffset( double verticalMin, double horizontalMin, double verticalMax, double horizontalMax);
void setSignalDetectionEnable(bool enable);
bool reload(bool force = false);
///
/// @brief Discover available Media Foundation USB devices (for configuration).
/// @param[in] params Parameters used to overwrite discovery default behaviour
/// @return A JSON structure holding a list of USB devices found
///
QJsonArray discover(const QJsonObject& params);
public slots:
bool prepare();
bool start();
void stop();
void newThreadFrame(Image<ColorRgb> image);
signals:
void newFrame(const Image<ColorRgb> & image);
void readError(const char* err);
private:
bool init();
void uninit();
HRESULT init_device(QString device, DeviceProperties props);
void enumVideoCaptureDevices();
void start_capturing();
void process_image(const void *frameImageBuffer, int size);
QString _currentDeviceName,
_newDeviceName;
QMap<QString, QList<DeviceProperties>> _deviceProperties;
QMap<QString, QList<DeviceControls>> _deviceControls;
HRESULT _hr;
IMFSourceReader* _sourceReader;
SourceReaderCB* _sourceReaderCB;
EncoderThreadManager* _threadManager;
PixelFormat _pixelFormat,
_pixelFormatConfig;
int _lineLength,
_frameByteSize,
_noSignalCounterThreshold,
_noSignalCounter,
_brightness,
_contrast,
_saturation,
_hue;
QAtomicInt _currentFrame;
ColorRgb _noSignalThresholdColor;
bool _signalDetectionEnabled,
_noSignalDetected,
_initialized,
_reload;
double _x_frac_min,
_y_frac_min,
_x_frac_max,
_y_frac_max;
};

View File

@ -21,12 +21,17 @@ public:
/// Construct a OsxFrameGrabber that will capture snapshots with specified dimensions. /// Construct a OsxFrameGrabber that will capture snapshots with specified dimensions.
/// ///
/// @param[in] display The index of the display to capture /// @param[in] display The index of the display to capture
/// @param[in] width The width of the captured screenshot
/// @param[in] height The heigth of the captured screenshot
/// ///
OsxFrameGrabber(unsigned display, unsigned width, unsigned height); OsxFrameGrabber(int display=kCGDirectMainDisplay);
~OsxFrameGrabber() override; ~OsxFrameGrabber() override;
///
/// @brief Setup a new capture screen, will free the previous one
/// @return True on success, false if no screen is found
///
bool setupDisplay();
/// ///
/// Captures a single snapshot of the display and writes the data to the given image. The /// Captures a single snapshot of the display and writes the data to the given image. The
/// provided image should have the same dimensions as the configured values (_width and /// provided image should have the same dimensions as the configured values (_width and
@ -40,12 +45,21 @@ public:
/// ///
/// @brief Overwrite Grabber.h implementation /// @brief Overwrite Grabber.h implementation
/// ///
void setDisplayIndex(int index) override; bool setDisplayIndex(int index) override;
///
/// @brief Discover OSX screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
private: private:
/// display /// display
unsigned _screenIndex; int _screenIndex;
/// Reference to the captured diaplay /// Reference to the captured display
CGDirectDisplayID _display; CGDirectDisplayID _display;
}; };

View File

@ -5,21 +5,79 @@
* this is a mock up for compiling and testing osx wrapper on no osx platform. * this is a mock up for compiling and testing osx wrapper on no osx platform.
* this will show a test image and rotate the colors. * this will show a test image and rotate the colors.
* *
* see https://github.com/phracker/MacOSX-SDKs/blob/master/MacOSX10.8.sdk/System/Library/Frameworks/CoreGraphics.framework/Versions/A/Headers
*
*/ */
#include <utils/Image.h> #include <utils/Image.h>
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
typedef int CGDirectDisplayID; enum _CGError {
kCGErrorSuccess = 0,
kCGErrorFailure = 1000,
kCGErrorIllegalArgument = 1001,
kCGErrorInvalidConnection = 1002,
kCGErrorInvalidContext = 1003,
kCGErrorCannotComplete = 1004,
kCGErrorNotImplemented = 1006,
kCGErrorRangeCheck = 1007,
kCGErrorTypeCheck = 1008,
kCGErrorInvalidOperation = 1010,
kCGErrorNoneAvailable = 1011,
/* Obsolete errors. */
kCGErrorNameTooLong = 1005,
kCGErrorNoCurrentPoint = 1009,
kCGErrorApplicationRequiresNewerSystem = 1015,
kCGErrorApplicationNotPermittedToExecute = 1016,
kCGErrorApplicationIncorrectExecutableFormatFound = 1023,
kCGErrorApplicationIsLaunching = 1024,
kCGErrorApplicationAlreadyRunning = 1025,
kCGErrorApplicationCanOnlyBeRunInOneSessionAtATime = 1026,
kCGErrorClassicApplicationsMustBeLaunchedByClassic = 1027,
kCGErrorForkFailed = 1028,
kCGErrorRetryRegistration = 1029,
kCGErrorFirst = 1000,
kCGErrorLast = 1029
};
typedef int32_t CGError;
typedef double CGFloat;
struct CGSize {
CGFloat width;
CGFloat height;
};
typedef struct CGSize CGSize;
struct CGPoint {
float x;
float y;
};
typedef struct CGPoint CGPoint;
struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CGRect CGRect;
typedef CGError CGDisplayErr;
typedef uint32_t CGDirectDisplayID;
typedef uint32_t CGDisplayCount;;
typedef struct CGDisplayMode *CGDisplayModeRef;
typedef Image<ColorRgb> CGImage; typedef Image<ColorRgb> CGImage;
typedef CGImage* CGImageRef; typedef CGImage* CGImageRef;
typedef unsigned char CFData; typedef unsigned char CFData;
typedef CFData* CFDataRef; typedef CFData* CFDataRef;
typedef unsigned CGDisplayCount;
const int kCGDirectMainDisplay = 0; const int kCGDirectMainDisplay = 0;
void CGGetActiveDisplayList(int max, CGDirectDisplayID *displays, CGDisplayCount *displayCount); CGError CGGetActiveDisplayList(uint32_t maxDisplays, CGDirectDisplayID *activeDisplays, uint32_t *displayCount);
CGDisplayModeRef CGDisplayCopyDisplayMode(CGDirectDisplayID display);
CGRect CGDisplayBounds(CGDirectDisplayID display);
void CGDisplayModeRelease(CGDisplayModeRef mode);
CGImageRef CGDisplayCreateImage(CGDirectDisplayID display); CGImageRef CGDisplayCreateImage(CGDirectDisplayID display);
void CGImageRelease(CGImageRef image); void CGImageRelease(CGImageRef image);
CGImageRef CGImageGetDataProvider(CGImageRef image); CGImageRef CGImageGetDataProvider(CGImageRef image);
@ -31,4 +89,5 @@ unsigned CGImageGetBitsPerPixel(CGImageRef image);
unsigned CGImageGetBytesPerRow(CGImageRef image); unsigned CGImageGetBytesPerRow(CGImageRef image);
void CFRelease(CFDataRef imgData); void CFRelease(CFDataRef imgData);
#endif #endif

View File

@ -4,9 +4,8 @@
#include <grabber/OsxFrameGrabber.h> #include <grabber/OsxFrameGrabber.h>
/// ///
/// The OsxWrapper uses an instance of the OsxFrameGrabber to obtain ImageRgb's from the /// The OsxWrapper uses an instance of the OsxFrameGrabber to obtain ImageRgb's from the displayed content.
/// displayed content. This ImageRgb is processed to a ColorRgb for each led and commmited to the /// This ImageRgb is processed to a ColorRgb for each led and committed to the attached Hyperion.
/// attached Hyperion.
/// ///
class OsxWrapper: public GrabberWrapper class OsxWrapper: public GrabberWrapper
{ {
@ -15,12 +14,14 @@ public:
/// ///
/// Constructs the osx frame grabber with a specified grab size and update rate. /// Constructs the osx frame grabber with a specified grab size and update rate.
/// ///
/// @param[in] display Index of the display to grab
/// @param[in] grabWidth The width of the grabbed image [pixels]
/// @param[in] grabHeight The height of the grabbed images [pixels]
/// @param[in] updateRate_Hz The image grab rate [Hz] /// @param[in] updateRate_Hz The image grab rate [Hz]
/// @param[in] display Index of the display to grab
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// ///
OsxWrapper(unsigned display, unsigned grabWidth, unsigned grabHeight, unsigned updateRate_Hz); OsxWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ,
int display = kCGDirectMainDisplay,
int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION
);
public slots: public slots:
/// ///

View File

@ -15,14 +15,13 @@ class QtGrabber : public Grabber
{ {
public: public:
QtGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display); QtGrabber(int display=0, int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
~QtGrabber() override; ~QtGrabber() override;
/// ///
/// Captures a single snapshot of the display and writes the data to the given image. The /// Captures a single snapshot of the display and writes the data to the given image. The
/// provided image should have the same dimensions as the configured values (_width and /// provided image should have the same dimensions as the configured values (_width and _height)
/// _height)
/// ///
/// @param[out] image The snapped screenshot (should be initialized with correct width and /// @param[out] image The snapped screenshot (should be initialized with correct width and
/// height) /// height)
@ -37,12 +36,12 @@ public:
/// ///
/// @brief Apply new width/height values, overwrite Grabber.h implementation as qt doesn't use width/height, just pixelDecimation to calc dimensions /// @brief Apply new width/height values, overwrite Grabber.h implementation as qt doesn't use width/height, just pixelDecimation to calc dimensions
/// ///
bool setWidthHeight(int width, int height) override { return true; } bool setWidthHeight(int /*width*/, int /*height*/) override { return true; }
/// ///
/// @brief Apply new pixelDecimation /// @brief Apply new pixelDecimation
/// ///
void setPixelDecimation(int pixelDecimation) override; bool setPixelDecimation(int pixelDecimation) override;
/// ///
/// Set the crop values /// Set the crop values
@ -51,14 +50,37 @@ public:
/// @param cropTop Top pixel crop /// @param cropTop Top pixel crop
/// @param cropBottom Bottom pixel crop /// @param cropBottom Bottom pixel crop
/// ///
void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) override; void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) override;
/// ///
/// @brief Apply display index /// @brief Apply display index
/// ///
void setDisplayIndex(int index) override; bool setDisplayIndex(int index) override;
///
/// @brief Discover QT screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
///
/// @brief Setup a new capture display, will free the previous one
/// @return True on success, false if no display is found
///
bool setupDisplay();
///
/// @brief Opens the input device.
///
/// @return Zero, on success (i.e. device is ready), else negative
///
bool open();
private slots: private slots:
/// ///
/// @brief is called whenever the current _screen changes it's geometry /// @brief is called whenever the current _screen changes it's geometry
/// @param geo The new geometry /// @param geo The new geometry
@ -66,11 +88,6 @@ private slots:
void geometryChanged(const QRect &geo); void geometryChanged(const QRect &geo);
private: private:
///
/// @brief Setup a new capture display, will free the previous one
/// @return True on success, false if no display is found
///
bool setupDisplay();
/// ///
/// @brief Is called whenever we need new screen dimension calculations based on window geometry /// @brief Is called whenever we need new screen dimension calculations based on window geometry
@ -84,13 +101,19 @@ private:
private: private:
unsigned _display; int _display;
int _pixelDecimation; int _numberOfSDisplays;
unsigned _screenWidth;
unsigned _screenHeight; int _calculatedWidth;
unsigned _src_x; int _calculatedHeight;
unsigned _src_y; int _src_x;
unsigned _src_x_max; int _src_y;
unsigned _src_y_max; int _src_x_max;
int _src_y_max;
bool _isWayland;
QScreen* _screen; QScreen* _screen;
bool _isVirtual;
Logger * _logger;
}; };

View File

@ -10,16 +10,28 @@ class QtWrapper: public GrabberWrapper
{ {
public: public:
/// ///
/// Constructs the framebuffer frame grabber with a specified grab size and update rate. /// Constructs the QT frame grabber with a specified grab size and update rate.
/// ///
/// @param[in] updateRate_Hz The image grab rate [Hz]
/// @param[in] display Display to be grabbed
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// @param[in] cropLeft Remove from left [pixels] /// @param[in] cropLeft Remove from left [pixels]
/// @param[in] cropRight Remove from right [pixels] /// @param[in] cropRight Remove from right [pixels]
/// @param[in] cropTop Remove from top [pixels] /// @param[in] cropTop Remove from top [pixels]
/// @param[in] cropBottom Remove from bottom [pixels] /// @param[in] cropBottom Remove from bottom [pixels]
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// @param[in] updateRate_Hz The image grab rate [Hz]
/// ///
QtWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, unsigned updateRate_Hz); QtWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ,
int display=0,
int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION,
int cropLeft=0, int cropRight=0,
int cropTop=0, int cropBottom=0
);
///
/// Starts the grabber which produces led values with the specified update rate
///
bool open() override;
public slots: public slots:
/// ///

View File

@ -14,30 +14,24 @@
// util includes // util includes
#include <utils/PixelFormat.h> #include <utils/PixelFormat.h>
#include <hyperion/Grabber.h> #include <hyperion/Grabber.h>
#include <grabber/VideoStandard.h> #include <hyperion/GrabberWrapper.h>
#include <utils/VideoStandard.h>
#include <utils/Components.h> #include <utils/Components.h>
#include <cec/CECEvent.h>
// general JPEG decoder includes // decoder thread includes
#ifdef HAVE_JPEG_DECODER #include <grabber/EncoderThread.h>
#include <QImage>
#include <QColor> // Determine the cmake options
#endif #include <HyperionConfig.h>
// System JPEG decoder #if defined(ENABLE_CEC)
#ifdef HAVE_JPEG #include <cec/CECEvent.h>
#include <jpeglib.h>
#include <csetjmp>
#endif
// TurboJPEG decoder
#ifdef HAVE_TURBO_JPEG
#include <turbojpeg.h>
#endif #endif
///
/// Capture class for V4L2 devices /// Capture class for V4L2 devices
/// ///
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
class V4L2Grabber : public Grabber class V4L2Grabber : public Grabber
{ {
Q_OBJECT Q_OBJECT
@ -46,117 +40,66 @@ public:
struct DeviceProperties struct DeviceProperties
{ {
QString name = QString(); QString name = QString();
QMultiMap<QString, int> inputs = QMultiMap<QString, int>(); struct InputProperties
QStringList resolutions = QStringList(); {
QStringList framerates = QStringList(); QString inputName = QString();
QList<VideoStandard> standards = QList<VideoStandard>();
struct EncodingProperties
{
int width = 0;
int height = 0;
QList<int> framerates = QList<int>();
};
QMultiMap<PixelFormat, EncodingProperties> encodingFormats = QMultiMap<PixelFormat, EncodingProperties>();
};
QMap<int, InputProperties> inputs = QMap<int, InputProperties>();
}; };
V4L2Grabber(const QString & device, struct DeviceControls
const unsigned width, {
const unsigned height, QString property = QString();
const unsigned fps, int minValue = 0;
const unsigned input, int maxValue = 0;
VideoStandard videoStandard, int step = 0;
PixelFormat pixelFormat, int defaultValue = 0;
int pixelDecimation int currentValue = 0;
); };
V4L2Grabber();
~V4L2Grabber() override; ~V4L2Grabber() override;
QRectF getSignalDetectionOffset() const
{
return QRectF(_x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max);
}
bool getSignalDetectionEnabled() const { return _signalDetectionEnabled; }
bool getCecDetectionEnabled() const { return _cecDetectionEnabled; }
int grabFrame(Image<ColorRgb> &); int grabFrame(Image<ColorRgb> &);
void setDevice(const QString& devicePath, const QString& deviceName);
///
/// @brief set new PixelDecimation value to ImageResampler
/// @param pixelDecimation The new pixelDecimation value
///
void setPixelDecimation(int pixelDecimation) override;
///
/// @brief overwrite Grabber.h implementation
///
void setSignalThreshold(
double redSignalThreshold,
double greenSignalThreshold,
double blueSignalThreshold,
int noSignalCounterThreshold = 50) override;
///
/// @brief overwrite Grabber.h implementation
///
void setSignalDetectionOffset(
double verticalMin,
double horizontalMin,
double verticalMax,
double horizontalMax) override;
///
/// @brief overwrite Grabber.h implementation
///
void setSignalDetectionEnable(bool enable) override;
///
/// @brief overwrite Grabber.h implementation
///
void setCecDetectionEnable(bool enable) override;
///
/// @brief overwrite Grabber.h implementation
///
void setDeviceVideoStandard(QString device, VideoStandard videoStandard) override;
///
/// @brief overwrite Grabber.h implementation
///
bool setInput(int input) override; bool setInput(int input) override;
///
/// @brief overwrite Grabber.h implementation
///
bool setWidthHeight(int width, int height) override; bool setWidthHeight(int width, int height) override;
void setEncoding(QString enc);
void setBrightnessContrastSaturationHue(int brightness, int contrast, int saturation, int hue);
void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold = 50);
void setSignalDetectionOffset( double verticalMin, double horizontalMin, double verticalMax, double horizontalMax);
void setSignalDetectionEnable(bool enable);
void setCecDetectionEnable(bool enable);
bool reload(bool force = false);
QRectF getSignalDetectionOffset() const { return QRectF(_x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max); } //used from hyperion-v4l2
/// ///
/// @brief overwrite Grabber.h implementation /// @brief Discover available V4L2 USB devices (for configuration).
/// @param[in] params Parameters used to overwrite discovery default behaviour
/// @return A JSON structure holding a list of USB devices found
/// ///
bool setFramerate(int fps) override; QJsonArray discover(const QJsonObject& params);
///
/// @brief overwrite Grabber.h implementation
///
QStringList getV4L2devices() const override;
///
/// @brief overwrite Grabber.h implementation
///
QString getV4L2deviceName(const QString& devicePath) const override;
///
/// @brief overwrite Grabber.h implementation
///
QMultiMap<QString, int> getV4L2deviceInputs(const QString& devicePath) const override;
///
/// @brief overwrite Grabber.h implementation
///
QStringList getResolutions(const QString& devicePath) const override;
///
/// @brief overwrite Grabber.h implementation
///
QStringList getFramerates(const QString& devicePath) const override;
public slots: public slots:
bool prepare();
bool start(); bool start();
void stop(); void stop();
void newThreadFrame(Image<ColorRgb> image);
#if defined(ENABLE_CEC)
void handleCecEvent(CECEvent event); void handleCecEvent(CECEvent event);
#endif
signals: signals:
void newFrame(const Image<ColorRgb> & image); void newFrame(const Image<ColorRgb> & image);
@ -166,36 +109,19 @@ private slots:
int read_frame(); int read_frame();
private: private:
void getV4Ldevices();
bool init(); bool init();
void uninit(); void uninit();
bool open_device(); bool open_device();
void close_device(); void close_device();
void init_read(unsigned int buffer_size); void init_read(unsigned int buffer_size);
void init_mmap(); void init_mmap();
void init_userp(unsigned int buffer_size); void init_userp(unsigned int buffer_size);
void init_device(VideoStandard videoStandard); void init_device(VideoStandard videoStandard);
void uninit_device(); void uninit_device();
void start_capturing(); void start_capturing();
void stop_capturing(); void stop_capturing();
bool process_image(const void *p, int size); bool process_image(const void *p, int size);
void process_image(const uint8_t *p, int size);
int xioctl(int request, void *arg); int xioctl(int request, void *arg);
int xioctl(int fileDescriptor, int request, void *arg); int xioctl(int fileDescriptor, int request, void *arg);
void throw_exception(const QString & error) void throw_exception(const QString & error)
@ -222,56 +148,28 @@ private:
size_t length; size_t length;
}; };
#ifdef HAVE_JPEG
struct errorManager
{
jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
static void errorHandler(j_common_ptr cInfo)
{
errorManager* mgr = reinterpret_cast<errorManager*>(cInfo->err);
longjmp(mgr->setjmp_buffer, 1);
}
static void outputHandler(j_common_ptr cInfo)
{
// Suppress fprintf warnings.
}
jpeg_decompress_struct* _decompress;
errorManager* _error;
#endif
#ifdef HAVE_TURBO_JPEG
tjhandle _decompress = nullptr;
int _subsamp;
#endif
private: private:
QString _deviceName; QString _currentDevicePath, _currentDeviceName;
std::map<QString, QString> _v4lDevices; EncoderThreadManager* _threadManager;
QMap<QString, V4L2Grabber::DeviceProperties> _deviceProperties; QMap<QString, V4L2Grabber::DeviceProperties> _deviceProperties;
QMap<QString, QList<DeviceControls>> _deviceControls;
VideoStandard _videoStandard;
io_method _ioMethod; io_method _ioMethod;
int _fileDescriptor; int _fileDescriptor;
std::vector<buffer> _buffers; std::vector<buffer> _buffers;
PixelFormat _pixelFormat; PixelFormat _pixelFormat, _pixelFormatConfig;
int _pixelDecimation;
int _lineLength; int _lineLength;
int _frameByteSize; int _frameByteSize;
QAtomicInt _currentFrame;
// signal detection // signal detection
int _noSignalCounterThreshold; int _noSignalCounterThreshold;
ColorRgb _noSignalThresholdColor; ColorRgb _noSignalThresholdColor;
bool _signalDetectionEnabled; bool _cecDetectionEnabled, _cecStandbyActivated, _signalDetectionEnabled, _noSignalDetected;
bool _cecDetectionEnabled;
bool _cecStandbyActivated;
bool _noSignalDetected;
int _noSignalCounter; int _noSignalCounter;
int _brightness, _contrast, _saturation, _hue;
double _x_frac_min; double _x_frac_min;
double _y_frac_min; double _y_frac_min;
double _x_frac_max; double _x_frac_max;
@ -279,9 +177,9 @@ private:
QSocketNotifier *_streamNotifier; QSocketNotifier *_streamNotifier;
bool _initialized; bool _initialized, _reload;
bool _deviceAutoDiscoverEnabled;
protected: protected:
void enumFrameIntervals(QStringList &framerates, int fileDescriptor, int pixelformat, int width, int height); void enumVideoCaptureDevices();
void enumFrameIntervals(QList<int> &framerates, int fileDescriptor, int pixelformat, int width, int height);
}; };

View File

@ -1,46 +0,0 @@
#pragma once
#include <hyperion/GrabberWrapper.h>
#include <grabber/V4L2Grabber.h>
class V4L2Wrapper : public GrabberWrapper
{
Q_OBJECT
public:
V4L2Wrapper(const QString & device,
const unsigned grabWidth,
const unsigned grabHeight,
const unsigned fps,
const unsigned input,
VideoStandard videoStandard,
PixelFormat pixelFormat,
int pixelDecimation );
~V4L2Wrapper() override;
bool getSignalDetectionEnable() const;
bool getCecDetectionEnable() const;
public slots:
bool start() override;
void stop() override;
void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold);
void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) override;
void setSignalDetectionOffset(double verticalMin, double horizontalMin, double verticalMax, double horizontalMax);
void setSignalDetectionEnable(bool enable);
void setCecDetectionEnable(bool enable);
void setDeviceVideoStandard(const QString& device, VideoStandard videoStandard);
void handleCecEvent(CECEvent event);
void handleSettingsUpdate(settings::type type, const QJsonDocument& config) override;
private slots:
void newFrame(const Image<ColorRgb> & image);
void readError(const char* err);
void action() override;
private:
/// The V4L2 grabber
V4L2Grabber _grabber;
};

View File

@ -0,0 +1,47 @@
#pragma once
#include <HyperionConfig.h> // Required to determine the cmake options
#include <hyperion/GrabberWrapper.h>
#if defined(ENABLE_MF)
#include <grabber/MFGrabber.h>
#elif defined(ENABLE_V4L2)
#include <grabber/V4L2Grabber.h>
#endif
#if defined(ENABLE_CEC)
#include <cec/CECEvent.h>
#endif
class VideoWrapper : public GrabberWrapper
{
Q_OBJECT
public:
VideoWrapper();
~VideoWrapper() override;
public slots:
bool start() override;
void stop() override;
#if defined(ENABLE_CEC)
void handleCecEvent(CECEvent event);
#endif
void handleSettingsUpdate(settings::type type, const QJsonDocument& config) override;
private slots:
void newFrame(const Image<ColorRgb> & image);
void readError(const char* err);
void action() override;
private:
/// The Media Foundation or V4L2 grabber
#if defined(ENABLE_MF)
MFGrabber _grabber;
#elif defined(ENABLE_V4L2)
V4L2Grabber _grabber;
#endif
};

View File

@ -2,7 +2,13 @@
#include <QAbstractEventDispatcher> #include <QAbstractEventDispatcher>
#include <QAbstractNativeEventFilter> #include <QAbstractNativeEventFilter>
#include <QCoreApplication> #include <QCoreApplication>
// QT includes
#include <QObject> #include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
// Hyperion-utils includes // Hyperion-utils includes
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
@ -20,11 +26,13 @@ class X11Grabber : public Grabber , public QAbstractNativeEventFilter
{ {
public: public:
X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation); X11Grabber(int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
~X11Grabber() override; ~X11Grabber() override;
bool Setup(); bool open();
bool setupDisplay();
/// ///
/// Captures a single snapshot of the display and writes the data to the given image. The /// Captures a single snapshot of the display and writes the data to the given image. The
@ -50,7 +58,7 @@ public:
/// ///
/// @brief Apply new pixelDecimation /// @brief Apply new pixelDecimation
/// ///
void setPixelDecimation(int pixelDecimation) override; bool setPixelDecimation(int pixelDecimation) override;
/// ///
/// Set the crop values /// Set the crop values
@ -59,22 +67,33 @@ public:
/// @param cropTop Top pixel crop /// @param cropTop Top pixel crop
/// @param cropBottom Bottom pixel crop /// @param cropBottom Bottom pixel crop
/// ///
void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) override; void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) override;
///
/// @brief Discover X11 screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
protected: protected:
bool nativeEventFilter(const QByteArray & eventType, void * message, long int * result) override; bool nativeEventFilter(const QByteArray & eventType, void * message, long int * result) override;
private: private:
bool _XShmAvailable, _XShmPixmapAvailable, _XRenderAvailable, _XRandRAvailable;
XImage* _xImage; void freeResources();
XShmSegmentInfo _shminfo; void setupResources();
/// Reference to the X11 display (nullptr if not opened) /// Reference to the X11 display (nullptr if not opened)
Display* _x11Display; Display* _x11Display;
Window _window; Window _window;
XWindowAttributes _windowAttr; XWindowAttributes _windowAttr;
XImage* _xImage;
XShmSegmentInfo _shminfo;
Pixmap _pixmap; Pixmap _pixmap;
XRenderPictFormat* _srcFormat; XRenderPictFormat* _srcFormat;
XRenderPictFormat* _dstFormat; XRenderPictFormat* _dstFormat;
@ -85,15 +104,19 @@ private:
int _XRandREventBase; int _XRandREventBase;
XTransform _transform; XTransform _transform;
int _pixelDecimation;
unsigned _screenWidth; unsigned _calculatedWidth;
unsigned _screenHeight; unsigned _calculatedHeight;
unsigned _src_x; unsigned _src_x;
unsigned _src_y; unsigned _src_y;
Image<ColorRgb> _image; bool _XShmAvailable;
bool _XShmPixmapAvailable;
bool _XRenderAvailable;
bool _XRandRAvailable;
bool _isWayland;
void freeResources(); Logger * _logger;
void setupResources();
Image<ColorRgb> _image;
}; };

View File

@ -9,25 +9,26 @@
/// ///
/// The X11Wrapper uses an instance of the X11Grabber to obtain ImageRgb's from the /// The X11Wrapper uses an instance of the X11Grabber to obtain ImageRgb's from the displayed content.
/// displayed content. This ImageRgb is processed to a ColorRgb for each led and commmited to the /// This ImageRgb is processed to a ColorRgb for each led and committed to the attached Hyperion.
/// attached Hyperion.
/// ///
class X11Wrapper: public GrabberWrapper class X11Wrapper: public GrabberWrapper
{ {
public: public:
/// ///
/// Constructs the framebuffer frame grabber with a specified grab size and update rate. /// Constructs the X11 frame grabber with a specified grab size and update rate.
/// ///
/// @param[in] device X11 device name/path
/// @param[in] grabWidth The width of the grabbed image [pixels]
/// @param[in] grabHeight The height of the grabbed images [pixels]
/// @param[in] updateRate_Hz The image grab rate [Hz] /// @param[in] updateRate_Hz The image grab rate [Hz]
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// ///
X11Wrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, unsigned updateRate_Hz); X11Wrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ,
int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION,
int cropLeft=0, int cropRight=0,
int cropTop=0, int cropBottom=0
);
/// ///
/// Destructor of this framebuffer frame grabber. Releases any claimed resources. /// Destructor of this frame grabber. Releases any claimed resources.
/// ///
~X11Wrapper() override; ~X11Wrapper() override;

View File

@ -1,7 +1,11 @@
#pragma once #pragma once
#include <QAbstractNativeEventFilter> #include <QAbstractNativeEventFilter>
// QT includes
#include <QObject> #include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <hyperion/Grabber.h> #include <hyperion/Grabber.h>
@ -21,16 +25,28 @@ class XcbGrabber : public Grabber, public QAbstractNativeEventFilter
Q_OBJECT Q_OBJECT
public: public:
XcbGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation); XcbGrabber(int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
~XcbGrabber() override; ~XcbGrabber() override;
bool Setup(); bool open();
bool setupDisplay();
int grabFrame(Image<ColorRgb> & image, bool forceUpdate = false); int grabFrame(Image<ColorRgb> & image, bool forceUpdate = false);
int updateScreenDimensions(bool force = false); int updateScreenDimensions(bool force = false);
void setVideoMode(VideoMode mode) override; void setVideoMode(VideoMode mode) override;
bool setWidthHeight(int width, int height) override { return true; } bool setWidthHeight(int width, int height) override { return true; }
void setPixelDecimation(int pixelDecimation) override; bool setPixelDecimation(int pixelDecimation) override;
void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) override; void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) override;
///
/// @brief Discover XCB screens available (for configuration).
///
/// @param[in] params Parameters used to overwrite discovery default behaviour
///
/// @return A JSON structure holding a list of devices found
///
QJsonObject discover(const QJsonObject& params);
private: private:
bool nativeEventFilter(const QByteArray & eventType, void * message, long int * result) override; bool nativeEventFilter(const QByteArray & eventType, void * message, long int * result) override;
@ -52,8 +68,7 @@ private:
xcb_render_transform_t _transform; xcb_render_transform_t _transform;
xcb_shm_seg_t _shminfo; xcb_shm_seg_t _shminfo;
int _pixelDecimation; int _screen_num;
unsigned _screenWidth; unsigned _screenWidth;
unsigned _screenHeight; unsigned _screenHeight;
unsigned _src_x; unsigned _src_x;
@ -63,6 +78,8 @@ private:
bool _XcbRandRAvailable; bool _XcbRandRAvailable;
bool _XcbShmAvailable; bool _XcbShmAvailable;
bool _XcbShmPixmapAvailable; bool _XcbShmPixmapAvailable;
bool _isWayland;
Logger * _logger; Logger * _logger;
uint8_t * _shmData; uint8_t * _shmData;

View File

@ -11,7 +11,12 @@
class XcbWrapper: public GrabberWrapper class XcbWrapper: public GrabberWrapper
{ {
public: public:
XcbWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, const unsigned updateRate_Hz); XcbWrapper( int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ,
int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION,
int cropLeft=0, int cropRight=0,
int cropTop=0, int cropBottom=0
);
~XcbWrapper() override; ~XcbWrapper() override;
public slots: public slots:

View File

@ -6,23 +6,21 @@
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <utils/Image.h> #include <utils/Image.h>
#include <utils/VideoMode.h> #include <utils/VideoMode.h>
#include <grabber/VideoStandard.h> #include <utils/VideoStandard.h>
#include <utils/ImageResampler.h> #include <utils/ImageResampler.h>
#include <utils/Logger.h> #include <utils/Logger.h>
#include <utils/Components.h> #include <utils/Components.h>
#include <QMultiMap>
/// ///
/// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler) /// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler)
/// Overwrite the videoMode with setVideoMode()
/// Overwrite setCropping()
class Grabber : public QObject class Grabber : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Grabber(const QString& grabberName = "", int width=0, int height=0, int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
Grabber(const QString& grabberName = "", int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
/// ///
/// Set the video mode (2D/3D) /// Set the video mode (2D/3D)
@ -31,12 +29,18 @@ public:
virtual void setVideoMode(VideoMode mode); virtual void setVideoMode(VideoMode mode);
/// ///
/// @brief Apply new crop values, on errors reject the values /// Apply new flip mode (vertical/horizontal/both)
/// @param[in] mode The new flip mode
/// ///
virtual void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom); virtual void setFlipMode(FlipMode mode);
/// ///
/// @brief Apply new video input (used from v4l) /// @brief Apply new crop values, on errors reject the values
///
virtual void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom);
///
/// @brief Apply new video input (used from v4l2/MediaFoundation)
/// @param input device input /// @param input device input
/// ///
virtual bool setInput(int input); virtual bool setInput(int input);
@ -48,108 +52,80 @@ public:
virtual bool setWidthHeight(int width, int height); virtual bool setWidthHeight(int width, int height);
/// ///
/// @brief Apply new framerate (used from v4l) /// @brief Apply new capture framerate in Hz
/// @param fps framesPerSecond /// @param fps framesPerSecond
/// ///
virtual bool setFramerate(int fps); virtual bool setFramerate(int fps);
/// ///
/// @brief Apply new pixelDecimation (used from x11, xcb and qt) /// @brief Apply new framerate software decimation (used from v4l2/MediaFoundation)
/// @param decimation how many frames per second to omit
/// ///
virtual void setPixelDecimation(int pixelDecimation) {} virtual void setFpsSoftwareDecimation(int decimation);
/// ///
/// @brief Apply new signalThreshold (used from v4l) /// @brief Apply videoStandard (used from v4l2)
/// ///
virtual void setSignalThreshold( virtual void setVideoStandard(VideoStandard videoStandard);
double redSignalThreshold,
double greenSignalThreshold,
double blueSignalThreshold,
int noSignalCounterThreshold = 50) {}
///
/// @brief Apply new SignalDetectionOffset (used from v4l)
///
virtual void setSignalDetectionOffset(
double verticalMin,
double horizontalMin,
double verticalMax,
double horizontalMax) {}
/// ///
/// @brief Apply SignalDetectionEnable (used from v4l) /// @brief Apply new pixelDecimation
/// ///
virtual void setSignalDetectionEnable(bool enable) {} virtual bool setPixelDecimation(int pixelDecimation);
///
/// @brief Apply CecDetectionEnable (used from v4l)
///
virtual void setCecDetectionEnable(bool enable) {}
///
/// @brief Apply device and videoStanded (used from v4l)
///
virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard) {}
/// ///
/// @brief Apply display index (used from qt) /// @brief Apply display index (used from qt)
/// ///
virtual void setDisplayIndex(int index) {} virtual bool setDisplayIndex(int /*index*/) { return true; }
///
/// @brief Apply path for device (used from framebuffer)
///
virtual void setDevicePath(const QString& path) {}
///
/// @brief get current resulting height of image (after crop)
///
virtual int getImageWidth() { return _width; }
///
/// @brief get current resulting width of image (after crop)
///
virtual int getImageHeight() { return _height; }
/// ///
/// @brief Prevent the real capture implementation from capturing if disabled /// @brief Prevent the real capture implementation from capturing if disabled
/// ///
void setEnabled(bool enable); virtual void setEnabled(bool enable);
/// ///
/// @brief Get a list of all available V4L devices /// @brief get current resulting height of image (after crop)
/// @return List of all available V4L devices on success else empty List
/// ///
virtual QStringList getV4L2devices() const { return QStringList(); } int getImageWidth() const { return _width; }
/// ///
/// @brief Get the V4L device name /// @brief get current resulting width of image (after crop)
/// @param devicePath The device path
/// @return The name of the V4L device on success else empty String
/// ///
virtual QString getV4L2deviceName(const QString& /*devicePath*/) const { return QString(); } int getImageHeight() const { return _height; }
/// ///
/// @brief Get a name/index pair of supported device inputs /// @brief Get current capture framerate in Hz
/// @param devicePath The device path /// @param fps framesPerSecond
/// @return multi pair of name/index on success else empty pair
/// ///
virtual QMultiMap<QString, int> getV4L2deviceInputs(const QString& /*devicePath*/) const { return QMultiMap<QString, int>(); } int getFramerate() const { return _fps; }
/// ///
/// @brief Get a list of supported device resolutions /// @brief Get capture interval in ms
/// @param devicePath The device path
/// @return List of resolutions on success else empty List
/// ///
virtual QStringList getResolutions(const QString& /*devicePath*/) const { return QStringList(); } int getUpdateInterval() const { return 1000/_fps; }
/// ///
/// @brief Get a list of supported device framerates /// @brief Get pixelDecimation
/// @param devicePath The device path
/// @return List of framerates on success else empty List
/// ///
virtual QStringList getFramerates(const QString& devicePath) const { return QStringList(); } int getPixelDecimation() const { return _pixelDecimation; }
QString getGrabberName() const { return _grabberName; }
protected slots:
///
/// @brief Set device in error state
///
/// @param[in] errorMsg The error message to be logged
///
virtual void setInError( const QString& errorMsg);
protected: protected:
QString _grabberName;
/// logger instance
Logger * _log;
ImageResampler _imageResampler; ImageResampler _imageResampler;
bool _useImageResampler; bool _useImageResampler;
@ -157,6 +133,15 @@ protected:
/// the selected VideoMode /// the selected VideoMode
VideoMode _videoMode; VideoMode _videoMode;
/// the used video standard
VideoStandard _videoStandard;
/// Image size decimation
int _pixelDecimation;
/// the used Flip Mode
FlipMode _flipMode;
/// With of the captured snapshot [pixels] /// With of the captured snapshot [pixels]
int _width; int _width;
@ -166,15 +151,21 @@ protected:
/// frame per second /// frame per second
int _fps; int _fps;
/// fps software decimation
int _fpsSoftwareDecimation;
/// device input /// device input
int _input; int _input;
/// number of pixels to crop after capturing /// number of pixels to crop after capturing
int _cropLeft, _cropRight, _cropTop, _cropBottom; int _cropLeft, _cropRight, _cropTop, _cropBottom;
bool _enabled; // Device states
/// logger instance /// Is the device enabled?
Logger * _log; bool _isEnabled;
/// Is the device in error state and stopped?
bool _isDeviceInError;
}; };

View File

@ -12,30 +12,38 @@
#include <utils/Image.h> #include <utils/Image.h>
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <utils/VideoMode.h> #include <utils/VideoMode.h>
#include <utils/PixelFormat.h>
#include <utils/settings.h> #include <utils/settings.h>
#include <utils/VideoStandard.h>
class Grabber; class Grabber;
class GlobalSignals; class GlobalSignals;
class QTimer; class QTimer;
/// List of Hyperion instances that requested screen capt
static QList<int> GRABBER_SYS_CLIENTS;
static QList<int> GRABBER_V4L_CLIENTS;
/// ///
/// This class will be inherted by FramebufferWrapper and others which contains the real capture interface /// This class will be inherited by GrabberWrappers which contains the real capture interface
/// ///
class GrabberWrapper : public QObject class GrabberWrapper : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
GrabberWrapper(const QString& grabberName, Grabber * ggrabber, unsigned width, unsigned height, unsigned updateRate_Hz = 0); GrabberWrapper(const QString& grabberName, Grabber * ggrabber,int updateRate_Hz = DEFAULT_RATE_HZ);
~GrabberWrapper() override; ~GrabberWrapper() override;
static GrabberWrapper* instance; static GrabberWrapper* instance;
static GrabberWrapper* getInstance(){ return instance; } static GrabberWrapper* getInstance(){ return instance; }
static const int DEFAULT_RATE_HZ;
static const int DEFAULT_MIN_GRAB_RATE_HZ;
static const int DEFAULT_MAX_GRAB_RATE_HZ;
static const int DEFAULT_PIXELDECIMATION;
static QMap<int, QString> GRABBER_SYS_CLIENTS;
static QMap<int, QString> GRABBER_V4L_CLIENTS;
static bool GLOBAL_GRABBER_SYS_ENABLE;
static bool GLOBAL_GRABBER_V4L_ENABLE;
/// ///
/// Starts the grabber which produces led values with the specified update rate /// Starts the grabber which produces led values with the specified update rate
/// ///
@ -56,45 +64,17 @@ public:
/// ///
virtual bool isActive() const; virtual bool isActive() const;
///
/// @brief Get a list of all available V4L devices
/// @return List of all available V4L devices on success else empty List
///
virtual QStringList getV4L2devices() const;
///
/// @brief Get the V4L device name
/// @param devicePath The device path
/// @return The name of the V4L device on success else empty String
///
virtual QString getV4L2deviceName(const QString& devicePath) const;
///
/// @brief Get a name/index pair of supported device inputs
/// @param devicePath The device path
/// @return multi pair of name/index on success else empty pair
///
virtual QMultiMap<QString, int> getV4L2deviceInputs(const QString& devicePath) const;
///
/// @brief Get a list of supported device resolutions
/// @param devicePath The device path
/// @return List of resolutions on success else empty List
///
virtual QStringList getResolutions(const QString& devicePath) const;
///
/// @brief Get a list of supported device framerates
/// @param devicePath The device path
/// @return List of framerates on success else empty List
///
virtual QStringList getFramerates(const QString& devicePath) const;
/// ///
/// @brief Get active grabber name /// @brief Get active grabber name
/// @return Active grabber name /// @param hyperionInd The instance index
/// @return Active grabbers
/// ///
virtual QString getActive() const; virtual QStringList getActive(int inst) const;
bool getSysGrabberState() const { return GLOBAL_GRABBER_SYS_ENABLE; }
void setSysGrabberState(bool sysGrabberState){ GLOBAL_GRABBER_SYS_ENABLE = sysGrabberState; }
bool getV4lGrabberState() const { return GLOBAL_GRABBER_V4L_ENABLE; }
void setV4lGrabberState(bool v4lGrabberState){ GLOBAL_GRABBER_V4L_ENABLE = v4lGrabberState; }
static QStringList availableGrabbers(); static QStringList availableGrabbers();
@ -130,6 +110,12 @@ public slots:
/// ///
virtual void setVideoMode(VideoMode videoMode); virtual void setVideoMode(VideoMode videoMode);
///
/// Set the Flip mode
/// @param flipMode The new flip mode
///
virtual void setFlipMode(const QString &flipMode);
/// ///
/// Set the crop values /// Set the crop values
/// @param cropLeft Left pixel crop /// @param cropLeft Left pixel crop
@ -137,11 +123,11 @@ public slots:
/// @param cropTop Top pixel crop /// @param cropTop Top pixel crop
/// @param cropBottom Bottom pixel crop /// @param cropBottom Bottom pixel crop
/// ///
virtual void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom); virtual void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom);
/// ///
/// @brief Handle settings update from HyperionDaemon Settingsmanager emit /// @brief Handle settings update from HyperionDaemon Settingsmanager emit
/// @param type settingyType from enum /// @param type settingsType from enum
/// @param config configuration object /// @param config configuration object
/// ///
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config); virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
@ -159,22 +145,38 @@ private slots:
/// ///
/// @brief Update Update capture rate /// @brief Update Update capture rate
/// @param type interval between frames in millisecons /// @param type interval between frames in milliseconds
/// ///
void updateTimer(int interval); void updateTimer(int interval);
protected: protected:
///
/// @brief Opens the input device.
///
/// @return True, on success (i.e. device is ready)
///
virtual bool open() { return true; }
///
/// @brief Closes the input device.
///
/// @return True on success (i.e. device is closed)
///
virtual bool close() { return true; }
QString _grabberName; QString _grabberName;
/// The Logger instance
Logger * _log;
/// The timer for generating events with the specified update rate /// The timer for generating events with the specified update rate
QTimer* _timer; QTimer* _timer;
/// The calced update rate [ms] /// The calculated update rate [ms]
int _updateInterval_ms; int _updateInterval_ms;
/// The Logger instance
Logger * _log;
Grabber *_ggrabber; Grabber *_ggrabber;
/// The image used for grabbing frames /// The image used for grabbing frames

View File

@ -99,7 +99,7 @@ public:
/// ///
QString getActiveDeviceType() const; QString getActiveDeviceType() const;
bool getReadOnlyMode() {return _readOnlyMode; }; bool getReadOnlyMode() {return _readOnlyMode; }
public slots: public slots:
@ -193,7 +193,7 @@ public slots:
bool clear(int priority, bool forceClearAll=false); bool clear(int priority, bool forceClearAll=false);
/// ############# /// #############
// EFFECTENGINE /// EFFECTENGINE
/// ///
/// @brief Get a pointer to the effect engine /// @brief Get a pointer to the effect engine
/// @return EffectEngine instance pointer /// @return EffectEngine instance pointer

View File

@ -57,8 +57,11 @@ public:
//Foreground and Background priorities //Foreground and Background priorities
const static int FG_PRIORITY; const static int FG_PRIORITY;
const static int BG_PRIORITY; const static int BG_PRIORITY;
const static int MANUAL_SELECTED_PRIORITY;
/// The lowest possible priority, which is used when no priority channels are active /// The lowest possible priority, which is used when no priority channels are active
const static int LOWEST_PRIORITY; const static int LOWEST_PRIORITY;
/// Timeout used to identify a non active priority
const static int TIMEOUT_NOT_ACTIVE_PRIO;
/// ///
/// Constructs the PriorityMuxer for the given number of LEDs (used to switch to black when /// Constructs the PriorityMuxer for the given number of LEDs (used to switch to black when
@ -84,7 +87,7 @@ public:
/// @param update True to update _currentPriority - INTERNAL usage. /// @param update True to update _currentPriority - INTERNAL usage.
/// @return True if changed has been applied, false if the state is unchanged /// @return True if changed has been applied, false if the state is unchanged
/// ///
bool setSourceAutoSelectEnabled(bool enabel, bool update = true); bool setSourceAutoSelectEnabled(bool enable, bool update = true);
/// ///
/// @brief Get the state of source auto selection /// @brief Get the state of source auto selection

View File

@ -9,12 +9,13 @@ class ImageResampler
{ {
public: public:
ImageResampler(); ImageResampler();
~ImageResampler(); ~ImageResampler() {}
void setHorizontalPixelDecimation(int decimator); void setHorizontalPixelDecimation(int decimator) { _horizontalDecimation = decimator; }
void setVerticalPixelDecimation(int decimator); void setVerticalPixelDecimation(int decimator) { _verticalDecimation = decimator; }
void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom); void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom);
void setVideoMode(VideoMode mode); void setVideoMode(VideoMode mode) { _videoMode = mode; }
void setFlipMode(FlipMode mode) { _flipMode = mode; }
void processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat, Image<ColorRgb> & outputImage) const; void processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat, Image<ColorRgb> & outputImage) const;
private: private:
@ -25,5 +26,6 @@ private:
int _cropTop; int _cropTop;
int _cropBottom; int _cropBottom;
VideoMode _videoMode; VideoMode _videoMode;
FlipMode _flipMode;
}; };

View File

@ -12,7 +12,9 @@ enum class PixelFormat {
BGR24, BGR24,
RGB32, RGB32,
BGR32, BGR32,
#ifdef HAVE_JPEG_DECODER NV12,
I420,
#ifdef HAVE_TURBO_JPEG
MJPEG, MJPEG,
#endif #endif
NO_CHANGE NO_CHANGE
@ -23,32 +25,40 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat)
// convert to lower case // convert to lower case
QString format = pixelFormat.toLower(); QString format = pixelFormat.toLower();
if (format.compare("yuyv") ) if (format.compare("yuyv") == 0)
{ {
return PixelFormat::YUYV; return PixelFormat::YUYV;
} }
else if (format.compare("uyvy") ) else if (format.compare("uyvy") == 0)
{ {
return PixelFormat::UYVY; return PixelFormat::UYVY;
} }
else if (format.compare("bgr16") ) else if (format.compare("bgr16") == 0)
{ {
return PixelFormat::BGR16; return PixelFormat::BGR16;
} }
else if (format.compare("bgr24") ) else if (format.compare("bgr24") == 0)
{ {
return PixelFormat::BGR24; return PixelFormat::BGR24;
} }
else if (format.compare("rgb32") ) else if (format.compare("rgb32") == 0)
{ {
return PixelFormat::RGB32; return PixelFormat::RGB32;
} }
else if (format.compare("bgr32") ) else if (format.compare("bgr32") == 0)
{ {
return PixelFormat::BGR32; return PixelFormat::BGR32;
} }
#ifdef HAVE_JPEG_DECODER else if (format.compare("i420") == 0)
else if (format.compare("mjpeg") ) {
return PixelFormat::I420;
}
else if (format.compare("nv12") == 0)
{
return PixelFormat::NV12;
}
#ifdef HAVE_TURBO_JPEG
else if (format.compare("mjpeg") == 0)
{ {
return PixelFormat::MJPEG; return PixelFormat::MJPEG;
} }
@ -57,3 +67,102 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat)
// return the default NO_CHANGE // return the default NO_CHANGE
return PixelFormat::NO_CHANGE; return PixelFormat::NO_CHANGE;
} }
inline QString pixelFormatToString(const PixelFormat& pixelFormat)
{
if ( pixelFormat == PixelFormat::YUYV)
{
return "YUYV";
}
else if (pixelFormat == PixelFormat::UYVY)
{
return "UYVY";
}
else if (pixelFormat == PixelFormat::BGR16)
{
return "BGR16";
}
else if (pixelFormat == PixelFormat::BGR24)
{
return "BGR24";
}
else if (pixelFormat == PixelFormat::RGB32)
{
return "RGB32";
}
else if (pixelFormat == PixelFormat::BGR32)
{
return "BGR32";
}
else if (pixelFormat == PixelFormat::I420)
{
return "I420";
}
else if (pixelFormat == PixelFormat::NV12)
{
return "NV12";
}
#ifdef HAVE_TURBO_JPEG
else if (pixelFormat == PixelFormat::MJPEG)
{
return "MJPEG";
}
#endif
// return the default NO_CHANGE
return "NO_CHANGE";
}
/**
* Enumeration of the possible flip modes
*/
enum class FlipMode
{
HORIZONTAL,
VERTICAL,
BOTH,
NO_CHANGE
};
inline FlipMode parseFlipMode(const QString& flipMode)
{
// convert to lower case
QString mode = flipMode.toLower();
if (mode.compare("horizontal") == 0)
{
return FlipMode::HORIZONTAL;
}
else if (mode.compare("vertical") == 0)
{
return FlipMode::VERTICAL;
}
else if (mode.compare("both") == 0)
{
return FlipMode::BOTH;
}
// return the default NO_CHANGE
return FlipMode::NO_CHANGE;
}
inline QString flipModeToString(const FlipMode& flipMode)
{
if ( flipMode == FlipMode::HORIZONTAL)
{
return "horizontal";
}
else if (flipMode == FlipMode::VERTICAL)
{
return "vertical";
}
else if (flipMode == FlipMode::BOTH)
{
return "both";
}
// return the default NO_CHANGE
return "NO_CHANGE";
}

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <QString>
/** /**
* Enumeration of the possible video standards the grabber can be set to * Enumeration of the possible video standards the grabber can be set to
*/ */
@ -13,17 +15,17 @@ enum class VideoStandard {
inline VideoStandard parseVideoStandard(const QString& videoStandard) inline VideoStandard parseVideoStandard(const QString& videoStandard)
{ {
// convert to lower case // convert to lower case
QString standard = videoStandard.toLower(); QString standard = videoStandard.toUpper();
if (standard == "pal") if (standard == "PAL")
{ {
return VideoStandard::PAL; return VideoStandard::PAL;
} }
else if (standard == "ntsc") else if (standard == "NTSC")
{ {
return VideoStandard::NTSC; return VideoStandard::NTSC;
} }
else if (standard == "secam") else if (standard == "SECAM")
{ {
return VideoStandard::SECAM; return VideoStandard::SECAM;
} }
@ -31,3 +33,14 @@ inline VideoStandard parseVideoStandard(const QString& videoStandard)
// return the default NO_CHANGE // return the default NO_CHANGE
return VideoStandard::NO_CHANGE; return VideoStandard::NO_CHANGE;
} }
inline QString VideoStandard2String(VideoStandard videoStandard)
{
switch (videoStandard)
{
case VideoStandard::PAL: return "PAL";
case VideoStandard::NTSC: return "NTSC";
case VideoStandard::SECAM: return "SECAM";
default: return "NO_CHANGE";
}
}

View File

@ -1,4 +1,6 @@
#pragma once #pragma once
#define QSTRING_CSTR(str) str.toLocal8Bit().constData() #define QSTRING_CSTR(str) str.toLocal8Bit().constData()
typedef QList< int > QIntList;

View File

@ -8,6 +8,7 @@
// fg effect // fg effect
#include <hyperion/Hyperion.h> #include <hyperion/Hyperion.h>
#include <hyperion/PriorityMuxer.h> #include <hyperion/PriorityMuxer.h>
#include <effectengine/Effect.h>
/// ///
/// @brief Provide utility methods for Hyperion class /// @brief Provide utility methods for Hyperion class
@ -17,7 +18,6 @@ namespace hyperion {
void handleInitialEffect(Hyperion* hyperion, const QJsonObject& FGEffectConfig) void handleInitialEffect(Hyperion* hyperion, const QJsonObject& FGEffectConfig)
{ {
#define FGCONFIG_ARRAY fgColorConfig.toArray() #define FGCONFIG_ARRAY fgColorConfig.toArray()
const int DURATION_INFINITY = 0;
// initial foreground effect/color // initial foreground effect/color
if (FGEffectConfig["enable"].toBool(true)) if (FGEffectConfig["enable"].toBool(true))
@ -27,7 +27,7 @@ namespace hyperion {
const QJsonValue fgColorConfig = FGEffectConfig["color"]; const QJsonValue fgColorConfig = FGEffectConfig["color"];
int default_fg_duration_ms = 3000; int default_fg_duration_ms = 3000;
int fg_duration_ms = FGEffectConfig["duration_ms"].toInt(default_fg_duration_ms); int fg_duration_ms = FGEffectConfig["duration_ms"].toInt(default_fg_duration_ms);
if (fg_duration_ms == DURATION_INFINITY) if (fg_duration_ms <= Effect::ENDLESS)
{ {
fg_duration_ms = default_fg_duration_ms; fg_duration_ms = default_fg_duration_ms;
Warning(Logger::getInstance("HYPERION"), "foreground effect duration 'infinity' is forbidden, set to default value %d ms",default_fg_duration_ms); Warning(Logger::getInstance("HYPERION"), "foreground effect duration 'infinity' is forbidden, set to default value %d ms",default_fg_duration_ms);

View File

@ -1,3 +1,12 @@
# Find the BCM-package (VC control)
IF ( "${PLATFORM}" MATCHES rpi)
find_package(BCM REQUIRED)
include_directories(${BCM_INCLUDE_DIRS})
ELSE()
SET(BCM_INCLUDE_DIRS "")
SET(BCM_LIBRARIES "")
ENDIF()
# Define the current source locations # Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/api) SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/api)
@ -12,6 +21,11 @@ add_library(hyperion-api
${Api_RESOURCES} ${Api_RESOURCES}
) )
if(ENABLE_DX)
include_directories(${DIRECTX9_INCLUDE_DIRS})
target_link_libraries(hyperion-api ${DIRECTX9_LIBRARIES})
endif(ENABLE_DX)
target_link_libraries(hyperion-api target_link_libraries(hyperion-api
hyperion hyperion
hyperion-utils hyperion-utils

View File

@ -0,0 +1,28 @@
{
"type":"object",
"required":true,
"properties": {
"command": {
"type": "string",
"required": true,
"enum": [ "inputsource" ]
},
"tan": {
"type": "integer"
},
"subcommand": {
"type": "string",
"required": true,
"enum": [ "discover", "getProperties" ]
},
"sourceType": {
"type": "string",
"required": true
},
"params": {
"type": "object",
"required": false
}
},
"additionalProperties": false
}

View File

@ -5,7 +5,7 @@
"command": { "command": {
"type" : "string", "type" : "string",
"required" : true, "required" : true,
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "instance", "leddevice", "transform", "correction" , "temperature"] "enum": [ "color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging", "processing", "sysinfo", "videomode", "authorize", "instance", "leddevice", "inputsource", "transform", "correction", "temperature" ]
} }
} }
} }

View File

@ -21,6 +21,7 @@
<file alias="schema-authorize">JSONRPC_schema/schema-authorize.json</file> <file alias="schema-authorize">JSONRPC_schema/schema-authorize.json</file>
<file alias="schema-instance">JSONRPC_schema/schema-instance.json</file> <file alias="schema-instance">JSONRPC_schema/schema-instance.json</file>
<file alias="schema-leddevice">JSONRPC_schema/schema-leddevice.json</file> <file alias="schema-leddevice">JSONRPC_schema/schema-leddevice.json</file>
<file alias="schema-inputsource">JSONRPC_schema/schema-inputsource.json</file>
<!-- The following schemas are derecated but used to ensure backward compatibility with hyperion Classic remote control--> <!-- The following schemas are derecated but used to ensure backward compatibility with hyperion Classic remote control-->
<file alias="schema-transform">JSONRPC_schema/schema-hyperion-classic.json</file> <file alias="schema-transform">JSONRPC_schema/schema-hyperion-classic.json</file>
<file alias="schema-correction">JSONRPC_schema/schema-hyperion-classic.json</file> <file alias="schema-correction">JSONRPC_schema/schema-hyperion-classic.json</file>

View File

@ -16,7 +16,45 @@
#include <leddevice/LedDevice.h> #include <leddevice/LedDevice.h>
#include <leddevice/LedDeviceFactory.h> #include <leddevice/LedDeviceFactory.h>
#include <HyperionConfig.h> // Required to determine the cmake options
#include <hyperion/GrabberWrapper.h> #include <hyperion/GrabberWrapper.h>
#include <grabber/QtGrabber.h>
#if defined(ENABLE_MF)
#include <grabber/MFGrabber.h>
#elif defined(ENABLE_V4L2)
#include <grabber/V4L2Grabber.h>
#endif
#if defined(ENABLE_X11)
#include <grabber/X11Grabber.h>
#endif
#if defined(ENABLE_XCB)
#include <grabber/XcbGrabber.h>
#endif
#if defined(ENABLE_DX)
#include <grabber/DirectXGrabber.h>
#endif
#if defined(ENABLE_FB)
#include <grabber/FramebufferFrameGrabber.h>
#endif
#if defined(ENABLE_DISPMANX)
#include <grabber/DispmanxFrameGrabber.h>
#endif
#if defined(ENABLE_AMLOGIC)
#include <grabber/AmlogicGrabber.h>
#endif
#if defined(ENABLE_OSX)
#include <grabber/OsxFrameGrabber.h>
#endif
#include <utils/jsonschema/QJsonFactory.h> #include <utils/jsonschema/QJsonFactory.h>
#include <utils/jsonschema/QJsonSchemaChecker.h> #include <utils/jsonschema/QJsonSchemaChecker.h>
#include <HyperionConfig.h> #include <HyperionConfig.h>
@ -41,7 +79,10 @@
using namespace hyperion; using namespace hyperion;
JsonAPI::JsonAPI(QString peerAddress, Logger* log, bool localConnection, QObject* parent, bool noListener) // Constants
namespace { const bool verbose = false; }
JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject *parent, bool noListener)
: API(log, localConnection, parent) : API(log, localConnection, parent)
{ {
_noListener = noListener; _noListener = noListener;
@ -86,7 +127,7 @@ bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced)
return false; return false;
} }
void JsonAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader) void JsonAPI::handleMessage(const QString &messageString, const QString &httpAuthHeader)
{ {
const QString ident = "JsonRpc@" + _peerAddress; const QString ident = "JsonRpc@" + _peerAddress;
QJsonObject message; QJsonObject message;
@ -174,6 +215,8 @@ proceed:
handleInstanceCommand(message, command, tan); handleInstanceCommand(message, command, tan);
else if (command == "leddevice") else if (command == "leddevice")
handleLedDeviceCommand(message, command, tan); handleLedDeviceCommand(message, command, tan);
else if (command == "inputsource")
handleInputSourceCommand(message, command, tan);
// BEGIN | The following commands are deprecated but used to ensure backward compatibility with hyperion Classic remote control // BEGIN | The following commands are deprecated but used to ensure backward compatibility with hyperion Classic remote control
else if (command == "clearall") else if (command == "clearall")
@ -187,17 +230,17 @@ proceed:
handleNotImplemented(command, tan); handleNotImplemented(command, tan);
} }
void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleColorCommand(const QJsonObject &message, const QString &command, int tan)
{ {
emit forwardJsonMessage(message); emit forwardJsonMessage(message);
int priority = message["priority"].toInt(); int priority = message["priority"].toInt();
int duration = message["duration"].toInt(-1); int duration = message["duration"].toInt(-1);
const QString origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress; const QString origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress;
const QJsonArray& jsonColor = message["color"].toArray(); const QJsonArray &jsonColor = message["color"].toArray();
std::vector<uint8_t> colors; std::vector<uint8_t> colors;
// TODO faster copy // TODO faster copy
for (const auto& entry : jsonColor) for (const auto &entry : jsonColor)
{ {
colors.emplace_back(uint8_t(entry.toInt())); colors.emplace_back(uint8_t(entry.toInt()));
} }
@ -206,7 +249,7 @@ void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& comm
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleImageCommand(const QJsonObject &message, const QString &command, int tan)
{ {
emit forwardJsonMessage(message); emit forwardJsonMessage(message);
@ -230,7 +273,7 @@ void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& comm
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleEffectCommand(const QJsonObject &message, const QString &command, int tan)
{ {
emit forwardJsonMessage(message); emit forwardJsonMessage(message);
@ -249,19 +292,19 @@ void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& com
sendErrorReply("Effect '" + dat.effectName + "' not found", command, tan); sendErrorReply("Effect '" + dat.effectName + "' not found", command, tan);
} }
void JsonAPI::handleCreateEffectCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleCreateEffectCommand(const QJsonObject &message, const QString &command, int tan)
{ {
const QString resultMsg = API::saveEffect(message); const QString resultMsg = API::saveEffect(message);
resultMsg.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(resultMsg, command, tan); resultMsg.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(resultMsg, command, tan);
} }
void JsonAPI::handleDeleteEffectCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleDeleteEffectCommand(const QJsonObject &message, const QString &command, int tan)
{ {
const QString res = API::deleteEffect(message["name"].toString()); const QString res = API::deleteEffect(message["name"].toString());
res.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(res, command, tan); res.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(res, command, tan);
} }
void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, int tan) void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command, int tan)
{ {
// create result // create result
QJsonObject result; QJsonObject result;
@ -304,7 +347,7 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, i
emit callbackMessage(result); emit callbackMessage(result);
} }
void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString &command, int tan)
{ {
QJsonObject info; QJsonObject info;
@ -315,9 +358,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
activePriorities.removeAll(255); activePriorities.removeAll(255);
int currentPriority = _hyperion->getCurrentPriority(); int currentPriority = _hyperion->getCurrentPriority();
for (int priority : activePriorities) for(int priority : activePriorities)
{ {
const Hyperion::InputInfo& priorityInfo = _hyperion->getPriorityInfo(priority); const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(priority);
QJsonObject item; QJsonObject item;
item["priority"] = priority; item["priority"] = priority;
if (priorityInfo.timeoutTime_ms > 0) if (priorityInfo.timeoutTime_ms > 0)
@ -371,9 +414,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// collect adjustment information // collect adjustment information
QJsonArray adjustmentArray; QJsonArray adjustmentArray;
for (const QString& adjustmentId : _hyperion->getAdjustmentIds()) for (const QString &adjustmentId : _hyperion->getAdjustmentIds())
{ {
const ColorAdjustment* colorAdjustment = _hyperion->getAdjustment(adjustmentId); const ColorAdjustment *colorAdjustment = _hyperion->getAdjustment(adjustmentId);
if (colorAdjustment == nullptr) if (colorAdjustment == nullptr)
{ {
Error(_log, "Incorrect color adjustment id: %s", QSTRING_CSTR(adjustmentId)); Error(_log, "Incorrect color adjustment id: %s", QSTRING_CSTR(adjustmentId));
@ -440,8 +483,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// collect effect info // collect effect info
QJsonArray effects; QJsonArray effects;
const std::list<EffectDefinition>& effectsDefinitions = _hyperion->getEffects(); const std::list<EffectDefinition> &effectsDefinitions = _hyperion->getEffects();
for (const EffectDefinition& effectDefinition : effectsDefinitions) for (const EffectDefinition &effectDefinition : effectsDefinitions)
{ {
QJsonObject effect; QJsonObject effect;
effect["name"] = effectDefinition.name; effect["name"] = effectDefinition.name;
@ -467,11 +510,18 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
QJsonObject grabbers; QJsonObject grabbers;
QJsonArray availableGrabbers; QJsonArray availableGrabbers;
#if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11) || defined(ENABLE_XCB) || defined(ENABLE_QT) #if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_MF) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11) || defined(ENABLE_XCB) || defined(ENABLE_QT)
if (GrabberWrapper::getInstance() != nullptr) if ( GrabberWrapper::getInstance() != nullptr )
{ {
grabbers["active"] = GrabberWrapper::getInstance()->getActive(); QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex());
QJsonArray activeGrabberNames;
for (auto grabberName : activeGrabbers)
{
activeGrabberNames.append(grabberName);
}
grabbers["active"] = activeGrabberNames;
} }
// get available grabbers // get available grabbers
@ -480,55 +530,20 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
availableGrabbers.append(grabber); availableGrabbers.append(grabber);
} }
#endif
#if defined(ENABLE_V4L2)
QJsonArray availableV4L2devices;
for (const auto& devicePath : GrabberWrapper::getInstance()->getV4L2devices())
{
QJsonObject device;
device["device"] = devicePath;
device["name"] = GrabberWrapper::getInstance()->getV4L2deviceName(devicePath);
QJsonArray availableInputs;
QMultiMap<QString, int> inputs = GrabberWrapper::getInstance()->getV4L2deviceInputs(devicePath);
for (auto input = inputs.begin(); input != inputs.end(); input++)
{
QJsonObject availableInput;
availableInput["inputName"] = input.key();
availableInput["inputIndex"] = input.value();
availableInputs.append(availableInput);
}
device.insert("inputs", availableInputs);
QJsonArray availableResolutions;
QStringList resolutions = GrabberWrapper::getInstance()->getResolutions(devicePath);
for (auto resolution : resolutions)
{
availableResolutions.append(resolution);
}
device.insert("resolutions", availableResolutions);
QJsonArray availableFramerates;
QStringList framerates = GrabberWrapper::getInstance()->getFramerates(devicePath);
for (auto framerate : framerates)
{
availableFramerates.append(framerate);
}
device.insert("framerates", availableFramerates);
availableV4L2devices.append(device);
}
grabbers["v4l2_properties"] = availableV4L2devices;
#endif #endif
grabbers["available"] = availableGrabbers; grabbers["available"] = availableGrabbers;
info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode())); info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode()));
info["grabbers"] = grabbers; info["grabbers"] = grabbers;
QJsonObject cecInfo;
#if defined(ENABLE_CEC)
cecInfo["enabled"] = true;
#else
cecInfo["enabled"] = false;
#endif
info["cec"] = cecInfo;
// get available components // get available components
QJsonArray component; QJsonArray component;
std::map<hyperion::Components, bool> components = _hyperion->getComponentRegister().getRegister(); std::map<hyperion::Components, bool> components = _hyperion->getComponentRegister().getRegister();
@ -547,7 +562,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// add sessions // add sessions
QJsonArray sessions; QJsonArray sessions;
#ifdef ENABLE_AVAHI #ifdef ENABLE_AVAHI
for (auto session : BonjourBrowserWrapper::getInstance()->getAllServices()) for (auto session: BonjourBrowserWrapper::getInstance()->getAllServices())
{ {
if (session.port < 0) if (session.port < 0)
continue; continue;
@ -564,7 +579,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
#endif #endif
// add instance info // add instance info
QJsonArray instanceInfo; QJsonArray instanceInfo;
for (const auto& entry : API::getAllInstanceData()) for (const auto &entry : API::getAllInstanceData())
{ {
QJsonObject obj; QJsonObject obj;
obj.insert("friendly_name", entry["friendly_name"].toString()); obj.insert("friendly_name", entry["friendly_name"].toString());
@ -586,7 +601,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// TRANSFORM INFORMATION (DEFAULT VALUES) // TRANSFORM INFORMATION (DEFAULT VALUES)
QJsonArray transformArray; QJsonArray transformArray;
for (const QString& transformId : _hyperion->getAdjustmentIds()) for (const QString &transformId : _hyperion->getAdjustmentIds())
{ {
QJsonObject transform; QJsonObject transform;
QJsonArray blacklevel, whitelevel, gamma, threshold; QJsonArray blacklevel, whitelevel, gamma, threshold;
@ -617,7 +632,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// ACTIVE EFFECT INFO // ACTIVE EFFECT INFO
QJsonArray activeEffects; QJsonArray activeEffects;
for (const ActiveEffectDefinition& activeEffectDefinition : _hyperion->getActiveEffects()) for (const ActiveEffectDefinition &activeEffectDefinition : _hyperion->getActiveEffects())
{ {
if (activeEffectDefinition.priority != PriorityMuxer::LOWEST_PRIORITY - 1) if (activeEffectDefinition.priority != PriorityMuxer::LOWEST_PRIORITY - 1)
{ {
@ -634,7 +649,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// ACTIVE STATIC LED COLOR // ACTIVE STATIC LED COLOR
QJsonArray activeLedColors; QJsonArray activeLedColors;
const Hyperion::InputInfo& priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority()); const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority());
if (priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty()) if (priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty())
{ {
QJsonObject LEDcolor; QJsonObject LEDcolor;
@ -706,7 +721,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
} }
} }
void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleClearCommand(const QJsonObject &message, const QString &command, int tan)
{ {
emit forwardJsonMessage(message); emit forwardJsonMessage(message);
int priority = message["priority"].toInt(); int priority = message["priority"].toInt();
@ -720,7 +735,7 @@ void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& comm
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleClearallCommand(const QJsonObject &message, const QString &command, int tan)
{ {
emit forwardJsonMessage(message); emit forwardJsonMessage(message);
QString replyMsg; QString replyMsg;
@ -728,12 +743,12 @@ void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& c
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleAdjustmentCommand(const QJsonObject &message, const QString &command, int tan)
{ {
const QJsonObject& adjustment = message["adjustment"].toObject(); const QJsonObject &adjustment = message["adjustment"].toObject();
const QString adjustmentId = adjustment["id"].toString(_hyperion->getAdjustmentIds().first()); const QString adjustmentId = adjustment["id"].toString(_hyperion->getAdjustmentIds().first());
ColorAdjustment* colorAdjustment = _hyperion->getAdjustment(adjustmentId); ColorAdjustment *colorAdjustment = _hyperion->getAdjustment(adjustmentId);
if (colorAdjustment == nullptr) if (colorAdjustment == nullptr)
{ {
Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str()); Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str());
@ -742,39 +757,39 @@ void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString&
if (adjustment.contains("red")) if (adjustment.contains("red"))
{ {
const QJsonArray& values = adjustment["red"].toArray(); const QJsonArray &values = adjustment["red"].toArray();
colorAdjustment->_rgbRedAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); colorAdjustment->_rgbRedAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
} }
if (adjustment.contains("green")) if (adjustment.contains("green"))
{ {
const QJsonArray& values = adjustment["green"].toArray(); const QJsonArray &values = adjustment["green"].toArray();
colorAdjustment->_rgbGreenAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); colorAdjustment->_rgbGreenAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
} }
if (adjustment.contains("blue")) if (adjustment.contains("blue"))
{ {
const QJsonArray& values = adjustment["blue"].toArray(); const QJsonArray &values = adjustment["blue"].toArray();
colorAdjustment->_rgbBlueAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); colorAdjustment->_rgbBlueAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
} }
if (adjustment.contains("cyan")) if (adjustment.contains("cyan"))
{ {
const QJsonArray& values = adjustment["cyan"].toArray(); const QJsonArray &values = adjustment["cyan"].toArray();
colorAdjustment->_rgbCyanAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); colorAdjustment->_rgbCyanAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
} }
if (adjustment.contains("magenta")) if (adjustment.contains("magenta"))
{ {
const QJsonArray& values = adjustment["magenta"].toArray(); const QJsonArray &values = adjustment["magenta"].toArray();
colorAdjustment->_rgbMagentaAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); colorAdjustment->_rgbMagentaAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
} }
if (adjustment.contains("yellow")) if (adjustment.contains("yellow"))
{ {
const QJsonArray& values = adjustment["yellow"].toArray(); const QJsonArray &values = adjustment["yellow"].toArray();
colorAdjustment->_rgbYellowAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); colorAdjustment->_rgbYellowAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
} }
if (adjustment.contains("white")) if (adjustment.contains("white"))
{ {
const QJsonArray& values = adjustment["white"].toArray(); const QJsonArray &values = adjustment["white"].toArray();
colorAdjustment->_rgbWhiteAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); colorAdjustment->_rgbWhiteAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
} }
@ -814,7 +829,7 @@ void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString&
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleSourceSelectCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleSourceSelectCommand(const QJsonObject &message, const QString &command, int tan)
{ {
if (message.contains("auto")) if (message.contains("auto"))
{ {
@ -832,7 +847,7 @@ void JsonAPI::handleSourceSelectCommand(const QJsonObject& message, const QStrin
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleConfigCommand(const QJsonObject &message, const QString &command, int tan)
{ {
QString subcommand = message["subcommand"].toString(""); QString subcommand = message["subcommand"].toString("");
QString full_command = command + "-" + subcommand; QString full_command = command + "-" + subcommand;
@ -876,14 +891,14 @@ void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& com
} }
} }
void JsonAPI::handleConfigSetCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleConfigSetCommand(const QJsonObject &message, const QString &command, int tan)
{ {
if (message.contains("config")) if (message.contains("config"))
{ {
QJsonObject config = message["config"].toObject(); QJsonObject config = message["config"].toObject();
if (API::isHyperionEnabled()) if (API::isHyperionEnabled())
{ {
if (API::saveSettings(config)) if ( API::saveSettings(config) )
{ {
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
@ -897,7 +912,7 @@ void JsonAPI::handleConfigSetCommand(const QJsonObject& message, const QString&
} }
} }
void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleSchemaGetCommand(const QJsonObject &message, const QString &command, int tan)
{ {
// create result // create result
QJsonObject schemaJson, alldevices, properties; QJsonObject schemaJson, alldevices, properties;
@ -912,7 +927,7 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString&
{ {
schemaJson = QJsonFactory::readSchema(schemaFile); schemaJson = QJsonFactory::readSchema(schemaFile);
} }
catch (const std::runtime_error& error) catch (const std::runtime_error &error)
{ {
throw std::runtime_error(error.what()); throw std::runtime_error(error.what());
} }
@ -949,9 +964,9 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString&
sendSuccessDataReply(QJsonDocument(schemaJson), command, tan); sendSuccessDataReply(QJsonDocument(schemaJson), command, tan);
} }
void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleComponentStateCommand(const QJsonObject &message, const QString &command, int tan)
{ {
const QJsonObject& componentState = message["componentstate"].toObject(); const QJsonObject &componentState = message["componentstate"].toObject();
QString comp = componentState["component"].toString("invalid"); QString comp = componentState["component"].toString("invalid");
bool compState = componentState["state"].toBool(true); bool compState = componentState["state"].toBool(true);
QString replyMsg; QString replyMsg;
@ -964,7 +979,7 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QStr
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString &command, int tan)
{ {
// create result // create result
QString subcommand = message["subcommand"].toString(""); QString subcommand = message["subcommand"].toString("");
@ -978,7 +993,7 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString&
_streaming_leds_reply["command"] = command + "-ledstream-update"; _streaming_leds_reply["command"] = command + "-ledstream-update";
_streaming_leds_reply["tan"] = tan; _streaming_leds_reply["tan"] = tan;
connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector<ColorRgb>& ledValues) { connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector<ColorRgb> &ledValues) {
_currentLedValues = ledValues; _currentLedValues = ledValues;
// necessary because Qt::UniqueConnection for lambdas does not work until 5.9 // necessary because Qt::UniqueConnection for lambdas does not work until 5.9
@ -1023,7 +1038,7 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString&
sendSuccessReply(command + "-" + subcommand, tan); sendSuccessReply(command + "-" + subcommand, tan);
} }
void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleLoggingCommand(const QJsonObject &message, const QString &command, int tan)
{ {
// create result // create result
QString subcommand = message["subcommand"].toString(""); QString subcommand = message["subcommand"].toString("");
@ -1065,25 +1080,25 @@ void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString& co
} }
} }
void JsonAPI::handleProcessingCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleProcessingCommand(const QJsonObject &message, const QString &command, int tan)
{ {
API::setLedMappingType(ImageProcessor::mappingTypeToInt(message["mappingType"].toString("multicolor_mean"))); API::setLedMappingType(ImageProcessor::mappingTypeToInt(message["mappingType"].toString("multicolor_mean")));
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleVideoModeCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleVideoModeCommand(const QJsonObject &message, const QString &command, int tan)
{ {
API::setVideoMode(parse3DMode(message["videoMode"].toString("2D"))); API::setVideoMode(parse3DMode(message["videoMode"].toString("2D")));
sendSuccessReply(command, tan); sendSuccessReply(command, tan);
} }
void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &command, int tan)
{ {
const QString& subc = message["subcommand"].toString().trimmed(); const QString &subc = message["subcommand"].toString().trimmed();
const QString& id = message["id"].toString().trimmed(); const QString &id = message["id"].toString().trimmed();
const QString& password = message["password"].toString().trimmed(); const QString &password = message["password"].toString().trimmed();
const QString& newPassword = message["newPassword"].toString().trimmed(); const QString &newPassword = message["newPassword"].toString().trimmed();
const QString& comment = message["comment"].toString().trimmed(); const QString &comment = message["comment"].toString().trimmed();
// catch test if auth is required // catch test if auth is required
if (subc == "tokenRequired") if (subc == "tokenRequired")
@ -1194,8 +1209,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (subc == "requestToken") if (subc == "requestToken")
{ {
// use id/comment // use id/comment
const QString& comment = message["comment"].toString().trimmed(); const QString &comment = message["comment"].toString().trimmed();
const bool& acc = message["accept"].toBool(true); const bool &acc = message["accept"].toBool(true);
if (acc) if (acc)
API::setNewTokenRequest(comment, id, tan); API::setNewTokenRequest(comment, id, tan);
else else
@ -1211,7 +1226,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (API::getPendingTokenRequests(vec)) if (API::getPendingTokenRequests(vec))
{ {
QJsonArray arr; QJsonArray arr;
for (const auto& entry : vec) for (const auto &entry : vec)
{ {
QJsonObject obj; QJsonObject obj;
obj["comment"] = entry.comment; obj["comment"] = entry.comment;
@ -1233,7 +1248,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (subc == "answerRequest") if (subc == "answerRequest")
{ {
// use id // use id
const bool& accept = message["accept"].toBool(false); const bool &accept = message["accept"].toBool(false);
if (!API::handlePendingTokenRequest(id, accept)) if (!API::handlePendingTokenRequest(id, accept))
sendErrorReply("No Authorization", command + "-" + subc, tan); sendErrorReply("No Authorization", command + "-" + subc, tan);
return; return;
@ -1246,7 +1261,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (API::getTokenList(defVect)) if (API::getTokenList(defVect))
{ {
QJsonArray tArr; QJsonArray tArr;
for (const auto& entry : defVect) for (const auto &entry : defVect)
{ {
QJsonObject subO; QJsonObject subO;
subO["comment"] = entry.comment; subO["comment"] = entry.comment;
@ -1265,7 +1280,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
// login // login
if (subc == "login") if (subc == "login")
{ {
const QString& token = message["token"].toString().trimmed(); const QString &token = message["token"].toString().trimmed();
// catch token // catch token
if (!token.isEmpty()) if (!token.isEmpty())
@ -1313,11 +1328,11 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
} }
} }
void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &command, int tan)
{ {
const QString& subc = message["subcommand"].toString(); const QString &subc = message["subcommand"].toString();
const quint8& inst = message["instance"].toInt(); const quint8 &inst = message["instance"].toInt();
const QString& name = message["name"].toString(); const QString &name = message["name"].toString();
if (subc == "switchTo") if (subc == "switchTo")
{ {
@ -1334,7 +1349,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c
if (subc == "startInstance") if (subc == "startInstance")
{ {
connect(this, &API::onStartInstanceResponse, [=](const int& tan) { sendSuccessReply(command + "-" + subc, tan); }); connect(this, &API::onStartInstanceResponse, [=] (const int &tan) { sendSuccessReply(command + "-" + subc, tan); });
if (!API::startInstance(inst, tan)) if (!API::startInstance(inst, tan))
sendErrorReply("Can't start Hyperion instance index " + QString::number(inst), command + "-" + subc, tan); sendErrorReply("Can't start Hyperion instance index " + QString::number(inst), command + "-" + subc, tan);
@ -1384,12 +1399,12 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c
} }
} }
void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString& command, int tan) void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &command, int tan)
{ {
Debug(_log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData()); Debug(_log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData() );
const QString& subc = message["subcommand"].toString().trimmed(); const QString &subc = message["subcommand"].toString().trimmed();
const QString& devType = message["ledDeviceType"].toString().trimmed(); const QString &devType = message["ledDeviceType"].toString().trimmed();
QString full_command = command + "-" + subc; QString full_command = command + "-" + subc;
@ -1407,27 +1422,27 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString&
if (subc == "discover") if (subc == "discover")
{ {
ledDevice = LedDeviceFactory::construct(config); ledDevice = LedDeviceFactory::construct(config);
const QJsonObject& params = message["params"].toObject(); const QJsonObject &params = message["params"].toObject();
const QJsonObject devicesDiscovered = ledDevice->discover(params); const QJsonObject devicesDiscovered = ledDevice->discover(params);
Debug(_log, "response: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); Debug(_log, "response: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
sendSuccessDataReply(QJsonDocument(devicesDiscovered), full_command, tan); sendSuccessDataReply(QJsonDocument(devicesDiscovered), full_command, tan);
} }
else if (subc == "getProperties") else if (subc == "getProperties")
{ {
ledDevice = LedDeviceFactory::construct(config); ledDevice = LedDeviceFactory::construct(config);
const QJsonObject& params = message["params"].toObject(); const QJsonObject &params = message["params"].toObject();
const QJsonObject deviceProperties = ledDevice->getProperties(params); const QJsonObject deviceProperties = ledDevice->getProperties(params);
Debug(_log, "response: [%s]", QString(QJsonDocument(deviceProperties).toJson(QJsonDocument::Compact)).toUtf8().constData()); Debug(_log, "response: [%s]", QString(QJsonDocument(deviceProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
sendSuccessDataReply(QJsonDocument(deviceProperties), full_command, tan); sendSuccessDataReply(QJsonDocument(deviceProperties), full_command, tan);
} }
else if (subc == "identify") else if (subc == "identify")
{ {
ledDevice = LedDeviceFactory::construct(config); ledDevice = LedDeviceFactory::construct(config);
const QJsonObject& params = message["params"].toObject(); const QJsonObject &params = message["params"].toObject();
ledDevice->identify(params); ledDevice->identify(params);
sendSuccessReply(full_command, tan); sendSuccessReply(full_command, tan);
@ -1441,12 +1456,152 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString&
} }
} }
void JsonAPI::handleNotImplemented(const QString& command, int tan) void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString& command, int tan)
{
DebugIf(verbose, _log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData());
const QString& subc = message["subcommand"].toString().trimmed();
const QString& sourceType = message["sourceType"].toString().trimmed();
QString full_command = command + "-" + subc;
// TODO: Validate that source type is a valid one
/* if ( ! valid type )
{
sendErrorReply("Unknown device", full_command, tan);
}
else
*/ {
if (subc == "discover")
{
QJsonObject inputSourcesDiscovered;
inputSourcesDiscovered.insert("sourceType", sourceType);
QJsonArray videoInputs;
#if defined(ENABLE_V4L2) || defined(ENABLE_MF)
if (sourceType == "video" )
{
#if defined(ENABLE_MF)
MFGrabber* grabber = new MFGrabber();
#elif defined(ENABLE_V4L2)
V4L2Grabber* grabber = new V4L2Grabber();
#endif
QJsonObject params;
videoInputs = grabber->discover(params);
delete grabber;
}
else
#endif
{
DebugIf(verbose, _log, "sourceType: [%s]", QSTRING_CSTR(sourceType));
if (sourceType == "screen")
{
QJsonObject params;
QJsonObject device;
#ifdef ENABLE_QT
QtGrabber* qtgrabber = new QtGrabber();
device = qtgrabber->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete qtgrabber;
#endif
#ifdef ENABLE_DX
DirectXGrabber* dxgrabber = new DirectXGrabber();
device = dxgrabber->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete dxgrabber;
#endif
#ifdef ENABLE_X11
X11Grabber* x11Grabber = new X11Grabber();
device = x11Grabber->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete x11Grabber;
#endif
#ifdef ENABLE_XCB
XcbGrabber* xcbGrabber = new XcbGrabber();
device = xcbGrabber->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete xcbGrabber;
#endif
#ifdef ENABLE_FB
FramebufferFrameGrabber* fbGrabber = new FramebufferFrameGrabber();
device = fbGrabber->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete fbGrabber;
#endif
#if defined(ENABLE_DISPMANX)
DispmanxFrameGrabber* dispmanx = new DispmanxFrameGrabber();
device = dispmanx->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete dispmanx;
#endif
#if defined(ENABLE_AMLOGIC)
AmlogicGrabber* amlGrabber = new AmlogicGrabber();
device = amlGrabber->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete amlGrabber;
#endif
#if defined(ENABLE_OSX)
OsxFrameGrabber* osxGrabber = new OsxFrameGrabber();
device = osxGrabber->discover(params);
if (!device.isEmpty() )
{
videoInputs.append(device);
}
delete osxGrabber;
#endif
}
}
inputSourcesDiscovered["video_sources"] = videoInputs;
DebugIf(verbose, _log, "response: [%s]", QString(QJsonDocument(inputSourcesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
sendSuccessDataReply(QJsonDocument(inputSourcesDiscovered), full_command, tan);
}
else
{
sendErrorReply("Unknown or missing subcommand", full_command, tan);
}
}
}
void JsonAPI::handleNotImplemented(const QString &command, int tan)
{ {
sendErrorReply("Command not implemented", command, tan); sendErrorReply("Command not implemented", command, tan);
} }
void JsonAPI::sendSuccessReply(const QString& command, int tan) void JsonAPI::sendSuccessReply(const QString &command, int tan)
{ {
// create reply // create reply
QJsonObject reply; QJsonObject reply;
@ -1458,7 +1613,7 @@ void JsonAPI::sendSuccessReply(const QString& command, int tan)
emit callbackMessage(reply); emit callbackMessage(reply);
} }
void JsonAPI::sendSuccessDataReply(const QJsonDocument& doc, const QString& command, int tan) void JsonAPI::sendSuccessDataReply(const QJsonDocument &doc, const QString &command, int tan)
{ {
QJsonObject reply; QJsonObject reply;
reply["success"] = true; reply["success"] = true;
@ -1472,7 +1627,7 @@ void JsonAPI::sendSuccessDataReply(const QJsonDocument& doc, const QString& comm
emit callbackMessage(reply); emit callbackMessage(reply);
} }
void JsonAPI::sendErrorReply(const QString& error, const QString& command, int tan) void JsonAPI::sendErrorReply(const QString &error, const QString &command, int tan)
{ {
// create reply // create reply
QJsonObject reply; QJsonObject reply;
@ -1485,12 +1640,12 @@ void JsonAPI::sendErrorReply(const QString& error, const QString& command, int t
emit callbackMessage(reply); emit callbackMessage(reply);
} }
void JsonAPI::streamLedcolorsUpdate(const std::vector<ColorRgb>& ledColors) void JsonAPI::streamLedcolorsUpdate(const std::vector<ColorRgb> &ledColors)
{ {
QJsonObject result; QJsonObject result;
QJsonArray leds; QJsonArray leds;
for (const auto& color : ledColors) for (const auto &color : ledColors)
{ {
leds << QJsonValue(color.red) << QJsonValue(color.green) << QJsonValue(color.blue); leds << QJsonValue(color.red) << QJsonValue(color.green) << QJsonValue(color.blue);
} }
@ -1502,9 +1657,9 @@ void JsonAPI::streamLedcolorsUpdate(const std::vector<ColorRgb>& ledColors)
emit callbackMessage(_streaming_leds_reply); emit callbackMessage(_streaming_leds_reply);
} }
void JsonAPI::setImage(const Image<ColorRgb>& image) void JsonAPI::setImage(const Image<ColorRgb> &image)
{ {
QImage jpgImage((const uint8_t*)image.memptr(), image.width(), image.height(), 3 * image.width(), QImage::Format_RGB888); QImage jpgImage((const uint8_t *)image.memptr(), image.width(), image.height(), 3 * image.width(), QImage::Format_RGB888);
QByteArray ba; QByteArray ba;
QBuffer buffer(&ba); QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
@ -1516,7 +1671,7 @@ void JsonAPI::setImage(const Image<ColorRgb>& image)
emit callbackMessage(_streaming_image_reply); emit callbackMessage(_streaming_image_reply);
} }
void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg) void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg)
{ {
QJsonObject result, message; QJsonObject result, message;
QJsonArray messageArray; QJsonArray messageArray;
@ -1524,7 +1679,7 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg)
if (!_streaming_logging_activated) if (!_streaming_logging_activated)
{ {
_streaming_logging_activated = true; _streaming_logging_activated = true;
const QList<Logger::T_LOG_MESSAGE>* logBuffer = LoggerManager::getInstance()->getLogMessageBuffer(); const QList<Logger::T_LOG_MESSAGE> *logBuffer = LoggerManager::getInstance()->getLogMessageBuffer();
for (int i = 0; i < logBuffer->length(); i++) for (int i = 0; i < logBuffer->length(); i++)
{ {
message["appName"] = logBuffer->at(i).appName; message["appName"] = logBuffer->at(i).appName;
@ -1560,7 +1715,7 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg)
emit callbackMessage(_streaming_logging_reply); emit callbackMessage(_streaming_logging_reply);
} }
void JsonAPI::newPendingTokenRequest(const QString& id, const QString& comment) void JsonAPI::newPendingTokenRequest(const QString &id, const QString &comment)
{ {
QJsonObject obj; QJsonObject obj;
obj["comment"] = comment; obj["comment"] = comment;
@ -1570,7 +1725,7 @@ void JsonAPI::newPendingTokenRequest(const QString& id, const QString& comment)
sendSuccessDataReply(QJsonDocument(obj), "authorize-tokenRequest", 1); sendSuccessDataReply(QJsonDocument(obj), "authorize-tokenRequest", 1);
} }
void JsonAPI::handleTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan) void JsonAPI::handleTokenResponse(bool success, const QString &token, const QString &comment, const QString &id, const int &tan)
{ {
const QString cmd = "authorize-requestToken"; const QString cmd = "authorize-requestToken";
QJsonObject result; QJsonObject result;
@ -1584,7 +1739,7 @@ void JsonAPI::handleTokenResponse(bool success, const QString& token, const QStr
sendErrorReply("Token request timeout or denied", cmd, tan); sendErrorReply("Token request timeout or denied", cmd, tan);
} }
void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name) void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString &name)
{ {
switch (state) switch (state)
{ {

View File

@ -19,6 +19,7 @@ Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &scr
, _hyperion(hyperion) , _hyperion(hyperion)
, _priority(priority) , _priority(priority)
, _timeout(timeout) , _timeout(timeout)
, _isEndless(timeout <= ENDLESS)
, _script(script) , _script(script)
, _name(name) , _name(name)
, _args(args) , _args(args)
@ -51,7 +52,7 @@ Effect::~Effect()
bool Effect::isInterruptionRequested() bool Effect::isInterruptionRequested()
{ {
return _interupt || getRemaining() < ENDLESS; return _interupt || (!_isEndless && getRemaining() <= 0);
} }
int Effect::getRemaining() const int Effect::getRemaining() const
@ -59,12 +60,11 @@ int Effect::getRemaining() const
// determine the timeout // determine the timeout
int timeout = _timeout; int timeout = _timeout;
if (timeout > 0) if (timeout >= 0)
{ {
timeout = static_cast<int>( _endTime - QDateTime::currentMSecsSinceEpoch()); timeout = static_cast<int>( _endTime - QDateTime::currentMSecsSinceEpoch());
return timeout;
} }
return ENDLESS; return timeout;
} }
void Effect::setModuleParameters() void Effect::setModuleParameters()

View File

@ -12,24 +12,24 @@ endif (ENABLE_FB)
if (ENABLE_OSX) if (ENABLE_OSX)
add_subdirectory(osx) add_subdirectory(osx)
endif() endif(ENABLE_OSX)
if (ENABLE_V4L2) if (ENABLE_V4L2 OR ENABLE_MF)
add_subdirectory(v4l2) add_subdirectory(video)
endif (ENABLE_V4L2) endif ()
if (ENABLE_X11) if (ENABLE_X11)
add_subdirectory(x11) add_subdirectory(x11)
endif() endif(ENABLE_X11)
if (ENABLE_XCB) if (ENABLE_XCB)
add_subdirectory(xcb) add_subdirectory(xcb)
endif() endif(ENABLE_XCB)
if (ENABLE_QT) if (ENABLE_QT)
add_subdirectory(qt) add_subdirectory(qt)
endif() endif(ENABLE_QT)
if (ENABLE_DX) if (ENABLE_DX)
add_subdirectory(directx) add_subdirectory(directx)
endif() endif(ENABLE_DX)

View File

@ -2,7 +2,6 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <QFile>
// Linux includes // Linux includes
#include <errno.h> #include <errno.h>
@ -12,62 +11,92 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
// qt
#include <QFile>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSize>
// Local includes // Local includes
#include <utils/Logger.h> #include <utils/Logger.h>
#include <grabber/AmlogicGrabber.h> #include <grabber/AmlogicGrabber.h>
#include "Amvideocap.h" #include "Amvideocap.h"
#define VIDEO_DEVICE "/dev/amvideo" // Constants
#define CAPTURE_DEVICE "/dev/amvideocap0" namespace {
const bool verbose = false;
AmlogicGrabber::AmlogicGrabber(unsigned width, unsigned height) const char DEFAULT_FB_DEVICE[] = "/dev/fb0";
: Grabber("AMLOGICGRABBER", qMax(160u, width), qMax(160u, height)) // Minimum required width or height is 160 const char DEFAULT_VIDEO_DEVICE[] = "/dev/amvideo";
const char DEFAULT_CAPTURE_DEVICE[] = "/dev/amvideocap0";
const int AMVIDEOCAP_WAIT_MAX_MS = 50;
} //End of constants
AmlogicGrabber::AmlogicGrabber()
: Grabber("AMLOGICGRABBER") // Minimum required width or height is 160
, _captureDev(-1) , _captureDev(-1)
, _videoDev(-1) , _videoDev(-1)
, _lastError(0) , _lastError(0)
, _fbGrabber("/dev/fb0",width,height) , _fbGrabber(DEFAULT_FB_DEVICE)
, _grabbingModeNotification(0) , _grabbingModeNotification(0)
{ {
Debug(_log, "constructed(%d x %d), grabber device: %s",_width,_height, CAPTURE_DEVICE);
_image_bgr.resize(_width, _height);
_bytesToRead = _image_bgr.size();
_image_ptr = _image_bgr.memptr(); _image_ptr = _image_bgr.memptr();
_useImageResampler = true;
} }
AmlogicGrabber::~AmlogicGrabber() AmlogicGrabber::~AmlogicGrabber()
{ {
closeDev(_captureDev); closeDevice(_captureDev);
closeDev(_videoDev); closeDevice(_videoDev);
} }
bool AmlogicGrabber::openDev(int &fd, const char* dev) bool AmlogicGrabber::setupScreen()
{
bool rc (false);
QSize screenSize = _fbGrabber.getScreenSize(DEFAULT_FB_DEVICE);
if ( !screenSize.isEmpty() )
{
if (setWidthHeight(screenSize.width(), screenSize.height()))
{
rc = _fbGrabber.setupScreen();
}
}
return rc;
}
bool AmlogicGrabber::openDevice(int &fd, const char* dev)
{ {
if (fd<0) if (fd<0)
{ {
fd = open(dev, O_RDWR); fd = ::open(dev, O_RDWR);
} }
return fd >= 0; return fd >= 0;
} }
void AmlogicGrabber::closeDev(int &fd) void AmlogicGrabber::closeDevice(int &fd)
{ {
if (fd >= 0) if (fd >= 0)
{ {
close(fd); ::close(fd);
fd = -1; fd = -1;
} }
} }
bool AmlogicGrabber::isVideoPlaying() bool AmlogicGrabber::isVideoPlaying()
{ {
if(!QFile::exists(VIDEO_DEVICE)) return false; bool rc = false;
if(QFile::exists(DEFAULT_VIDEO_DEVICE))
{
int videoDisabled = 1; int videoDisabled = 1;
if (!openDev(_videoDev, VIDEO_DEVICE)) if (!openDevice(_videoDev, DEFAULT_VIDEO_DEVICE))
{ {
Error(_log, "Failed to open video device(%s): %d - %s", VIDEO_DEVICE, errno, strerror(errno)); Error(_log, "Failed to open video device(%s): %d - %s", DEFAULT_VIDEO_DEVICE, errno, strerror(errno));
return false;
} }
else else
{ {
@ -75,93 +104,230 @@ bool AmlogicGrabber::isVideoPlaying()
if(ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0) if(ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0)
{ {
Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno)); Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno));
closeDev(_videoDev); closeDevice(_videoDev);
return false; }
else
{
if ( videoDisabled == 0 )
{
rc = true;
}
} }
} }
return videoDisabled == 0; }
return rc;
} }
int AmlogicGrabber::grabFrame(Image<ColorRgb> & image) int AmlogicGrabber::grabFrame(Image<ColorRgb> & image)
{ {
if (!_enabled) return 0; int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
// Make sure video is playing, else there is nothing to grab // Make sure video is playing, else there is nothing to grab
if (isVideoPlaying()) if (isVideoPlaying())
{ {
if (_grabbingModeNotification!=1) if (_grabbingModeNotification!=1)
{ {
Info(_log, "VPU mode"); Info(_log, "Switch to VPU capture mode");
_grabbingModeNotification = 1; _grabbingModeNotification = 1;
_lastError = 0; _lastError = 0;
} }
if (grabFrame_amvideocap(image) < 0) if (grabFrame_amvideocap(image) < 0) {
closeDev(_captureDev); closeDevice(_captureDev);
rc = -1;
}
} }
else else
{ {
if (_grabbingModeNotification!=2) if (_grabbingModeNotification!=2)
{ {
Info( _log, "FB mode"); Info( _log, "Switch to Framebuffer capture mode");
_grabbingModeNotification = 2; _grabbingModeNotification = 2;
_lastError = 0; _lastError = 0;
} }
_fbGrabber.grabFrame(image); rc = _fbGrabber.grabFrame(image);
usleep(50 * 1000); //usleep(50 * 1000);
} }
}
return 0; return rc;
} }
int AmlogicGrabber::grabFrame_amvideocap(Image<ColorRgb> & image) int AmlogicGrabber::grabFrame_amvideocap(Image<ColorRgb> & image)
{ {
int rc = 0;
// If the device is not open, attempt to open it // If the device is not open, attempt to open it
if (_captureDev < 0) if (_captureDev < 0)
{ {
if (! openDev(_captureDev, 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; _lastError = 1;
return -1; rc = -1;
return rc;
}
} }
long r1 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, _width); long r1 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, _width);
long r2 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT, _height); long r2 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT, _height);
long r3 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_AT_FLAGS, CAP_FLAG_AT_END); 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, 500); 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; _lastError = 2;
return -1; rc = -1;
}
} }
else
{
int linelen = ((_width + 31) & ~31) * 3;
size_t _bytesToRead = linelen * _height;
// Read the snapshot into the memory // Read the snapshot into the memory
ssize_t bytesRead = pread(_captureDev, _image_ptr, _bytesToRead, 0); ssize_t bytesRead = pread(_captureDev, _image_ptr, _bytesToRead, 0);
if (bytesRead < 0) if (bytesRead < 0)
{
int state;
ioctl(_captureDev, AMVIDEOCAP_IOR_GET_STATE, &state);
if (state == AMVIDEOCAP_STATE_ON_CAPTURE)
{
DebugIf(_lastError != 5, _log,"Video playback has been paused");
_lastError = 5;
}
else
{ {
ErrorIf(_lastError != 3, _log,"Read of device failed: %d - %s", errno, strerror(errno)); ErrorIf(_lastError != 3, _log,"Read of device failed: %d - %s", errno, strerror(errno));
_lastError = 3; _lastError = 3;
return -1;
} }
else if (_bytesToRead != bytesRead) rc = -1;
}
else
{
if (static_cast<ssize_t>(_bytesToRead) != bytesRead)
{ {
// Read of snapshot failed // Read of snapshot failed
ErrorIf(_lastError != 4, _log,"Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", _bytesToRead, bytesRead); ErrorIf(_lastError != 4, _log,"Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", _bytesToRead, bytesRead);
_lastError = 4; _lastError = 4;
return -1; rc = -1;
}
else {
_imageResampler.processImage(static_cast<uint8_t*>(_image_ptr),
_width,
_height,
linelen,
PixelFormat::BGR24, image);
_lastError = 0;
rc = 0;
}
}
}
return rc;
}
QJsonObject AmlogicGrabber::discover(const QJsonObject& params)
{
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject inputsDiscovered;
if(QFile::exists(DEFAULT_VIDEO_DEVICE) && QFile::exists(DEFAULT_CAPTURE_DEVICE) )
{
QJsonArray video_inputs;
QSize screenSize = _fbGrabber.getScreenSize();
if ( !screenSize.isEmpty() )
{
int fbIdx = _fbGrabber.getPath().rightRef(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, 40, 50, 60 };
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);
} }
_useImageResampler = true; if (!video_inputs.isEmpty())
_imageResampler.processImage((const uint8_t*)_image_ptr, _width, _height, (_width << 1) + _width, PixelFormat::BGR24, image); {
_lastError = 0; inputsDiscovered["device"] = "amlogic";
inputsDiscovered["device_name"] = "AmLogic";
inputsDiscovered["type"] = "screen";
inputsDiscovered["video_inputs"] = video_inputs;
}
}
return 0; if (inputsDiscovered.isEmpty())
{
DebugIf(verbose, _log, "No displays found to capture from!");
}
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
}
void AmlogicGrabber::setVideoMode(VideoMode mode)
{
Grabber::setVideoMode(mode);
_fbGrabber.setVideoMode(mode);
}
bool AmlogicGrabber::setPixelDecimation(int pixelDecimation)
{
return ( Grabber::setPixelDecimation( pixelDecimation) &&
_fbGrabber.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);
}
bool AmlogicGrabber::setWidthHeight(int width, int height)
{
bool rc (false);
if ( Grabber::setWidthHeight(width, height) )
{
_image_bgr.resize(static_cast<unsigned>(width), static_cast<unsigned>(height));
_width = width;
_height = height;
_bytesToRead = _image_bgr.size();
_image_ptr = _image_bgr.memptr();
rc = _fbGrabber.setWidthHeight(width, height);
}
return rc;
}
bool AmlogicGrabber::setFramerate(int fps)
{
return (Grabber::setFramerate(fps) &&
_fbGrabber.setFramerate(fps));
} }

View File

@ -1,9 +1,11 @@
#include <grabber/AmlogicWrapper.h> #include <grabber/AmlogicWrapper.h>
AmlogicWrapper::AmlogicWrapper(unsigned grabWidth, unsigned grabHeight) AmlogicWrapper::AmlogicWrapper(int pixelDecimation, int updateRate_Hz)
: GrabberWrapper("AmLogic", &_grabber, grabWidth, grabHeight) : GrabberWrapper("Amlogic", &_grabber, updateRate_Hz)
, _grabber(grabWidth, grabHeight) , _grabber()
{} {
_grabber.setPixelDecimation(pixelDecimation);
}
void AmlogicWrapper::action() void AmlogicWrapper::action()
{ {

View File

@ -11,11 +11,35 @@
#define CAP_FLAG_AT_TIME_WINDOW 1 #define CAP_FLAG_AT_TIME_WINDOW 1
#define CAP_FLAG_AT_END 2 #define CAP_FLAG_AT_END 2
// #define AMVIDEOCAP_IOW_SET_WANTFRAME_FORMAT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x01, int) #define AMVIDEOCAP_IOW_SET_WANTFRAME_FORMAT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x01, int)
#define AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH _IOW(AMVIDEOCAP_IOC_MAGIC, 0x02, int) #define AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH _IOW(AMVIDEOCAP_IOC_MAGIC, 0x02, int)
#define AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x03, int) #define AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x03, int)
#define AMVIDEOCAP_IOW_SET_WANTFRAME_TIMESTAMP_MS _IOW(AMVIDEOCAP_IOC_MAGIC, 0x04, unsigned long long)
#define AMVIDEOCAP_IOW_SET_WANTFRAME_WAIT_MAX_MS _IOW(AMVIDEOCAP_IOC_MAGIC, 0x05, unsigned long long) #define AMVIDEOCAP_IOW_SET_WANTFRAME_WAIT_MAX_MS _IOW(AMVIDEOCAP_IOC_MAGIC, 0x05, unsigned long long)
#define AMVIDEOCAP_IOW_SET_WANTFRAME_AT_FLAGS _IOW(AMVIDEOCAP_IOC_MAGIC, 0x06, int) #define AMVIDEOCAP_IOW_SET_WANTFRAME_AT_FLAGS _IOW(AMVIDEOCAP_IOC_MAGIC, 0x06, int)
#define AMVIDEOCAP_IOR_GET_FRAME_FORMAT _IOR(AMVIDEOCAP_IOC_MAGIC, 0x10, int)
#define AMVIDEOCAP_IOR_GET_FRAME_WIDTH _IOR(AMVIDEOCAP_IOC_MAGIC, 0x11, int)
#define AMVIDEOCAP_IOR_GET_FRAME_HEIGHT _IOR(AMVIDEOCAP_IOC_MAGIC, 0x12, int)
#define AMVIDEOCAP_IOR_GET_FRAME_TIMESTAMP_MS _IOR(AMVIDEOCAP_IOC_MAGIC, 0x13, int)
#define AMVIDEOCAP_IOR_GET_SRCFRAME_FORMAT _IOR(AMVIDEOCAP_IOC_MAGIC, 0x20, int)
#define AMVIDEOCAP_IOR_GET_SRCFRAME_WIDTH _IOR(AMVIDEOCAP_IOC_MAGIC, 0x21, int)
#define AMVIDEOCAP_IOR_GET_SRCFRAME_HEIGHT _IOR(AMVIDEOCAP_IOC_MAGIC, 0x22, int)
#define AMVIDEOCAP_IOR_GET_STATE _IOR(AMVIDEOCAP_IOC_MAGIC, 0x31, int)
#define AMVIDEOCAP_IOW_SET_START_CAPTURE _IOW(AMVIDEOCAP_IOC_MAGIC, 0x32, int)
#define AMVIDEOCAP_IOW_SET_CANCEL_CAPTURE _IOW(AMVIDEOCAP_IOC_MAGIC, 0x33, int)
#define _A_M 'S' #define _A_M 'S'
#define AMSTREAM_IOC_GET_VIDEO_DISABLE _IOR((_A_M), 0x48, int) #define AMSTREAM_IOC_GET_VIDEO_DISABLE _IOR((_A_M), 0x48, int)
#define AMVIDEOCAP_IOC_MAGIC 'V'
#define AMVIDEOCAP_IOW_SET_START_CAPTURE _IOW(AMVIDEOCAP_IOC_MAGIC, 0x32, int)
enum amvideocap_state{
AMVIDEOCAP_STATE_INIT=0,
AMVIDEOCAP_STATE_ON_CAPTURE=200,
AMVIDEOCAP_STATE_FINISHED_CAPTURE=300,
AMVIDEOCAP_STATE_ERROR=0xffff,
};

View File

@ -4,9 +4,13 @@
#pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3d9.lib")
#pragma comment(lib,"d3dx9.lib") #pragma comment(lib,"d3dx9.lib")
DirectXGrabber::DirectXGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display) // Constants
: Grabber("DXGRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom) namespace {
, _pixelDecimation(pixelDecimation) const bool verbose = true;
} //End of constants
DirectXGrabber::DirectXGrabber(int display, int cropLeft, int cropRight, int cropTop, int cropBottom)
: Grabber("DXGRABBER", cropLeft, cropRight, cropTop, cropBottom)
, _display(unsigned(display)) , _display(unsigned(display))
, _displayWidth(0) , _displayWidth(0)
, _displayHeight(0) , _displayHeight(0)
@ -15,8 +19,6 @@ DirectXGrabber::DirectXGrabber(int cropLeft, int cropRight, int cropTop, int cro
, _device(nullptr) , _device(nullptr)
, _surface(nullptr) , _surface(nullptr)
{ {
// init
setupDisplay();
} }
DirectXGrabber::~DirectXGrabber() DirectXGrabber::~DirectXGrabber()
@ -140,15 +142,24 @@ bool DirectXGrabber::setupDisplay()
int DirectXGrabber::grabFrame(Image<ColorRgb> & image) int DirectXGrabber::grabFrame(Image<ColorRgb> & image)
{ {
if (!_enabled) if (!_isEnabled)
{
qDebug() << "AUS";
return 0; return 0;
}
if (_device == nullptr)
{
// reinit, this will disable capture on failure
bool result = setupDisplay();
setEnabled(result);
return -1;
}
if (FAILED(_device->GetFrontBufferData(0, _surface))) if (FAILED(_device->GetFrontBufferData(0, _surface)))
{ {
// reinit, this will disable capture on failure
Error(_log, "Unable to get Buffer Surface Data"); Error(_log, "Unable to get Buffer Surface Data");
setEnabled(setupDisplay()); return 0;
return -1;
} }
D3DXLoadSurfaceFromSurface(_surfaceDest, nullptr, nullptr, _surface, nullptr, _srcRect, D3DX_DEFAULT, 0); D3DXLoadSurfaceFromSurface(_surfaceDest, nullptr, nullptr, _surface, nullptr, _srcRect, D3DX_DEFAULT, 0);
@ -181,22 +192,91 @@ void DirectXGrabber::setVideoMode(VideoMode mode)
setupDisplay(); setupDisplay();
} }
void DirectXGrabber::setPixelDecimation(int pixelDecimation) bool DirectXGrabber::setPixelDecimation(int pixelDecimation)
{ {
_pixelDecimation = pixelDecimation; if(Grabber::setPixelDecimation(pixelDecimation))
return setupDisplay();
return false;
} }
void DirectXGrabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) void DirectXGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
{ {
Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom);
setupDisplay(); setupDisplay();
} }
void DirectXGrabber::setDisplayIndex(int index) bool DirectXGrabber::setDisplayIndex(int index)
{ {
bool rc (true);
if(_display != unsigned(index)) if(_display != unsigned(index))
{ {
_display = unsigned(index); _display = unsigned(index);
setupDisplay(); rc = setupDisplay();
} }
return rc;
}
QJsonObject DirectXGrabber::discover(const QJsonObject& params)
{
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject inputsDiscovered;
if ((_d3d9 = Direct3DCreate9(D3D_SDK_VERSION)) != nullptr)
{
int adapterCount = (int)_d3d9->GetAdapterCount();
if(adapterCount > 0)
{
inputsDiscovered["device"] = "dx";
inputsDiscovered["device_name"] = "DX";
inputsDiscovered["type"] = "screen";
QJsonArray video_inputs;
QJsonArray fps = { 1, 5, 10, 15, 20, 25, 30, 40, 50, 60 };
for(int adapter = 0; adapter < adapterCount; adapter++)
{
QJsonObject in;
in["inputIdx"] = adapter;
D3DADAPTER_IDENTIFIER9 identifier;
_d3d9->GetAdapterIdentifier(adapter, D3DENUM_WHQL_LEVEL, &identifier);
QString name = identifier.DeviceName;
int pos = name.lastIndexOf('\\');
if (pos != -1)
name = name.right(name.length()-pos-1);
in["name"] = name;
D3DDISPLAYMODE ddm;
_d3d9->GetAdapterDisplayMode(adapter, &ddm);
QJsonArray formats, resolutionArray;
QJsonObject format, resolution;
resolution["width"] = (int)ddm.Width;
resolution["height"] = (int)ddm.Height;
resolution["fps"] = fps;
resolutionArray.append(resolution);
format["resolutions"] = resolutionArray;
formats.append(format);
in["formats"] = formats;
video_inputs.append(in);
}
inputsDiscovered["video_inputs"] = video_inputs;
}
else
{
DebugIf(verbose, _log, "No displays found to capture from!");
}
}
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
} }

View File

@ -1,9 +1,16 @@
#include <grabber/DirectXWrapper.h> #include <grabber/DirectXWrapper.h>
DirectXWrapper::DirectXWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, const unsigned updateRate_Hz) DirectXWrapper::DirectXWrapper( int updateRate_Hz,
: GrabberWrapper("DirectX", &_grabber, 0, 0, updateRate_Hz) int display,
, _grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation, display) int pixelDecimation,
{} int cropLeft, int cropRight, int cropTop, int cropBottom
)
: GrabberWrapper("DirectX", &_grabber, updateRate_Hz)
, _grabber(display, cropLeft, cropRight, cropTop, cropBottom)
{
_grabber.setPixelDecimation(pixelDecimation);
}
void DirectXWrapper::action() void DirectXWrapper::action()
{ {

View File

@ -3,48 +3,34 @@
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
//Qt
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSize>
// Constants
namespace {
const bool verbose = false;
const int DEFAULT_DEVICE = 0;
} //End of constants
// Local includes // Local includes
#include "grabber/DispmanxFrameGrabber.h" #include "grabber/DispmanxFrameGrabber.h"
DispmanxFrameGrabber::DispmanxFrameGrabber(unsigned width, unsigned height) DispmanxFrameGrabber::DispmanxFrameGrabber()
: Grabber("DISPMANXGRABBER", 0, 0) : Grabber("DISPMANXGRABBER")
, _vc_display(0) , _vc_display(0)
, _vc_resource(0) , _vc_resource(0)
, _vc_flags(0) , _vc_flags(DISPMANX_TRANSFORM_T(0))
, _captureBuffer(new ColorRgba[0]) , _captureBuffer(new ColorRgba[0])
, _captureBufferSize(0) , _captureBufferSize(0)
, _image_rgba(width, height) , _image_rgba()
{ {
_useImageResampler = false; _useImageResampler = true;
// Initialise BCM
// Initiase BCM
bcm_host_init(); bcm_host_init();
// Check if the display can be opened and display the current resolution
// Open the connection to the display
_vc_display = vc_dispmanx_display_open(0);
assert(_vc_display > 0);
// Obtain the display information
DISPMANX_MODEINFO_T vc_info;
int result = vc_dispmanx_display_get_info(_vc_display, &vc_info);
// Keep compiler happy in 'release' mode
(void)result;
// Close the display
vc_dispmanx_display_close(_vc_display);
if(result != 0)
{
Error(_log, "Failed to open display! Probably no permissions to access the capture interface");
setEnabled(false);
return;
}
else
Info(_log, "Display opened with resolution: %dx%d", vc_info.width, vc_info.height);
// init the resource and capture rectangle
setWidthHeight(width, height);
} }
DispmanxFrameGrabber::~DispmanxFrameGrabber() DispmanxFrameGrabber::~DispmanxFrameGrabber()
@ -55,6 +41,28 @@ DispmanxFrameGrabber::~DispmanxFrameGrabber()
bcm_host_deinit(); bcm_host_deinit();
} }
bool DispmanxFrameGrabber::setupScreen()
{
bool rc (false);
int deviceIdx (DEFAULT_DEVICE);
QSize screenSize = getScreenSize(deviceIdx);
if ( screenSize.isEmpty() )
{
Error(_log, "Failed to open display [%d]! Probably no permissions to access the capture interface", deviceIdx);
setEnabled(false);
}
else
{
setWidthHeight(screenSize.width(), screenSize.height());
Info(_log, "Display [%d] opened with resolution: %dx%d", deviceIdx, screenSize.width(), screenSize.height());
setEnabled(true);
rc = true;
}
return rc;
}
void DispmanxFrameGrabber::freeResources() void DispmanxFrameGrabber::freeResources()
{ {
delete[] _captureBuffer; delete[] _captureBuffer;
@ -64,11 +72,14 @@ void DispmanxFrameGrabber::freeResources()
bool DispmanxFrameGrabber::setWidthHeight(int width, int height) bool DispmanxFrameGrabber::setWidthHeight(int width, int height)
{ {
bool rc = false;
if(Grabber::setWidthHeight(width, height)) if(Grabber::setWidthHeight(width, height))
{ {
if(_vc_resource != 0) if(_vc_resource != 0) {
vc_dispmanx_resource_delete(_vc_resource); vc_dispmanx_resource_delete(_vc_resource);
// Create the resources for capturing image }
Debug(_log,"Create the resources for capturing image");
uint32_t vc_nativeImageHandle; uint32_t vc_nativeImageHandle;
_vc_resource = vc_dispmanx_resource_create( _vc_resource = vc_dispmanx_resource_create(
VC_IMAGE_RGBA32, VC_IMAGE_RGBA32,
@ -77,55 +88,42 @@ bool DispmanxFrameGrabber::setWidthHeight(int width, int height)
&vc_nativeImageHandle); &vc_nativeImageHandle);
assert(_vc_resource); assert(_vc_resource);
// Define the capture rectangle with the same size if (_vc_resource != 0)
{
Debug(_log,"Define the capture rectangle with the same size");
vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height); vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height);
return true; rc = true;
} }
return false; }
return rc;
} }
void DispmanxFrameGrabber::setFlags(int vc_flags) void DispmanxFrameGrabber::setFlags(DISPMANX_TRANSFORM_T vc_flags)
{ {
_vc_flags = vc_flags; _vc_flags = vc_flags;
} }
int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image) int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
{ {
if (!_enabled) return 0; int rc = 0;
if (_isEnabled && !_isDeviceInError)
int ret; {
// vc_dispmanx_resource_read_data doesn't seem to work well // vc_dispmanx_resource_read_data doesn't seem to work well
// with arbitrary positions so we have to handle cropping by ourselves // with arbitrary positions so we have to handle cropping by ourselves
unsigned cropLeft = _cropLeft; int cropLeft = _cropLeft;
unsigned cropRight = _cropRight; int cropRight = _cropRight;
unsigned cropTop = _cropTop; int cropTop = _cropTop;
unsigned cropBottom = _cropBottom; int cropBottom = _cropBottom;
if (_vc_flags & DISPMANX_SNAPSHOT_FILL) if (_vc_flags & DISPMANX_SNAPSHOT_FILL)
{ {
// disable cropping, we are capturing the video overlay window // disable cropping, we are capturing the video overlay window
Debug(_log,"Disable cropping, as the video overlay window is captured");
cropLeft = cropRight = cropTop = cropBottom = 0; cropLeft = cropRight = cropTop = cropBottom = 0;
} }
unsigned imageWidth = _width - cropLeft - cropRight; unsigned imageWidth = static_cast<unsigned>(_width - cropLeft - cropRight);
unsigned imageHeight = _height - cropTop - cropBottom; unsigned imageHeight = static_cast<unsigned>(_height - cropTop - cropBottom);
// calculate final image dimensions and adjust top/left cropping in 3D modes
switch (_videoMode)
{
case VideoMode::VIDEO_3DSBS:
imageWidth /= 2;
cropLeft /= 2;
break;
case VideoMode::VIDEO_3DTAB:
imageHeight /= 2;
cropTop /= 2;
break;
case VideoMode::VIDEO_2D:
default:
break;
}
// resize the given image if needed // resize the given image if needed
if (image.width() != imageWidth || image.height() != imageHeight) if (image.width() != imageWidth || image.height() != imageHeight)
@ -139,22 +137,23 @@ int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
} }
// Open the connection to the display // Open the connection to the display
_vc_display = vc_dispmanx_display_open(0); _vc_display = vc_dispmanx_display_open(DEFAULT_DEVICE);
if (_vc_display < 0) if (_vc_display < 0)
{ {
Error(_log, "Cannot open display: %d", _vc_display); Error(_log, "Cannot open display: %d", DEFAULT_DEVICE);
return -1; rc = -1;
} }
else {
// Create the snapshot (incl down-scaling) // Create the snapshot (incl down-scaling)
ret = vc_dispmanx_snapshot(_vc_display, _vc_resource, (DISPMANX_TRANSFORM_T) _vc_flags); int ret = vc_dispmanx_snapshot(_vc_display, _vc_resource, _vc_flags);
if (ret < 0) if (ret < 0)
{ {
Error(_log, "Snapshot failed: %d", ret); Error(_log, "Snapshot failed: %d", ret);
vc_dispmanx_display_close(_vc_display); rc = ret;
return ret;
} }
else
{
// Read the snapshot into the memory // Read the snapshot into the memory
void* imagePtr = _image_rgba.memptr(); void* imagePtr = _image_rgba.memptr();
void* capturePtr = imagePtr; void* capturePtr = imagePtr;
@ -166,11 +165,11 @@ int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
// grab to temp buffer if image pitch isn't valid or if we are cropping // grab to temp buffer if image pitch isn't valid or if we are cropping
if (imagePitch != capturePitch if (imagePitch != capturePitch
|| (unsigned)_rectangle.width != imageWidth || static_cast<unsigned>(_rectangle.width) != imageWidth
|| (unsigned)_rectangle.height != imageHeight) || static_cast<unsigned>(_rectangle.height) != imageHeight)
{ {
// check if we need to resize the capture buffer // check if we need to resize the capture buffer
unsigned captureSize = capturePitch * _rectangle.height / sizeof(ColorRgba); unsigned captureSize = capturePitch * static_cast<unsigned>(_rectangle.height) / sizeof(ColorRgba);
if (_captureBufferSize != captureSize) if (_captureBufferSize != captureSize)
{ {
delete[] _captureBuffer; delete[] _captureBuffer;
@ -185,31 +184,107 @@ int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
if (ret < 0) if (ret < 0)
{ {
Error(_log, "vc_dispmanx_resource_read_data failed: %d", ret); Error(_log, "vc_dispmanx_resource_read_data failed: %d", ret);
vc_dispmanx_display_close(_vc_display); rc = ret;
return ret;
} }
else
// copy capture data to image if we captured to temp buffer
if (imagePtr != capturePtr)
{ {
// adjust source pointer to top/left cropping _imageResampler.processImage(static_cast<uint8_t*>(capturePtr),
uint8_t* src_ptr = (uint8_t*) capturePtr _width,
+ cropLeft * sizeof(ColorRgba) _height,
+ cropTop * capturePitch; static_cast<int>(capturePitch),
PixelFormat::RGB32,
for (unsigned y = 0; y < imageHeight; y++) image);
{
memcpy((uint8_t*)imagePtr + y * imagePitch,
src_ptr + y * capturePitch,
imagePitch);
} }
} }
// Close the displaye
vc_dispmanx_display_close(_vc_display); vc_dispmanx_display_close(_vc_display);
}
// image to output image }
_image_rgba.toRgb(image); return rc;
}
return 0;
QSize DispmanxFrameGrabber::getScreenSize(int device) const
{
int width (0);
int height(0);
DISPMANX_DISPLAY_HANDLE_T vc_display = vc_dispmanx_display_open(device);
if ( vc_display > 0)
{
// Obtain the display information
DISPMANX_MODEINFO_T vc_info;
int result = vc_dispmanx_display_get_info(vc_display, &vc_info);
(void)result;
if (result == 0)
{
width = vc_info.width;
height = vc_info.height;
DebugIf(verbose, _log, "Display found with resolution: %dx%d", width, height);
}
// Close the display
vc_dispmanx_display_close(vc_display);
}
return QSize(width, height);
}
QJsonObject DispmanxFrameGrabber::discover(const QJsonObject& params)
{
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject inputsDiscovered;
int deviceIdx (DEFAULT_DEVICE);
QJsonArray video_inputs;
QSize screenSize = getScreenSize(deviceIdx);
if ( !screenSize.isEmpty() )
{
QJsonArray fps = { 1, 5, 10, 15, 20, 25, 30, 40, 50, 60 };
QJsonObject in;
QString displayName;
displayName = QString("Screen:%1").arg(deviceIdx);
in["name"] = displayName;
in["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);
in["formats"] = formats;
video_inputs.append(in);
}
if (!video_inputs.isEmpty())
{
inputsDiscovered["device"] = "dispmanx";
inputsDiscovered["device_name"] = "DispmanX";
inputsDiscovered["type"] = "screen";
inputsDiscovered["video_inputs"] = video_inputs;
}
if (inputsDiscovered.isEmpty())
{
DebugIf(verbose, _log, "No displays found to capture from!");
}
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
} }

View File

@ -5,6 +5,7 @@
unsigned __bcm_frame_counter = 0; unsigned __bcm_frame_counter = 0;
const int __screenWidth = 800; const int __screenWidth = 800;
const int __screenHeight = 600; const int __screenHeight = 600;
const int __display_num = 0;
void bcm_host_init() void bcm_host_init()
{ {
@ -27,6 +28,7 @@ int vc_dispmanx_display_get_info(int, DISPMANX_MODEINFO_T *vc_info)
{ {
vc_info->width = __screenWidth; vc_info->width = __screenWidth;
vc_info->height = __screenHeight; vc_info->height = __screenHeight;
vc_info->display_num = __display_num;
return 0; return 0;
} }
@ -54,7 +56,7 @@ void vc_dispmanx_rect_set(VC_RECT_T *rectangle, int left, int top, int width, in
rectangle->top = top; rectangle->top = top;
} }
int vc_dispmanx_snapshot(int, DISPMANX_RESOURCE_HANDLE_T resource, int vc_flags) int vc_dispmanx_snapshot(DISPMANX_DISPLAY_HANDLE_T /*display*/, DISPMANX_RESOURCE_HANDLE_T resource, DISPMANX_TRANSFORM_T /*vc_flags*/)
{ {
__bcm_frame_counter++; __bcm_frame_counter++;
if (__bcm_frame_counter > 100) if (__bcm_frame_counter > 100)
@ -66,7 +68,7 @@ int vc_dispmanx_snapshot(int, DISPMANX_RESOURCE_HANDLE_T resource, int vc_flags)
if (__bcm_frame_counter < 25) if (__bcm_frame_counter < 25)
{ {
color[0] = ColorRgba::WHITE; color[0] = ColorRgba::WHITE;
0 color[1] = ColorRgba::RED; color[1] = ColorRgba::RED;
color[2] = ColorRgba::BLUE; color[2] = ColorRgba::BLUE;
color[3] = ColorRgba::GREEN; color[3] = ColorRgba::GREEN;
} }

View File

@ -1,10 +1,12 @@
#include <grabber/DispmanxWrapper.h> #include <grabber/DispmanxWrapper.h>
DispmanxWrapper::DispmanxWrapper(unsigned grabWidth, unsigned grabHeight, unsigned updateRate_Hz) DispmanxWrapper::DispmanxWrapper( int updateRate_Hz,
: GrabberWrapper("Dispmanx", &_grabber, grabWidth, grabHeight, updateRate_Hz) int pixelDecimation
, _grabber(grabWidth, grabHeight) )
: GrabberWrapper("Dispmanx", &_grabber, updateRate_Hz)
, _grabber()
{ {
_grabber.setPixelDecimation(pixelDecimation);
} }
void DispmanxWrapper::action() void DispmanxWrapper::action()

View File

@ -10,102 +10,261 @@
// STL includes // STL includes
#include <iostream> #include <iostream>
//Qt
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QDir>
#include <QSize>
// Constants
namespace {
const bool verbose = false;
// fb discovery service
const char DISCOVERY_DIRECTORY[] = "/dev/";
const char DISCOVERY_FILEPATTERN[] = "fb?";
} //End of constants
// Local includes // Local includes
#include <grabber/FramebufferFrameGrabber.h> #include <grabber/FramebufferFrameGrabber.h>
FramebufferFrameGrabber::FramebufferFrameGrabber(const QString & device, unsigned width, unsigned height) FramebufferFrameGrabber::FramebufferFrameGrabber(const QString & device)
: Grabber("FRAMEBUFFERGRABBER", width, height) : Grabber("FRAMEBUFFERGRABBER")
, _fbDevice() , _fbDevice(device)
, _fbfd (-1)
{ {
setDevicePath(device); _useImageResampler = true;
}
FramebufferFrameGrabber::~FramebufferFrameGrabber()
{
closeDevice();
}
bool FramebufferFrameGrabber::setupScreen()
{
bool rc (false);
if ( _fbfd >= 0 )
{
closeDevice();
}
rc = getScreenInfo();
setEnabled(rc);
return rc;
}
bool FramebufferFrameGrabber::setWidthHeight(int width, int height)
{
bool rc (false);
if(Grabber::setWidthHeight(width, height))
{
rc = setupScreen();
}
return rc;
} }
int FramebufferFrameGrabber::grabFrame(Image<ColorRgb> & image) int FramebufferFrameGrabber::grabFrame(Image<ColorRgb> & image)
{ {
if (!_enabled) return 0; int rc = 0;
struct fb_var_screeninfo vinfo; if (_isEnabled && !_isDeviceInError)
unsigned capSize, bytesPerPixel; {
PixelFormat pixelFormat; if ( getScreenInfo() )
{
/* map the device to memory */
uint8_t * fbp = static_cast<uint8_t*>(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<int>(_varInfo.xres),
static_cast<int>(_varInfo.yres),
static_cast<int>(_fixInfo.line_length),
_pixelFormat,
image);
munmap(fbp, _fixInfo.smem_len);
}
}
closeDevice();
}
return rc;
}
bool FramebufferFrameGrabber::openDevice()
{
bool rc = true;
/* Open the framebuffer device */ /* Open the framebuffer device */
int fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY); _fbfd = ::open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (fbfd == -1) if (_fbfd < 0)
{ {
Error(_log, "Error opening %s, %s : ", QSTRING_CSTR(_fbDevice), std::strerror(errno)); QString errorReason = QString ("Error opening %1, [%2] %3").arg(_fbDevice).arg(errno).arg(std::strerror(errno));
return -1; this->setInError ( errorReason );
rc = false;
} }
return rc;
}
/* get variable screen information */ bool FramebufferFrameGrabber::closeDevice()
ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo); {
bool rc = false;
bytesPerPixel = vinfo.bits_per_pixel / 8; if (_fbfd >= 0)
capSize = vinfo.xres * vinfo.yres * bytesPerPixel;
switch (vinfo.bits_per_pixel)
{ {
case 16: pixelFormat = PixelFormat::BGR16; break; if( ::close(_fbfd) == 0) {
case 24: pixelFormat = PixelFormat::BGR24; break; rc = true;
}
_fbfd = -1;
}
return rc;
}
QSize FramebufferFrameGrabber::getScreenSize() const
{
return getScreenSize(_fbDevice);
}
QSize FramebufferFrameGrabber::getScreenSize(const QString& device) const
{
int width (0);
int height(0);
int fbfd = ::open(QSTRING_CSTR(device), O_RDONLY);
if (fbfd != -1)
{
struct fb_var_screeninfo vinfo;
int result = ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo);
if (result == 0)
{
width = static_cast<int>(vinfo.xres);
height = static_cast<int>(vinfo.yres);
DebugIf(verbose, _log, "FB device [%s] found with resolution: %dx%d", QSTRING_CSTR(device), width, height);
}
::close(fbfd);
}
return QSize(width, height);
}
bool FramebufferFrameGrabber::getScreenInfo()
{
bool rc (false);
if ( openDevice() )
{
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;
#ifdef ENABLE_AMLOGIC #ifdef ENABLE_AMLOGIC
case 32: pixelFormat = PixelFormat::PIXELFORMAT_RGB32; break; case 32: _pixelFormat = PixelFormat::PIXELFORMAT_RGB32;
break;
#else #else
case 32: pixelFormat = PixelFormat::BGR32; break; case 32: _pixelFormat = PixelFormat::BGR32;
break;
#endif #endif
default: default:
Error(_log, "Unknown pixel format: %d bits per pixel", vinfo.bits_per_pixel); rc= false;
close(fbfd); QString errorReason = QString ("Unknown pixel format: %1 bits per pixel").arg(static_cast<int>(_varInfo.bits_per_pixel));
return -1; this->setInError ( errorReason );
closeDevice();
} }
/* map the device to memory */
unsigned char * fbp = (unsigned char*)mmap(0, capSize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fbfd, 0);
if (fbp == MAP_FAILED) {
Error(_log, "Error mapping %s, %s : ", QSTRING_CSTR(_fbDevice), std::strerror(errno));
return -1;
} }
}
_imageResampler.setHorizontalPixelDecimation(vinfo.xres/_width); return rc;
_imageResampler.setVerticalPixelDecimation(vinfo.yres/_height);
_imageResampler.processImage(fbp,
vinfo.xres,
vinfo.yres,
vinfo.xres * bytesPerPixel,
pixelFormat,
image);
munmap(fbp, capSize);
close(fbfd);
return 0;
} }
void FramebufferFrameGrabber::setDevicePath(const QString& path) QJsonObject FramebufferFrameGrabber::discover(const QJsonObject& params)
{ {
if(_fbDevice != path) DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
{
_fbDevice = path;
int result;
struct fb_var_screeninfo vinfo;
// Check if the framebuffer device can be opened and display the current resolution QJsonObject inputsDiscovered;
int fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (fbfd == -1) //Find framebuffer devices 0-9
QDir deviceDirectory (DISCOVERY_DIRECTORY);
QStringList deviceFilter(DISCOVERY_FILEPATTERN);
deviceDirectory.setNameFilters(deviceFilter);
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)
{ {
Error(_log, "Error opening %s, %s : ", QSTRING_CSTR(_fbDevice), std::strerror(errno)); fbIdx = (*deviceFileIterator).fileName().rightRef(1).toInt();
} QString device = (*deviceFileIterator).absoluteFilePath();
else DebugIf(verbose, _log, "FB device [%s] found", QSTRING_CSTR(device));
QSize screenSize = getScreenSize(device);
if ( !screenSize.isEmpty() )
{ {
// get variable screen information QJsonArray fps = { "1", "5", "10", "15", "20", "25", "30", "40", "50", "60" };
result = ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo);
if (result != 0) QJsonObject in;
QString displayName;
displayName = QString("FB%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())
{ {
Error(_log, "Could not get screen information, %s", std::strerror(errno)); inputsDiscovered["device"] = "framebuffer";
inputsDiscovered["device_name"] = "Framebuffer";
inputsDiscovered["type"] = "screen";
inputsDiscovered["video_inputs"] = video_inputs;
} }
else }
if (inputsDiscovered.isEmpty())
{ {
Info(_log, "Display opened with resolution: %dx%d@%dbit", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); DebugIf(verbose, _log, "No displays found to capture from!");
}
close(fbfd);
}
} }
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
} }

View File

@ -1,9 +1,13 @@
#include <grabber/FramebufferWrapper.h> #include <grabber/FramebufferWrapper.h>
FramebufferWrapper::FramebufferWrapper(const QString & device, unsigned grabWidth, unsigned grabHeight, unsigned updateRate_Hz) FramebufferWrapper::FramebufferWrapper( int updateRate_Hz,
: GrabberWrapper("FrameBuffer", &_grabber, grabWidth, grabHeight, updateRate_Hz) const QString & device,
, _grabber(device, grabWidth, grabHeight) int pixelDecimation)
{} : GrabberWrapper("FrameBuffer", &_grabber, updateRate_Hz)
, _grabber(device)
{
_grabber.setPixelDecimation(pixelDecimation);
}
void FramebufferWrapper::action() void FramebufferWrapper::action()
{ {

View File

@ -5,35 +5,57 @@
// Local includes // Local includes
#include <grabber/OsxFrameGrabber.h> #include <grabber/OsxFrameGrabber.h>
OsxFrameGrabber::OsxFrameGrabber(unsigned display, unsigned width, unsigned height) //Qt
: Grabber("OSXGRABBER", width, height) #include <QJsonObject>
, _screenIndex(100) #include <QJsonArray>
#include <QJsonDocument>
// Constants
namespace {
const bool verbose = false;
} //End of constants
OsxFrameGrabber::OsxFrameGrabber(int display)
: Grabber("OSXGRABBER")
, _screenIndex(display)
{ {
// check if display is available _isEnabled = false;
setDisplayIndex(display); _useImageResampler = true;
} }
OsxFrameGrabber::~OsxFrameGrabber() OsxFrameGrabber::~OsxFrameGrabber()
{ {
} }
bool OsxFrameGrabber::setupDisplay()
{
bool rc (false);
rc = setDisplayIndex(_screenIndex);
return rc;
}
int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image) int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image)
{ {
if (!_enabled) return 0; int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
CGImageRef dispImage; CGImageRef dispImage;
CFDataRef imgData; CFDataRef imgData;
unsigned char * pImgData; unsigned char * pImgData;
unsigned dspWidth, dspHeight; unsigned dspWidth;
unsigned dspHeight;
dispImage = CGDisplayCreateImage(_display); dispImage = CGDisplayCreateImage(_display);
// display lost, use main // display lost, use main
if (dispImage == NULL && _display) if (dispImage == nullptr && _display != 0)
{ {
dispImage = CGDisplayCreateImage(kCGDirectMainDisplay); dispImage = CGDisplayCreateImage(kCGDirectMainDisplay);
// no displays connected, return // no displays connected, return
if (dispImage == NULL) if (dispImage == nullptr)
{ {
Error(_log, "No display connected..."); Error(_log, "No display connected...");
return -1; return -1;
@ -44,55 +66,143 @@ int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image)
dspWidth = CGImageGetWidth(dispImage); dspWidth = CGImageGetWidth(dispImage);
dspHeight = CGImageGetHeight(dispImage); dspHeight = CGImageGetHeight(dispImage);
_imageResampler.setHorizontalPixelDecimation(dspWidth/_width);
_imageResampler.setVerticalPixelDecimation(dspHeight/_height);
_imageResampler.processImage( pImgData, _imageResampler.processImage( pImgData,
dspWidth, static_cast<int>(dspWidth),
dspHeight, static_cast<int>(dspHeight),
CGImageGetBytesPerRow(dispImage), static_cast<int>(CGImageGetBytesPerRow(dispImage)),
PixelFormat::BGR32, PixelFormat::BGR32,
image); image);
CFRelease(imgData); CFRelease(imgData);
CGImageRelease(dispImage); CGImageRelease(dispImage);
return 0; }
return rc;
} }
void OsxFrameGrabber::setDisplayIndex(int index) bool OsxFrameGrabber::setDisplayIndex(int index)
{ {
if(_screenIndex != index) bool rc (true);
if(_screenIndex != index || !_isEnabled)
{ {
_screenIndex = index; _screenIndex = index;
CGImageRef image;
CGDisplayCount displayCount;
CGDirectDisplayID displays[8];
// get list of displays // get list of displays
CGGetActiveDisplayList(8, displays, &displayCount); CGDisplayCount dspyCnt = 0 ;
if (_screenIndex + 1 > displayCount) CGDisplayErr err;
err = CGGetActiveDisplayList(0, nullptr, &dspyCnt);
if (err == kCGErrorSuccess && dspyCnt > 0)
{ {
Error(_log, "Display with index %d is not available. Using main display", _screenIndex); CGDirectDisplayID *activeDspys = new CGDirectDisplayID [dspyCnt] ;
_display = kCGDirectMainDisplay; err = CGGetActiveDisplayList(dspyCnt, activeDspys, &dspyCnt) ;
if (err == kCGErrorSuccess)
{
CGImageRef image;
if (_screenIndex + 1 > static_cast<int>(dspyCnt))
{
Error(_log, "Display with index %d is not available.", _screenIndex);
rc = false;
} }
else else
{ {
_display = displays[_screenIndex]; _display = activeDspys[_screenIndex];
}
image = CGDisplayCreateImage(_display); image = CGDisplayCreateImage(_display);
if(image == NULL) if(image == nullptr)
{ {
Error(_log, "Failed to open main display, disable capture interface");
setEnabled(false); setEnabled(false);
return; Error(_log, "Failed to open main display, disable capture interface");
rc = false;
} }
else else
{
setEnabled(true); setEnabled(true);
rc = true;
Info(_log, "Display opened with resolution: %dx%d@%dbit", CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerPixel(image)); Info(_log, "Display [%u] opened with resolution: %ux%u@%ubit", _display, CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerPixel(image));
}
CGImageRelease(image); CGImageRelease(image);
} }
}
}
else
{
rc=false;
}
}
return rc;
}
QJsonObject OsxFrameGrabber::discover(const QJsonObject& params)
{
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject inputsDiscovered;
// get list of displays
CGDisplayCount dspyCnt = 0 ;
CGDisplayErr err;
err = CGGetActiveDisplayList(0, nullptr, &dspyCnt);
if (err == kCGErrorSuccess && dspyCnt > 0)
{
CGDirectDisplayID *activeDspys = new CGDirectDisplayID [dspyCnt] ;
err = CGGetActiveDisplayList(dspyCnt, activeDspys, &dspyCnt) ;
if (err == kCGErrorSuccess)
{
inputsDiscovered["device"] = "osx";
inputsDiscovered["device_name"] = "OSX";
inputsDiscovered["type"] = "screen";
QJsonArray video_inputs;
QJsonArray fps = { 1, 5, 10, 15, 20, 25, 30, 40, 50, 60 };
for (int i = 0; i < static_cast<int>(dspyCnt); ++i)
{
QJsonObject in;
CGDirectDisplayID did = activeDspys[i];
QString displayName;
displayName = QString("Display:%1").arg(did);
in["name"] = displayName;
in["inputIdx"] = i;
QJsonArray formats;
QJsonObject format;
QJsonArray resolutionArray;
QJsonObject resolution;
CGDisplayModeRef dispMode = CGDisplayCopyDisplayMode(did);
CGRect rect = CGDisplayBounds(did);
resolution["width"] = static_cast<int>(rect.size.width);
resolution["height"] = static_cast<int>(rect.size.height);
CGDisplayModeRelease(dispMode);
resolution["fps"] = fps;
resolutionArray.append(resolution);
format["resolutions"] = resolutionArray;
formats.append(format);
in["formats"] = formats;
video_inputs.append(in);
}
inputsDiscovered["video_inputs"] = video_inputs;
}
delete [] activeDspys;
}
if (inputsDiscovered.isEmpty())
{
DebugIf(verbose, _log, "No displays found to capture from!");
}
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
} }

View File

@ -5,15 +5,33 @@ unsigned __osx_frame_counter = 0;
const int __screenWidth = 800; const int __screenWidth = 800;
const int __screenHeight = 600; const int __screenHeight = 600;
void CGGetActiveDisplayList(int max, CGDirectDisplayID *displays, CGDisplayCount *displayCount) CGError CGGetActiveDisplayList(uint32_t maxDisplays, CGDirectDisplayID *activeDisplays, uint32_t *displayCount)
{ {
*displayCount = 1; if (maxDisplays == 0 || activeDisplays == nullptr)
displays[0] = 1; {
*displayCount = 2;
}
else
{
displayCount = &maxDisplays;
if (activeDisplays != nullptr)
{
for (CGDirectDisplayID i = 0; i < maxDisplays; ++i)
{
activeDisplays[i] = i;
}
}
else
{
return kCGErrorFailure;
}
}
return kCGErrorSuccess;
} }
CGImageRef CGDisplayCreateImage(CGDirectDisplayID display) CGImageRef CGDisplayCreateImage(CGDirectDisplayID display)
{ {
CGImageRef image = new CGImage(__screenWidth, __screenHeight); CGImageRef image = new CGImage(__screenWidth / (display+1), __screenHeight / (display+1));
return image; return image;
} }
@ -123,4 +141,19 @@ void CFRelease(CFDataRef imgData)
delete imgData; delete imgData;
} }
CGDisplayModeRef CGDisplayCopyDisplayMode(CGDirectDisplayID display)
{
return nullptr;
}
CGRect CGDisplayBounds(CGDirectDisplayID display)
{
CGRect rect;
rect.size.width = __screenWidth / (display+1);
rect.size.height = __screenHeight / (display+1);
return rect;
}
void CGDisplayModeRelease(CGDisplayModeRef mode)
{
}
#endif #endif

View File

@ -1,9 +1,14 @@
#include <grabber/OsxWrapper.h> #include <grabber/OsxWrapper.h>
OsxWrapper::OsxWrapper(unsigned display, unsigned grabWidth, unsigned grabHeight, unsigned updateRate_Hz) OsxWrapper::OsxWrapper( int updateRate_Hz,
: GrabberWrapper("OSX FrameGrabber", &_grabber, grabWidth, grabHeight, updateRate_Hz) int display,
, _grabber(display, grabWidth, grabHeight) int pixelDecimation
{} )
: GrabberWrapper("OSX", &_grabber, updateRate_Hz)
, _grabber(display)
{
_grabber.setPixelDecimation(pixelDecimation);
}
void OsxWrapper::action() void OsxWrapper::action()
{ {

View File

@ -7,23 +7,30 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QWidget> #include <QWidget>
#include <QScreen> #include <QScreen>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
QtGrabber::QtGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display) // Constants
: Grabber("QTGRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom) namespace {
, _display(unsigned(display)) const bool verbose = false;
, _pixelDecimation(pixelDecimation) } //End of constants
, _screenWidth(0)
, _screenHeight(0) QtGrabber::QtGrabber(int display, int cropLeft, int cropRight, int cropTop, int cropBottom)
: Grabber("QTGRABBER", cropLeft, cropRight, cropTop, cropBottom)
, _display(display)
, _calculatedWidth(0)
, _calculatedHeight(0)
, _src_x(0) , _src_x(0)
, _src_y(0) , _src_y(0)
, _src_x_max(0) , _src_x_max(0)
, _src_y_max(0) , _src_y_max(0)
, _isWayland(false)
, _screen(nullptr) , _screen(nullptr)
, _isVirtual(false)
{ {
_logger = Logger::getInstance("Qt");
_useImageResampler = false; _useImageResampler = false;
// init
setupDisplay();
} }
QtGrabber::~QtGrabber() QtGrabber::~QtGrabber()
@ -36,10 +43,38 @@ void QtGrabber::freeResources()
// Qt seems to hold the ownership of the QScreen pointers // Qt seems to hold the ownership of the QScreen pointers
} }
bool QtGrabber::open()
{
bool rc = false;
#ifndef _WIN32
if (getenv("WAYLAND_DISPLAY") != nullptr)
{
_isWayland = true;
}
else
#endif
{
rc = true;
}
return rc;
}
bool QtGrabber::setupDisplay() bool QtGrabber::setupDisplay()
{ {
bool result = false;
if ( ! open() )
{
if ( _isWayland )
{
Error(_log, "Grabber does not work under Wayland!");
}
}
else
{
// cleanup last screen // cleanup last screen
freeResources(); freeResources();
_numberOfSDisplays = 0;
QScreen* primary = QGuiApplication::primaryScreen(); QScreen* primary = QGuiApplication::primaryScreen();
QList<QScreen *> screens = QGuiApplication::screens(); QList<QScreen *> screens = QGuiApplication::screens();
@ -49,38 +84,70 @@ bool QtGrabber::setupDisplay()
screens.prepend(primary); screens.prepend(primary);
// remove last main screen if twice in list // remove last main screen if twice in list
if(screens.lastIndexOf(primary) > 0) if(screens.lastIndexOf(primary) > 0)
{
screens.removeAt(screens.lastIndexOf(primary)); screens.removeAt(screens.lastIndexOf(primary));
} }
}
if(screens.isEmpty()) if(screens.isEmpty())
{ {
Error(_log, "No displays found to capture from!"); Error(_log, "No displays found to capture from!");
return false; result = false;
} }
else
{
_numberOfSDisplays = screens.size();
Info(_log,"Available Displays:"); Info(_log,"Available Displays:");
int index = 0; int index = 0;
for(auto screen : screens) for(auto * screen : qAsConst(screens))
{ {
const QRect geo = screen->geometry(); const QRect geo = screen->geometry();
Info(_log,"Display %d: Name:%s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.left(), geo.top() ,geo.right(), geo.bottom(), screen->depth()); Info(_log,"Display %d: Name: %s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.left(), geo.top() ,geo.right(), geo.bottom(), screen->depth());
index++; ++index;
} }
if (screens.at(0)->size() != screens.at(0)->virtualSize())
{
const QRect vgeo = screens.at(0)->virtualGeometry();
Info(_log,"Display %d: Name: %s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", _numberOfSDisplays, "All Displays", vgeo.left(), vgeo.top() ,vgeo.right(), vgeo.bottom(), screens.at(0)->depth());
}
_isVirtual = false;
// be sure the index is available // be sure the index is available
if(_display > unsigned(screens.size()-1)) if (_display > _numberOfSDisplays - 1 )
{
if ((screens.at(0)->size() != screens.at(0)->virtualSize()) && (_display == _numberOfSDisplays))
{
_isVirtual = true;
_display = 0;
}
else
{ {
Info(_log, "The requested display index '%d' is not available, falling back to display 0", _display); Info(_log, "The requested display index '%d' is not available, falling back to display 0", _display);
_display = 0; _display = 0;
} }
}
// init the requested display // init the requested display
_screen = screens.at(_display); _screen = screens.at(_display);
connect(_screen, &QScreen::geometryChanged, this, &QtGrabber::geometryChanged); connect(_screen, &QScreen::geometryChanged, this, &QtGrabber::geometryChanged);
updateScreenDimensions(true); updateScreenDimensions(true);
if (_isVirtual)
{
Info(_log, "Using virtual display across all screens");
}
else
{
Info(_log,"Initialized display %d", _display); Info(_log,"Initialized display %d", _display);
return true; }
result = true;
}
}
return result;
} }
void QtGrabber::geometryChanged(const QRect &geo) void QtGrabber::geometryChanged(const QRect &geo)
@ -91,90 +158,109 @@ void QtGrabber::geometryChanged(const QRect &geo)
int QtGrabber::grabFrame(Image<ColorRgb> & image) int QtGrabber::grabFrame(Image<ColorRgb> & image)
{ {
if (!_enabled) return 0; int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
if(_screen == nullptr) if(_screen == nullptr)
{ {
// reinit, this will disable capture on failure // reinit, this will disable capture on failure
setEnabled(setupDisplay()); bool result = setupDisplay();
return -1; setEnabled(result);
} }
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
QPixmap resizedPixmap = originalPixmap.scaled(_width,_height);
QImage imageFrame = resizedPixmap.toImage().convertToFormat( QImage::Format_RGB888);
image.resize(imageFrame.width(), imageFrame.height());
for (int y=0; y<imageFrame.height(); ++y) if (_isEnabled)
for (int x=0; x<imageFrame.width(); ++x)
{ {
QColor inPixel(imageFrame.pixel(x,y)); QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
ColorRgb & outPixel = image(x,y); if (originalPixmap.isNull())
outPixel.red = inPixel.red(); {
outPixel.green = inPixel.green(); rc = -1;
outPixel.blue = inPixel.blue();
} }
else
{
QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat( QImage::Format_RGB888);
image.resize(static_cast<uint>(_calculatedWidth), static_cast<uint>(_calculatedHeight));
return 0; for (int y = 0; y < imageFrame.height(); y++)
{
memcpy((unsigned char*)image.memptr() + y * image.width() * 3, static_cast<unsigned char*>(imageFrame.scanLine(y)), imageFrame.width() * 3);
}
}
}
}
return rc;
} }
int QtGrabber::updateScreenDimensions(bool force) int QtGrabber::updateScreenDimensions(bool force)
{ {
if(!_screen) if(_screen == nullptr)
{
return -1; return -1;
}
const QRect& geo = _screen->geometry(); QRect geo;
if (!force && _screenWidth == unsigned(geo.right()) && _screenHeight == unsigned(geo.bottom()))
if (_isVirtual)
{
geo = _screen->virtualGeometry();
}
else
{
geo = _screen->geometry();
}
if (!force && _width == geo.width() && _height == geo.height())
{ {
// No update required // No update required
return 0; return 0;
} }
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, geo.right(), geo.bottom()); Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _width, _height, geo.width(), geo.height());
_screenWidth = geo.right() - geo.left(); _width = geo.width();
_screenHeight = geo.bottom() - geo.top(); _height = geo.height();
int width=0, height=0; int width=0;
int height=0;
// Image scaling is performed by Qt // Image scaling is performed by Qt
width = (_screenWidth > unsigned(_cropLeft + _cropRight)) width = (_width > (_cropLeft + _cropRight))
? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation) ? ((_width - _cropLeft - _cropRight) / _pixelDecimation)
: (_screenWidth / _pixelDecimation); : (_width / _pixelDecimation);
height = (_screenHeight > unsigned(_cropTop + _cropBottom)) height = (_height > (_cropTop + _cropBottom))
? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation) ? ((_height - _cropTop - _cropBottom) / _pixelDecimation)
: (_screenHeight / _pixelDecimation); : (_height / _pixelDecimation);
// calculate final image dimensions and adjust top/left cropping in 3D modes // calculate final image dimensions and adjust top/left cropping in 3D modes
switch (_videoMode) switch (_videoMode)
{ {
case VideoMode::VIDEO_3DSBS: case VideoMode::VIDEO_3DSBS:
_width = width /2; _calculatedWidth = width /2;
_height = height; _calculatedHeight = height;
_src_x = _cropLeft / 2; _src_x = _cropLeft / 2;
_src_y = _cropTop; _src_y = _cropTop;
_src_x_max = (_screenWidth / 2) - _cropRight; _src_x_max = (_width / 2) - _cropRight - _cropLeft;
_src_y_max = _screenHeight - _cropBottom; _src_y_max = _height - _cropBottom - _cropTop;
break; break;
case VideoMode::VIDEO_3DTAB: case VideoMode::VIDEO_3DTAB:
_width = width; _calculatedWidth = width;
_height = height / 2; _calculatedHeight = height / 2;
_src_x = _cropLeft; _src_x = _cropLeft;
_src_y = _cropTop / 2; _src_y = _cropTop / 2;
_src_x_max = _screenWidth - _cropRight; _src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = (_screenHeight / 2) - _cropBottom; _src_y_max = (_height / 2) - _cropBottom - _cropTop;
break; break;
case VideoMode::VIDEO_2D: case VideoMode::VIDEO_2D:
default: default:
_width = width; _calculatedWidth = width;
_height = height; _calculatedHeight = height;
_src_x = _cropLeft; _src_x = _cropLeft;
_src_y = _cropTop; _src_y = _cropTop;
_src_x_max = _screenWidth - _cropRight; _src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = _screenHeight - _cropBottom; _src_y_max = _height - _cropBottom - _cropTop;
break; break;
} }
Info(_log, "Update output image resolution to [%dx%d]", _width, _height); Info(_log, "Update output image resolution to [%dx%d]", _calculatedWidth, _calculatedHeight);
return 1; return 1;
} }
@ -184,22 +270,129 @@ void QtGrabber::setVideoMode(VideoMode mode)
updateScreenDimensions(true); updateScreenDimensions(true);
} }
void QtGrabber::setPixelDecimation(int pixelDecimation) bool QtGrabber::setPixelDecimation(int pixelDecimation)
{ {
_pixelDecimation = pixelDecimation; bool rc (true);
if(Grabber::setPixelDecimation(pixelDecimation))
{
if ( updateScreenDimensions(true) < 0)
{
rc = false;
}
}
return rc;
} }
void QtGrabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) void QtGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
{ {
Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom);
updateScreenDimensions(true); updateScreenDimensions(true);
} }
void QtGrabber::setDisplayIndex(int index) bool QtGrabber::setDisplayIndex(int index)
{ {
if(_display != unsigned(index)) bool rc (true);
if (_display != index)
{ {
_display = unsigned(index); if (index <= _numberOfSDisplays)
setupDisplay(); {
_display = index;
} }
else {
_display = 0;
}
rc = setupDisplay();
}
return rc;
}
QJsonObject QtGrabber::discover(const QJsonObject& params)
{
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject inputsDiscovered;
if ( open() )
{
QList<QScreen*> screens = QGuiApplication::screens();
if (!screens.isEmpty())
{
inputsDiscovered["device"] = "qt";
inputsDiscovered["device_name"] = "QT";
inputsDiscovered["type"] = "screen";
QJsonArray video_inputs;
QJsonArray fps = { 1, 5, 10, 15, 20, 25, 30, 40, 50, 60 };
for (int i = 0; i < screens.size(); ++i)
{
QJsonObject in;
QString name = screens.at(i)->name();
int pos = name.lastIndexOf('\\');
if (pos != -1)
{
name = name.right(name.length()-pos-1);
}
in["name"] = name;
in["inputIdx"] = i;
QJsonArray formats;
QJsonObject format;
QJsonArray resolutionArray;
QJsonObject resolution;
resolution["width"] = screens.at(i)->size().width();
resolution["height"] = screens.at(i)->size().height();
resolution["fps"] = fps;
resolutionArray.append(resolution);
format["resolutions"] = resolutionArray;
formats.append(format);
in["formats"] = formats;
video_inputs.append(in);
}
if (screens.at(0)->size() != screens.at(0)->virtualSize())
{
QJsonObject in;
in["name"] = "All Displays";
in["inputIdx"] = screens.size();
in["virtual"] = true;
QJsonArray formats;
QJsonObject format;
QJsonArray resolutionArray;
QJsonObject resolution;
resolution["width"] = screens.at(0)->virtualSize().width();
resolution["height"] = screens.at(0)->virtualSize().height();
resolution["fps"] = fps;
resolutionArray.append(resolution);
format["resolutions"] = resolutionArray;
formats.append(format);
in["formats"] = formats;
video_inputs.append(in);
}
inputsDiscovered["video_inputs"] = video_inputs;
}
if (inputsDiscovered.isEmpty())
{
DebugIf(verbose, _log, "No displays found to capture from!");
}
}
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
} }

View File

@ -1,9 +1,20 @@
#include <grabber/QtWrapper.h> #include <grabber/QtWrapper.h>
QtWrapper::QtWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, unsigned updateRate_Hz) QtWrapper::QtWrapper( int updateRate_Hz,
: GrabberWrapper("Qt", &_grabber, 0, 0, updateRate_Hz) int display,
, _grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation, display) int pixelDecimation,
{} int cropLeft, int cropRight, int cropTop, int cropBottom
)
: GrabberWrapper("Qt", &_grabber, updateRate_Hz)
, _grabber(display, cropLeft, cropRight, cropTop, cropBottom)
{
_grabber.setPixelDecimation(pixelDecimation);
}
bool QtWrapper::open()
{
return _grabber.open();
}
void QtWrapper::action() void QtWrapper::action()
{ {

View File

@ -1,18 +0,0 @@
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/v4l2)
FILE ( GLOB V4L2_SOURCES "${CURRENT_HEADER_DIR}/V4L2*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
add_library(v4l2-grabber ${V4L2_SOURCES} )
target_link_libraries(v4l2-grabber
hyperion
${QT_LIBRARIES}
)
if(TURBOJPEG_FOUND)
target_link_libraries(v4l2-grabber ${TurboJPEG_LIBRARY})
elseif (JPEG_FOUND)
target_link_libraries(v4l2-grabber ${JPEG_LIBRARY})
endif(TURBOJPEG_FOUND)

Some files were not shown because too many files have changed in this diff Show More