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 5b0ce3c0f2.

* 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
# detect CI
if [ "$SYSTEM_COLLECTIONID" != "" ]; then
# Azure Pipelines
echo "Azure detected"
CI_NAME="$(echo "$AGENT_OS" | tr '[:upper:]' '[:lower:]')"
CI_BUILD_DIR="$BUILD_SOURCESDIRECTORY"
elif [ "$HOME" != "" ]; then
if [ "$HOME" != "" ]; then
# GitHub Actions
echo "Github Actions detected"
CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')"
CI_BUILD_DIR="$GITHUB_WORKSPACE"
else

View File

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

View File

@ -159,10 +159,21 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\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
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
shell: cmd

View File

@ -122,10 +122,21 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\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
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
shell: cmd

8
.gitignore vendored
View File

@ -27,5 +27,11 @@ libsrc/flatbufserver/hyperion_request_generated.h
*.kdev*
# Visual Studio 2015/2017/2019 cache/options directory
.vs/
# Ignore
.vs/*
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",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/Debug/hyperiond.exe",
"program": "${command:cmake.launchTargetDirectory}/hyperiond",
"args": ["-d"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false
"console": "internalConsole"
}
]
}

View File

@ -4,10 +4,15 @@ message( STATUS "CMake Version: ${CMAKE_VERSION}" )
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)
file (STRINGS "version" 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.
set(CMAKE_AUTOMOC ON)
@ -50,6 +55,7 @@ SET ( DEFAULT_USE_SYSTEM_PROTO_LIBS OFF )
SET ( DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF )
SET ( DEFAULT_TESTS OFF )
SET ( DEFAULT_EXPERIMENTAL OFF )
SET ( DEFAULT_MF OFF )
SET ( DEFAULT_DEPLOY_DEPENDENCIES ON )
IF ( ${CMAKE_SYSTEM} MATCHES "Linux" )
@ -60,7 +66,8 @@ IF ( ${CMAKE_SYSTEM} MATCHES "Linux" )
SET ( DEFAULT_USB_HID ON )
SET ( DEFAULT_CEC ON )
ELSEIF ( WIN32 )
SET ( DEFAULT_DX OFF )
SET ( DEFAULT_DX ON )
SET ( DEFAULT_MF ON )
ELSE()
SET ( DEFAULT_V4L2 OFF )
SET ( DEFAULT_FB OFF )
@ -121,9 +128,14 @@ elseif ( "${PLATFORM}" MATCHES "rpi" )
SET ( DEFAULT_DISPMANX ON )
SET ( DEFAULT_WS281XPWM ON )
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" )
SET ( DEFAULT_AMLOGIC ON )
SET ( DEFAULT_AMLOGIC ON )
elseif ( "${PLATFORM}" MATCHES "x11" )
SET ( DEFAULT_X11 ON )
SET ( DEFAULT_XCB ON )
@ -150,17 +162,18 @@ ADD_DEFINITIONS( ${PLATFORM_DEFINE} )
option(ENABLE_AMLOGIC "Enable the AMLOGIC video grabber" ${DEFAULT_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)
SET(ENABLE_FB ON)
else()
option(ENABLE_FB "Enable the framebuffer grabber" ${DEFAULT_FB} )
endif()
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}")
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})
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} )
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})
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}")
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
FILE ( GLOB_RECURSE HYPERION_SCHEMAS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libsrc/*schema*.json )
SET( JSON_FILES
config/hyperion.config.json.default
${HYPERION_SCHEMAS}
)
SET( JSON_FILES ${CMAKE_BINARY_DIR}/config/hyperion.config.json.default ${HYPERION_SCHEMAS})
EXECUTE_PROCESS (
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')
IF (NOT WIN32)
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}
RESULT_VARIABLE CHECK_CONFIG_FAILED
)
@ -399,31 +412,6 @@ find_package(libusb-1.0 REQUIRED)
find_package(Threads REQUIRED)
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)
set(CMAKE_EXE_LINKER_FLAGS "-framework CoreGraphics")
endif()

View File

@ -1,48 +1,51 @@
// Generated config file
// Define to enable the dispmanx grabber
// Define to enable the DispmanX grabber
#cmakedefine ENABLE_DISPMANX
// Define to enable the v4l2 grabber
// Define to enable the V4L2 grabber
#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
// Define to enable the amlogic grabber
// Define to enable the AMLogic grabber
#cmakedefine ENABLE_AMLOGIC
// Define to enable the osx grabber
// Define to enable the OSX grabber
#cmakedefine ENABLE_OSX
// Define to enable the x11 grabber
// Define to enable the X11 grabber
#cmakedefine ENABLE_X11
// Define to enable the xcb grabber
// Define to enable the XCB grabber
#cmakedefine ENABLE_XCB
// Define to enable the qt grabber
// Define to enable the Qt grabber
#cmakedefine ENABLE_QT
// Define to enable the DirectX grabber
#cmakedefine ENABLE_DX
// Define to enable the spi-device
// Define to enable the SPI-Device
#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
// Define to enable the tinkerforge device
// Define to enable the Tinkerforge device
#cmakedefine ENABLE_TINKERFORGE
// Define to enable avahi
// Define to enable AVAHI
#cmakedefine ENABLE_AVAHI
// Define to enable cec
// Define to enable CEC
#cmakedefine ENABLE_CEC
// Define to enable the usb / hid devices
// Define to enable the USB / HID devices
#cmakedefine ENABLE_USB_HID
// Define to enable profiler for development purpose

View File

@ -2,7 +2,7 @@
<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>
<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 id="conf_cont"></div>
</div>
</div>

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

@ -6,17 +6,17 @@
<div class="container-fluid">
<h3 class="page-header"><i class="mdi mdi-lightbulb-on fa-fw"></i><span data-i18n="main_menu_leds_conf_token">LED 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 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="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>
<ul id="leds_cfg_nav" class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#menu_controller" data-i18n="conf_leds_nav_label_ledcontroller">LED Controller</a></li>
@ -37,10 +37,10 @@
<div class="panel-body">
<div id="btn_wiz_holder"></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 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>
</button>
<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>
</div>
<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">
<thead>
<tr>
@ -29,22 +57,34 @@
<tr>
<td></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>
<td></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>
<td></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>
<td></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>
</tbody>
</table>
@ -61,17 +101,17 @@
<tr>
<td></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>
<td></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>
<td></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>
</tbody>
</table>

View File

@ -8,6 +8,7 @@
<div class="panel-body">
<form>
<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="show_pw" type="checkbox" id="show_pw" /><label for="show_pw">Show/Hide Password</label>
</div>

View File

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

View File

@ -274,6 +274,9 @@
"edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_PAL": "PAL",
"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_bbclassic": "Klassisch",
"edt_conf_enum_bbdefault": "Standard",
@ -442,6 +445,8 @@
"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_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_title": "Zertifikats-Pfad",
"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_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_lang_text": "If you don't like the result of the automatic language detection you could overwrite it here.",
"InfoDialog_lang_title": "Language setting",
"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_nostorage_title": "Cannot store settings",
"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_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_error": "We had trouble collecting 3rd party licenses information from web. <br />Please follow this link to the GitHub Resource.",
"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_label_title": "General settings",
"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_option": "Option",
"conf_leds_config_error": "Error in LED/LED layout configuration",
@ -127,10 +130,12 @@
"conf_leds_optgroup_RPiGPIO": "RPi GPIO",
"conf_leds_optgroup_RPiPWM": "RPi PWM",
"conf_leds_optgroup_RPiSPI": "RPi SPI",
"conf_leds_optgroup_debug": "Debug",
"conf_leds_optgroup_network": "Network",
"conf_leds_optgroup_other": "Other",
"conf_leds_optgroup_usb": "USB/Serial",
"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_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.",
@ -274,6 +279,9 @@
"edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_PAL": "PAL",
"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_bbclassic": "Classic",
"edt_conf_enum_bbdefault": "Default",
@ -358,11 +366,20 @@
"edt_conf_general_port_title": "Port",
"edt_conf_general_priority_expl": "The priority of this component",
"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_v4lEnable_expl": "Enables the USB capture for this LED hardware instance",
"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_log_heading_title": "Logging",
"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_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_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_title": "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_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_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_title": "Certificate path",
"edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)",
@ -607,6 +644,7 @@
"edt_eff_height": "Height",
"edt_eff_huechange": "Color change",
"edt_eff_image": "Image file",
"edt_eff_initial_blink" : "Flash for attention",
"edt_eff_interval": "Interval",
"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.",
@ -644,6 +682,7 @@
"edt_eff_reversedirection": "Reverse direction",
"edt_eff_rotationtime": "Rotation time",
"edt_eff_saturation": "Saturation",
"edt_eff_set_post_color" : "Set post color after alam",
"edt_eff_showseconds": "Show seconds",
"edt_eff_sleeptime": "Sleep time",
"edt_eff_smooth_custom": "Enable smoothing",
@ -748,11 +787,11 @@
"general_comp_BOBLIGHTSERVER": "Boblight Server",
"general_comp_FLATBUFSERVER": "Flatbuffers Server",
"general_comp_FORWARDER": "Forwarder",
"general_comp_GRABBER": "Screen Capture",
"general_comp_LEDDEVICE": "LED device",
"general_comp_GRABBER": "Capture Screen",
"general_comp_LEDDEVICE": "LED Output",
"general_comp_PROTOSERVER": "Protocol Buffers Server",
"general_comp_SMOOTHING": "Smoothing",
"general_comp_V4L": "USB Capture",
"general_comp_V4L": "Capture USB-Input",
"general_country_cn": "China",
"general_country_de": "Germany",
"general_country_es": "Spain",
@ -812,6 +851,7 @@
"main_ledsim_btn_togglelednumber": "LED numbers",
"main_ledsim_btn_toggleleds": "Show LEDs",
"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_title": "LED Visualization",
"main_menu_about_token": "About Hyperion",
@ -823,7 +863,8 @@
"main_menu_general_conf_token": "General",
"main_menu_grabber_conf_token": "Capturing Hardware",
"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_network_conf_token": "Network Services",
"main_menu_remotecontrol_token": "Remote Control",
@ -956,6 +997,7 @@
"wiz_hue_title": "Philips Hue Wizard",
"wiz_hue_username": "User ID:",
"wiz_identify": "Identify",
"wiz_identify_tip": "Identify configured device by lighting it up",
"wiz_identify_light": "Identify $1",
"wiz_ids_disabled": "Deactivated",
"wiz_ids_entire": "Whole picture",
@ -974,4 +1016,4 @@
"wiz_yeelight_intro1": "This wizards configures Hyperion for the Yeelight system. Features are the Yeelighs' auto detection, setting each light to a specific position on your picture or disable it and tune the Hyperion settings automatically! So in short: All you need are some clicks and you are done!",
"wiz_yeelight_title": "Yeelight Wizard",
"wiz_yeelight_unsupported": "Unsupported"
}
}

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

@ -2,359 +2,365 @@
<html>
<head>
<script>
if (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent))
{
window.location.pathname = '/content/ie_not_supported.html';
}
</script>
<script>
if (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent)) {
window.location.pathname = '/content/ie_not_supported.html';
}
</script>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
<meta name="description" content="Hyperion Web Configuration">
<meta name="msapplication-TileColor" content="#91989C">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<link rel="apple-touch-icon-precomposed" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" href="/favicon.png" sizes="32x32">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
<meta name="description" content="Hyperion Web Configuration">
<meta name="msapplication-TileColor" content="#91989C">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<link rel="apple-touch-icon-precomposed" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" href="/favicon.png" sizes="32x32">
<title data-i18n="general_webui_title">Hyperion - Web Configuration</title>
<title data-i18n="general_webui_title">Hyperion - Web Configuration</title>
<!-- jQuery -->
<script src="js/lib/jquery.min.js"></script>
<!-- jQuery -->
<script src="js/lib/jquery.min.js"></script>
<!-- SemVer -->
<script src='js/lib/semver.js'></script>
<!-- SemVer -->
<script src='js/lib/semver.js'></script>
<!-- md Stuff -->
<script src="js/lib/marked.min.js"></script>
<script type="text/javascript" src="js/lib/purify.min.js"></script>
<!-- md Stuff -->
<script src="js/lib/marked.min.js"></script>
<script type="text/javascript" src="js/lib/purify.min.js"></script>
<!-- Hyperion -->
<script src="js/hyperion.js"></script>
<script src="js/ui_utils.js"></script>
<!-- Hyperion -->
<script src="js/hyperion.js"></script>
<script src="js/ui_utils.js"></script>
<!-- Colorpicker -->
<script src="js/lib/bootstrap-colorpicker.min.js"></script>
<!-- Colorpicker -->
<script src="js/lib/bootstrap-colorpicker.min.js"></script>
<!-- BS Notfiy -->
<script src="js/lib/bootstrap-notify.min.js"></script>
<!-- BS Notfiy -->
<script src="js/lib/bootstrap-notify.min.js"></script>
<!-- JSONEditor -->
<script src="js/lib/jsoneditor.js"></script>
<!-- JSONEditor -->
<script src="js/lib/jsoneditor.js"></script>
<!--Language Support -->
<script src="js/lib/jquery.i18n/jquery.i18n.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.messagestore.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.fallbacks.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.parser.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.emitter.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.language.js"></script>
<script src="js/lib/jquery.i18n/CLDRPluralRuleParser.js"></script>
<!--Language Support -->
<script src="js/lib/jquery.i18n/jquery.i18n.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.messagestore.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.fallbacks.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.parser.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.emitter.js"></script>
<script src="js/lib/jquery.i18n/jquery.i18n.language.js"></script>
<script src="js/lib/jquery.i18n/CLDRPluralRuleParser.js"></script>
<!-- Bootstrap Core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/languages.js"></script>
<link href="css/bootstrap-colorpicker.min.css" rel="stylesheet">
<!-- Bootstrap Core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- BSToggle -->
<link href="css/bootstrap-toggle.min.css" rel="stylesheet">
<link href="css/bootstrap-colorpicker.min.css" rel="stylesheet">
<!-- BSSelect -->
<link href="css/bootstrap-select.min.css" rel="stylesheet">
<!-- BSToggle -->
<link href="css/bootstrap-toggle.min.css" rel="stylesheet">
<!-- MetisMenu CSS -->
<link href="css/metisMenu.css" rel="stylesheet">
<!-- BSSelect -->
<link href="css/bootstrap-select.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="css/sb-admin-2.css" rel="stylesheet">
<link href="css/hyperion.css" rel="stylesheet">
<!-- MetisMenu CSS -->
<link href="css/metisMenu.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css">
<!-- Custom CSS -->
<link href="css/sb-admin-2.css" rel="stylesheet">
<link href="css/hyperion.css" rel="stylesheet">
<!-- Material Design Icons - https://github.com/Templarian/MaterialDesign-Webfont -->
<link href="css/materialdesignicons.min.css" media="all" rel="stylesheet" type="text/css">
<!-- Custom Fonts -->
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- Material Design Icons - https://github.com/Templarian/MaterialDesign-Webfont -->
<link href="css/materialdesignicons.min.css" media="all" rel="stylesheet" type="text/css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<noscript>
<div style="color:red;margin: 40px 0;text-align:center">
<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>
</div>
<style type="text/css"> #loading_overlay, #wrapper{ display: none } </style>
</noscript>
<noscript>
<div style="color:red;margin: 40px 0;text-align:center">
<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>
</div>
<style type="text/css">
#loading_overlay, #wrapper {
display: none
}
</style>
</noscript>
<div id="loading_overlay" class="overlay"></div>
<div id="wrapper">
<div id="loading_overlay" class="overlay"></div>
<div id="wrapper">
<!-- Navigation -->
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0; min-height: 65px">
<div class="navbar-header">
<button type="button" class="navbar-toggle closed" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar top-bar"></span>
<span class="icon-bar middle-bar"></span>
<span class="icon-bar bottom-bar"></span>
</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>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0; min-height: 85px">
<div class="navbar-header">
<button type="button" class="navbar-toggle closed" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar top-bar"></span>
<span class="icon-bar middle-bar"></span>
<span class="icon-bar bottom-bar"></span>
</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="80"></a>
</div>
<!-- /.navbar-header -->
</div>
<!-- /.navbar-header -->
<ul class="nav navbar-top-links navbar-right" id="top-navbar">
<!-- Browser built in capture stream - streamer.js -->
<li class="dropdown" id="btn_streamer" style="display:none">
<!-- Hidden helpers -->
<canvas style="display:none" id="streamcanvas"></canvas>
<video style="display:none" id="streamvideo" autoplay></video>
<a>
<i id="btn_streamer_icon" class="fa fa-video-camera fa-fw"></i>
</a>
</li>
<li class="dropdown" id="btn_darkmode">
<a>
<i id="btn_darkmode_icon" class="fa fa-moon-o"></i>
</a>
</li>
<li class="dropdown" id="btn_open_ledsim">
<a>
<i class="fa fa-television fa-fw"></i>
</a>
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-magic fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-alerts">
<li id="btn_wizard_byteorder">
<a>
<div>
<i class="fa fa-magic fa-fw"></i>
<span data-i18n="wiz_rgb_title"></span>
</div>
</a>
</li>
<li class="divider"></li>
<li id="btn_wizard_colorcalibration">
<a>
<div>
<i class="fa fa-magic fa-fw"></i>
<span data-i18n="wiz_cc_title"></span>
</div>
</a>
</li>
</ul>
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-alerts">
<ul class="nav navbar-top-links navbar-right" id="top-navbar">
<!-- Browser built in capture stream - streamer.js -->
<li class="dropdown" id="btn_streamer" style="display:none">
<!-- Hidden helpers -->
<canvas style="display:none" id="streamcanvas"></canvas>
<video style="display:none" id="streamvideo" autoplay></video>
<a>
<i id="btn_streamer_icon" class="fa fa-video-camera fa-fw"></i>
</a>
</li>
<li class="dropdown" id="btn_darkmode">
<a>
<i id="btn_darkmode_icon" class="fa fa-moon-o"></i>
</a>
</li>
<li class="dropdown" id="btn_open_ledsim">
<a>
<i class="fa fa-television fa-fw"></i>
</a>
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-magic fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-alerts">
<li id="btn_wizard_byteorder">
<a>
<div>
<i class="fa fa-magic fa-fw"></i>
<span data-i18n="wiz_rgb_title"></span>
</div>
</a>
</li>
<li class="divider"></li>
<li id="btn_wizard_colorcalibration">
<a>
<div>
<i class="fa fa-magic fa-fw"></i>
<span data-i18n="wiz_cc_title"></span>
</div>
</a>
</li>
</ul>
</li>
<!-- /.dropdown -->
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-alerts">
<!-- /.language selector -->
<li id="btn_setlang">
<a>
<div>
<i class="fa fa-globe"></i>
<select id="language-select" class="selectpicker" data-width="fit" data-style="btn-transparent" > </select>
</div>
</a>
</li>
<li class="divider"></li>
<li id="btn_setaccess">
<a>
<div>
<i class="fa fa-wrench fa-fw"></i>
<span data-i18n="InfoDialog_access_title"></span>
</div>
</a>
</li>
<li class="divider"></li>
<li id="btn_changePassword">
<a>
<div>
<i class="fa fa-key fa-fw"></i>
<span data-i18n="InfoDialog_changePassword_title"></span>
</div>
</a>
</li>
</ul>
</li>
<!-- /.language selector -->
<li id="btn_setlang">
<a>
<div>
<i class="fa fa-globe fa-fw"></i>
<select id="language-select" class="selectpicker" data-width="fit" data-style="btn-transparent"> </select>
</div>
</a>
</li>
<li class="divider"></li>
<li id="btn_setaccess">
<a>
<div>
<i class="fa fa-wrench fa-fw"></i>
<span data-i18n="InfoDialog_access_title"></span>
</div>
</a>
</li>
<li class="divider"></li>
<li id="btn_changePassword">
<a>
<div>
<i class="fa fa-key fa-fw"></i>
<span data-i18n="InfoDialog_changePassword_title"></span>
</div>
</a>
</li>
</ul>
</li>
<!-- /.lock-ui -->
<li class="dropdown" id="btn_lock_ui" style="display:none">
<a>
<i class="fa fa-lock fa-fw"></i>
</a>
</li>
</ul>
<!-- /.navbar-top-left -->
<!-- /.lock-ui -->
<li class="dropdown" id="btn_lock_ui" style="display:none">
<a>
<i class="fa fa-lock fa-fw"></i>
</a>
</li>
</ul>
<!-- /.navbar-top-left -->
<div id="main-nav" class="navbar-default sidebar" role="navigation">
<div class="sidebar-nav navbar-collapse">
<ul class="nav" id="side-menu">
<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"><i class="fa fa-cog fa-fw"></i><span data-i18n="main_menu_configuration_token">Configuration</span><span class="fa arrow"></span></a>
<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" 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>
</ul>
</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" 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="#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"><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">
<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="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="#about"><i class="fa fa-info-circle fa-fw"></i><span data-i18n="main_menu_about_token">About</span></a> </li>
</ul>
</li>
</ul>
</div>
<!-- /.sidebar-collapse -->
</div>
<!-- /.navbar-static-side -->
</nav>
<div id="main-nav" class="navbar-default sidebar" role="navigation">
<div class="sidebar-nav navbar-collapse">
<ul class="nav" id="side-menu">
<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"><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">
<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_colors"><i class="fa fa-photo fa-fw"></i><span data-i18n="main_menu_colors_conf_token">Image Processing</span></a> </li>
</ul>
</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" 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="#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"><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">
<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" 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>
</ul>
</li>
</ul>
</div>
<!-- /.sidebar-collapse -->
</div>
<!-- /.navbar-static-side -->
</nav>
<!-- Page Content -->
<div id="page-wrapper" style="padding-top:10px; overflow: hidden;">
<div id="hyperion_disabled_notify" style="display:none;padding:0 10px;margin:0">
<div class="bs-callout bs-callout-danger">
<h4 data-i18n="dashboard_alert_message_disabled_t"></h4>
<span data-i18n="dashboard_alert_message_disabled"></span>
</div>
</div>
<!-- Page Content -->
<div id="page-wrapper" style="padding-top:10px; overflow: hidden;">
<div id="hyperion_disabled_notify" style="display:none;padding:0 10px;margin:0">
<div class="bs-callout bs-callout-danger">
<h4 data-i18n="dashboard_alert_message_disabled_t"></h4>
<span data-i18n="dashboard_alert_message_disabled"></span>
</div>
</div>
<div id="page-content"></div>
</div>
<div id="page-content"></div>
</div>
</div>
<!-- /#wrapper -->
</div>
<!-- /#wrapper -->
<div id="container_connection_lost" style="display:none"></div>
<div id="container_restart" style="display:none"></div>
<div id="container_connection_lost" style="display:none"></div>
<div id="container_restart" style="display:none"></div>
<!-- infoDialog -->
<div id="modal_dialog" class="modal fade" role="dialog" style="z-index:9999">
<div class="modal-dialog">
<center>
<div class="modal-content">
<div id="id_body" class="modal-body"></div>
<div id="id_footer" class="modal-footer" style="text-align:center"></div>
</div>
</center>
</div>
</div>
<!-- infoDialog -->
<div id="modal_dialog" class="modal fade" role="dialog" style="z-index:9999">
<div class="modal-dialog">
<center>
<div class="modal-content">
<div id="id_body" class="modal-body"></div>
<div id="id_footer" class="modal-footer" style="text-align:center"></div>
</div>
</center>
</div>
</div>
<!-- renameDialog -->
<div id="modal_dialog_rename" class="modal fade" role="dialog" style="z-index:9999">
<div class="modal-dialog">
<center>
<div class="modal-content">
<div id="id_body_rename" class="modal-body"></div>
<div id="id_footer_rename" class="modal-footer" style="text-align:center"></div>
</div>
</center>
</div>
</div>
<!-- renameDialog -->
<div id="modal_dialog_rename" class="modal fade" role="dialog" style="z-index:9999">
<div class="modal-dialog">
<center>
<div class="modal-content">
<div id="id_body_rename" class="modal-body"></div>
<div id="id_footer_rename" class="modal-footer" style="text-align:center"></div>
</div>
</center>
</div>
</div>
<!-- wizardDialog -->
<div id="wizard_modal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div id="wiz_header" class="modal-header"></div>
<div id="wizp1">
<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">
<div id="wizp1_body" ></div>
</div>
<div id="wizp1_footer" class="modal-footer" style="text-align:center"></div>
</div>
<div id="wizp2" style="display:none">
<center>
<div class="modal-body">
<img id="wizard_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" style="margin-bottom:15px">
<div id="wizp2_body"></div>
</div>
</center>
<div id="wizp2_footer" class="modal-footer" style="text-align:center"></div>
</div>
<div id="wizp3" style="display:none">
<center>
<div class="modal-body">
<img id="wizard_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" style="margin-bottom:15px">
<div id="wizp3_body"></div>
</div>
</center>
<div id="wizp3_footer" class="modal-footer" style="text-align:center"></div>
</div>
</div>
</div>
</div>
<!-- wizardDialog -->
<div id="wizard_modal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div id="wiz_header" class="modal-header"></div>
<div id="wizp1">
<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">
<div id="wizp1_body"></div>
</div>
<div id="wizp1_footer" class="modal-footer" style="text-align:center"></div>
</div>
<div id="wizp2" style="display:none">
<center>
<div class="modal-body">
<img id="wizard_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" style="margin-bottom:15px">
<div id="wizp2_body"></div>
</div>
</center>
<div id="wizp2_footer" class="modal-footer" style="text-align:center"></div>
</div>
<div id="wizp3" style="display:none">
<center>
<div class="modal-body">
<img id="wizard_logo" src="img/hyperion/logo_positiv.png" alt="Redefine ambient light!" style="margin-bottom:15px">
<div id="wizp3_body"></div>
</div>
</center>
<div id="wizp3_footer" class="modal-footer" style="text-align:center"></div>
</div>
</div>
</div>
</div>
<!-- ledsimDialog -->
<div id="ledsim_dialog" style="display:none; z-index:9998">
<div data-role="body" style="padding:0px;">
<div id="ledsim_text"></div>
<div id="leds_canvas"></div>
</div>
<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-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_live_video"><i class="fa fa-fw fa-television"></i><span data-i18n="main_ledsim_btn_togglelivevideo">live video</span></button>
</div>
</div>
<!-- ledsimDialog -->
<div id="ledsim_dialog" style="display:none; z-index:9998">
<div data-role="body" style="padding:0px;">
<div id="ledsim_text"></div>
<div id="leds_canvas"></div>
</div>
<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-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>
</div>
</div>
<!-- Bootstrap Core JavaScript -->
<script src="js/lib/bootstrap.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="js/lib/bootstrap.min.js"></script>
<!-- BSToggle -->
<script src="js/lib/bootstrap-toggle.min.js"></script>
<!-- BSToggle -->
<script src="js/lib/bootstrap-toggle.min.js"></script>
<!-- Bootstrap Select JavaScript -->
<script src="js/lib/bootstrap-select.min.js"></script>
<!-- Bootstrap Select JavaScript -->
<script src="js/lib/bootstrap-select.min.js"></script>
<!-- Metis Menu Plugin JavaScript -->
<script src="js/lib/metisMenu.min.js"></script>
<!-- Metis Menu Plugin JavaScript -->
<script src="js/lib/metisMenu.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="js/lib/sb-admin-2.js"></script>
<!-- Custom Theme JavaScript -->
<script src="js/lib/sb-admin-2.js"></script>
<script src="js/content_index.js"></script>
<script src="js/settings.js"></script>
<script src="js/streamer.js"></script>
<script src="js/wizard.js"></script>
<script src="js/content_index.js"></script>
<script src="js/settings.js"></script>
<script src="js/streamer.js"></script>
<script src="js/wizard.js"></script>
<!--gijgo dialog-->
<script src="js/lib/draggable.min.js"></script>
<script src="js/lib/dialog.min.js"></script>
<script src="js/ledsim.js"></script>
<!--gijgo dialog-->
<script src="js/lib/draggable.min.js"></script>
<script src="js/lib/dialog.min.js"></script>
<script src="js/ledsim.js"></script>
<!--Client-side download-->
<script src='js/lib/download.min.js'></script>
<!--Client-side download-->
<script src='js/lib/download.min.js'></script>
<!--JsonAceEditor-->
<script src='js/lib/jsonaceeditor.min.js'></script>
<link href="css/jsonaceeditor.min.css" rel="stylesheet" type="text/css">
<!--JsonAceEditor-->
<script src='js/lib/jsonaceeditor.min.js'></script>
<link href="css/jsonaceeditor.min.css" rel="stylesheet" type="text/css">
</body>
</html>

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 += '</div></div>';
instances_html += '<div class="panel-body">';
instances_html += '<table class="table borderless">';
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 += '<span>' + $.i18n('dashboard_infobox_label_title') + '</span>';
instances_html += '</th></tr></thead>';
instances_html += '<tbody><tr><td></td>';
instances_html += '<td>' + $.i18n('conf_leds_contr_label_contrtype') + '</td>';
instances_html += '<td style="text-align:right">' + window.serverConfig.device.type + '</td>';
instances_html += '</tr><tr></tbody></table>';
instances_html += '<tbody>';
instances_html += '<tr><td></td><td>' + $.i18n('conf_leds_contr_label_contrtype') + '</td>';
instances_html += '<td style="text-align:right; padding-right:0">';
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 += '<thead><tr><th colspan="3">';
@ -50,28 +52,35 @@ $(document).ready(function () {
instances_html += '<span>' + $.i18n('dashboard_componentbox_label_title') + '</span>';
instances_html += '</th></tr></thead>';
var tab_components = "";
var componentBtn = "";
var instance_components = "";
for (var idx = 0; idx < components.length; idx++) {
if (components[idx].name != "ALL") {
var comp_enabled = components[idx].enabled ? "checked" : "";
const general_comp = "general_comp_" + components[idx].name;
var componentBtn = '<input ' +
'id="' + general_comp + '" ' + comp_enabled +
' type="checkbox" ' +
'data-toggle="toggle" ' +
'data-size="mini" ' +
'data-onstyle="success" ' +
'data-on="' + $.i18n('general_btn_on') + '" ' +
'data-off="' + $.i18n('general_btn_off') + '">';
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;
tab_components += '<tr><td></td><td>' + $.i18n('general_comp_' + components[idx].name) + '</td><td style="text-align:right">' + componentBtn + '</td></tr>';
var comp_enabled = components[idx].enabled ? "checked" : "";
const general_comp = "general_comp_" + components[idx].name;
componentBtn = '<input ' +
'id="' + general_comp + '" ' + comp_enabled +
' type="checkbox" ' +
'data-toggle="toggle" ' +
'data-size="mini" ' +
'data-onstyle="success" ' +
'data-on="' + $.i18n('general_btn_on') + '" ' +
'data-off="' + $.i18n('general_btn_off') + '">';
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').prepend(instances_html);
updateUiOnInstance(window.currentHyperionInstance);
updateHyperionInstanceListing();
@ -83,7 +92,7 @@ $(document).ready(function () {
for (var idx = 0; idx < components.length; idx++) {
if (components[idx].name != "ALL") {
$("#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 => {
requestSetComponentState(e.currentTarget.id.split('_')[2], e.currentTarget.checked);
});
@ -92,6 +101,12 @@ $(document).ready(function () {
}
// 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');
$('#dash_fbPort').html(fbPort);
var pbPort = window.serverConfig.protoServer.enable ? window.serverConfig.protoServer.port : $.i18n('general_disabled');

View File

@ -1,196 +1,182 @@
$(document).ready( function() {
performTranslation();
$(document).ready(function () {
performTranslation();
var importedConf;
var confName;
var conf_editor = null;
var importedConf;
var confName;
var conf_editor = null;
$('#conf_cont').append(createOptPanel('fa-wrench', $.i18n("edt_conf_gen_heading_title"), 'editor_container', 'btn_submit', 'panel-system'));
if(window.showOptHelp)
{
$('#conf_cont').append(createHelpTable(window.schema.general.properties, $.i18n("edt_conf_gen_heading_title")));
}
else
$('#conf_imp').appendTo('#conf_cont');
$('#conf_cont').append(createOptPanel('fa-wrench', $.i18n("edt_conf_gen_heading_title"), 'editor_container', 'btn_submit', 'panel-system'));
if (window.showOptHelp) {
$('#conf_cont').append(createHelpTable(window.schema.general.properties, $.i18n("edt_conf_gen_heading_title")));
}
else
$('#conf_imp').appendTo('#conf_cont');
conf_editor = createJsonEditor('editor_container', {
general: window.schema.general
}, true, true);
conf_editor = createJsonEditor('editor_container', {
general: window.schema.general
}, true, true);
conf_editor.on('change',function() {
conf_editor.validate().length || window.readOnlyMode ? $('#btn_submit').attr('disabled', true) : $('#btn_submit').attr('disabled', false);
});
conf_editor.on('change', function () {
conf_editor.validate().length || window.readOnlyMode ? $('#btn_submit').attr('disabled', true) : $('#btn_submit').attr('disabled', false);
});
$('#btn_submit').off().on('click',function() {
requestWriteConfig(conf_editor.getValue());
});
$('#btn_submit').off().on('click', function () {
window.showOptHelp = conf_editor.getEditor("root.general.showOptHelp").getValue();
requestWriteConfig(conf_editor.getValue());
});
// Instance handling
function handleInstanceRename(e)
{
// Instance handling
function handleInstanceRename(e) {
conf_editor.on('change',function() {
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);
});
conf_editor.on('change', function () {
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);
});
var inst = e.currentTarget.id.split("_")[1];
showInfoDialog('renInst', $.i18n('conf_general_inst_renreq_t'), getInstanceNameByIndex(inst));
var inst = e.currentTarget.id.split("_")[1];
showInfoDialog('renInst', $.i18n('conf_general_inst_renreq_t'), getInstanceNameByIndex(inst));
$("#id_btn_ok").off().on('click', function(){
requestInstanceRename(inst, $('#renInst_name').val())
});
$("#id_btn_ok").off().on('click', function () {
requestInstanceRename(inst, $('#renInst_name').val())
});
$('#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);
});
}
$('#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);
});
}
function handleInstanceDelete(e)
{
var inst = e.currentTarget.id.split("_")[1];
showInfoDialog('delInst',$.i18n('conf_general_inst_delreq_h'),$.i18n('conf_general_inst_delreq_t',getInstanceNameByIndex(inst)));
$("#id_btn_yes").off().on('click', function(){
requestInstanceDelete(inst)
});
}
function handleInstanceDelete(e) {
var inst = e.currentTarget.id.split("_")[1];
showInfoDialog('delInst', $.i18n('conf_general_inst_delreq_h'), $.i18n('conf_general_inst_delreq_t', getInstanceNameByIndex(inst)));
$("#id_btn_yes").off().on('click', function () {
requestInstanceDelete(inst)
});
}
function buildInstanceList()
{
var inst = serverInfo.instance
$('.itbody').html("");
for(var key in inst)
{
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 startBtn = ""
var delBtn = "";
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>';
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')+'">';
function buildInstanceList() {
var inst = serverInfo.instance
$('.itbody').html("");
for (var key in inst) {
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 startBtn = ""
var delBtn = "";
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>';
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));
$('#instren_'+inst[key].instance).off().on('click', handleInstanceRename);
}
$('.itbody').append(createTableRow([inst[key].friendly_name, startBtn, renameBtn, delBtn], false, true));
$('#instren_' + inst[key].instance).off().on('click', handleInstanceRename);
$('#inst_'+inst[key].instance).bootstrapToggle();
$('#inst_'+inst[key].instance).change(e => {
requestInstanceStartStop(e.currentTarget.id.split('_').pop(), e.currentTarget.checked);
});
$('#instdel_'+inst[key].instance).off().on('click', handleInstanceDelete);
$('#inst_' + inst[key].instance).bootstrapToggle();
$('#inst_' + inst[key].instance).change(e => {
requestInstanceStartStop(e.currentTarget.id.split('_').pop(), e.currentTarget.checked);
});
$('#instdel_' + inst[key].instance).off().on('click', handleInstanceDelete);
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 ? $('#instdel_'+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 ? $('#instdel_' + inst[key].instance).attr('disabled', true) : $('#btn_submit').attr('disabled', false);
}
}
createTable('ithead', 'itbody', 'itable');
$('.ithead').html(createTableRow([$.i18n('conf_general_inst_namehead'), "", $.i18n('conf_general_inst_actionhead'), ""], true, true));
buildInstanceList();
createTable('ithead', 'itbody', 'itable');
$('.ithead').html(createTableRow([$.i18n('conf_general_inst_namehead'), "", $.i18n('conf_general_inst_actionhead'), ""], true, true));
buildInstanceList();
$('#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);
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'))
else
$('#inst_chars_needed').html("<br />")
});
$('#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);
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'))
else
$('#inst_chars_needed').html("<br />")
});
$('#btn_create_inst').off().on('click',function(e) {
requestInstanceCreate($('#inst_name').val());
$('#inst_name').val("");
$('#btn_create_inst').attr('disabled', true)
});
$('#btn_create_inst').off().on('click', function (e) {
requestInstanceCreate($('#inst_name').val());
$('#inst_name').val("");
$('#btn_create_inst').attr('disabled', true)
});
$(hyperion).off("instance-updated").on("instance-updated", function(event) {
buildInstanceList()
});
$(hyperion).off("instance-updated").on("instance-updated", function (event) {
buildInstanceList()
});
//import
function dis_imp_btn(state)
{
state || window.readOnlyMode ? $('#btn_import_conf').attr('disabled', true) : $('#btn_import_conf').attr('disabled', false);
}
//import
function dis_imp_btn(state) {
state || window.readOnlyMode ? $('#btn_import_conf').attr('disabled', true) : $('#btn_import_conf').attr('disabled', false);
}
function readFile(evt)
{
var f = evt.target.files[0];
function readFile(evt) {
var f = evt.target.files[0];
if (f)
{
var r = new FileReader();
r.onload = function(e)
{
var content = e.target.result.replace(/[^:]?\/\/.*/g, ''); //remove Comments
if (f) {
var r = new FileReader();
r.onload = function (e) {
var content = e.target.result.replace(/[^:]?\/\/.*/g, ''); //remove Comments
//check file is json
var check = isJsonString(content);
if(check.length != 0)
{
showInfoDialog('error', "", $.i18n('infoDialog_import_jsonerror_text', f.name, JSON.stringify(check)));
dis_imp_btn(true);
}
else
{
content = JSON.parse(content);
//check for hyperion json
if(typeof content.leds === 'undefined' || typeof content.general === 'undefined')
{
showInfoDialog('error', "", $.i18n('infoDialog_import_hyperror_text', f.name));
dis_imp_btn(true);
}
else
{
dis_imp_btn(false);
importedConf = content;
confName = f.name;
}
}
}
r.readAsText(f);
}
}
//check file is json
var check = isJsonString(content);
if (check.length != 0) {
showInfoDialog('error', "", $.i18n('infoDialog_import_jsonerror_text', f.name, JSON.stringify(check)));
dis_imp_btn(true);
}
else {
content = JSON.parse(content);
//check for hyperion json
if (typeof content.leds === 'undefined' || typeof content.general === 'undefined') {
showInfoDialog('error', "", $.i18n('infoDialog_import_hyperror_text', f.name));
dis_imp_btn(true);
}
else {
dis_imp_btn(false);
importedConf = content;
confName = f.name;
}
}
}
r.readAsText(f);
}
}
$('#btn_import_conf').off().on('click', function(){
showInfoDialog('import', $.i18n('infoDialog_import_confirm_title'), $.i18n('infoDialog_import_confirm_text', confName));
$('#btn_import_conf').off().on('click', function () {
showInfoDialog('import', $.i18n('infoDialog_import_confirm_title'), $.i18n('infoDialog_import_confirm_text', confName));
$('#id_btn_import').off().on('click', function(){
requestWriteConfig(importedConf, true);
setTimeout(initRestart, 100);
});
});
$('#id_btn_import').off().on('click', function () {
requestWriteConfig(importedConf, true);
setTimeout(initRestart, 100);
});
});
$('#select_import_conf').off().on('change', function(e){
if (window.File && window.FileReader && window.FileList && window.Blob)
readFile(e);
else
showInfoDialog('error', "", $.i18n('infoDialog_import_comperror_text'));
});
$('#select_import_conf').off().on('change', function (e) {
if (window.File && window.FileReader && window.FileList && window.Blob)
readFile(e);
else
showInfoDialog('error', "", $.i18n('infoDialog_import_comperror_text'));
});
//export
$('#btn_export_conf').off().on('click', function(){
var name = window.serverConfig.general.name;
//export
$('#btn_export_conf').off().on('click', function () {
var name = window.serverConfig.general.name;
var d = new Date();
var month = d.getMonth()+1;
var day = d.getDate();
var d = new Date();
var month = d.getMonth() + 1;
var day = d.getDate();
var timestamp = d.getFullYear() + '.' +
(month<10 ? '0' : '') + month + '.' +
(day<10 ? '0' : '') + day;
var timestamp = d.getFullYear() + '.' +
(month < 10 ? '0' : '') + month + '.' +
(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
if(window.showOptHelp)
{
createHint("intro", $.i18n('conf_general_intro'), "editor_container");
createHint("intro", $.i18n('conf_general_tok_desc'), "tok_desc_cont");
createHint("intro", $.i18n('conf_general_inst_desc'), "inst_desc_cont");
}
//create introduction
if (window.showOptHelp) {
createHint("intro", $.i18n('conf_general_intro'), "editor_container");
createHint("intro", $.i18n('conf_general_tok_desc'), "tok_desc_cont");
createHint("intro", $.i18n('conf_general_inst_desc'), "inst_desc_cont");
}
removeOverlay();
removeOverlay();
});

1055
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).scroll(function(){
$(window).scroll(function() {
if ($(window).scrollTop() > 65)
$("#navbar_brand_logo").css("display", "none");
else
$("#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() {
@ -349,7 +354,7 @@ $("#btn_darkmode").off().on("click", function (e) {
});
// Menuitem toggle;
function SwitchToMenuItem(target) {
function SwitchToMenuItem(target, item) {
document.getElementById(target).click(); // Get <a href menu item;
let sidebar = $('#side-menu'); // Get sidebar menu;
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).addClass('active'); // Add active state by classname;
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 colorOrderDefault = "rgb";
$('#btn_test_controller').hide();
switch (ledType) {
case "cololight":
case "wled":
@ -769,11 +771,38 @@ $(document).ready(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 canSave = false;
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 "wled":
var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue();
@ -797,48 +826,20 @@ $(document).ready(function () {
}
}
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:
canIdentify = false;
canSave = true;
}
if (canIdentify) {
$("#btn_test_controller").removeClass('hidden');
$('#btn_test_controller').attr('disabled', false);
}
else {
$('#btn_test_controller').attr('disabled', true);
}
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
if (hardwareLedCount < 1) {
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);
}
} else {
canSave = false;
}
@ -886,9 +887,9 @@ $(document).ready(function () {
conf_editor.getEditor(specOptPath + "host").setValue(val);
break;
}
}
showAllDeviceInputOptions("hostList", showOptions);
showAllDeviceInputOptions("hostList", showOptions);
}
});
conf_editor.watch('root.specificOptions.host', () => {
@ -900,8 +901,10 @@ $(document).ready(function () {
else {
let params = {};
switch (ledType) {
case "cololight":
params = { host: host };
getProperties_device(ledType, host, params);
break;
case "nanoleaf":
@ -910,33 +913,70 @@ $(document).ready(function () {
return;
}
params = { host: host, token: token };
getProperties_device(ledType, host, params);
break;
case "wled":
params = { host: host, filter: "info" };
getProperties_device(ledType, host, params);
break;
default:
}
getProperties_device(ledType, host, params);
}
});
conf_editor.watch('root.specificOptions.output', () => {
var output = conf_editor.getEditor("root.specificOptions.output").getValue();
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);
showAllDeviceInputOptions("output", false);
}
else {
showAllDeviceInputOptions("output", true);
let params = {};
var canIdentify = false;
switch (ledType) {
case "adalight":
canIdentify = true;
break;
case "atmo":
case "karate":
params = { serialPort: output };
getProperties_device(ledType, output, params);
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;
}
//Rewrite whole LED & Layout configuration, in case changes were done accross tabs and no default layout
if (genDefLayout !== true) {
result.ledConfig = getLedConfig();
@ -1269,8 +1311,6 @@ var updateSelectList = function (ledType, discoveryInfo) {
ledTypeGroup = "devRPiGPIO";
}
var specOpt = conf_editor.getEditor('root.specificOptions'); // get specificOptions of the editor
switch (ledTypeGroup) {
case "devNET":
key = "hostList";
@ -1434,11 +1474,14 @@ var updateSelectList = function (ledType, discoveryInfo) {
}
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) {
$('#btn_submit_controller').attr('disabled', true);
const result = await requestLedDeviceDiscovery(ledType, params);
var discoveryResult;
@ -1479,6 +1522,7 @@ async function getProperties_device(ledType, key, params) {
}
else {
$('#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.ledCount.length > 0) {
var configuredLedCount = window.serverConfig.device.hardwareLedCount;
var generalOpt = conf_editor.getEditor('root.generalOptions');
updateJsonEditorSelection(generalOpt, "hardwareLedCount", {}, ledProperties.ledCount, [], configuredLedCount);
updateJsonEditorSelection(conf_editor, 'root.generalOptions', "hardwareLedCount", {}, ledProperties.ledCount, [], configuredLedCount);
}
}
break;

View File

@ -25,7 +25,7 @@ $(document).ready(function () {
$('#btn_submit').off().on('click', function () {
var displayedLogLevel = conf_editor.getEditor("root.logger.level").getValue();
var newLogLevel = {logger:{}};
var newLogLevel = { logger: {} };
newLogLevel.logger.level = displayedLogLevel;
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 () {
const temp = document.createElement('textarea');
@ -153,7 +153,7 @@ $(document).ready(function () {
$(window.hyperion).on("cmd-logging-update", function (event) {
var messages = (event.response.result.messages);
var messages = (event.response.result.messages);
if (messages.length != 0) {
if (!createdCont) {

View File

@ -7,7 +7,7 @@ $(document).ready(function () {
var oldEffects = [];
var cpcolor = '#B500FF';
var mappingList = window.serverSchema.properties.color.properties.imageToLedMappingType.enum;
var duration = 0;
var duration = ENDLESS;
var rgb = { r: 255, g: 0, b: 0 };
var lastImgData = "";
var lastFileName = "";
@ -201,7 +201,9 @@ $(document).ready(function () {
});
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;
const enable_style = (comp.enabled ? "checked" : "");

View File

@ -33,37 +33,39 @@ window.comps = [];
window.defaultPasswordIsSet = null;
tokenList = {};
const ENDLESS = -1;
function initRestart()
{
$(window.hyperion).off();
requestServerConfigReload();
window.watchdog = 10;
connectionLostDetection('restart');
$(window.hyperion).off();
requestServerConfigReload();
window.watchdog = 10;
connectionLostDetection('restart');
}
function connectionLostDetection(type)
{
if ( window.watchdog > 2 )
{
var interval_id = window.setInterval(function(){clearInterval(interval_id);}, 9999); // Get a reference to the last
for (var i = 1; i < interval_id; i++)
window.clearInterval(i);
if(type == 'restart')
{
$("body").html($("#container_restart").html());
// setTimeout delay for probably slower systems, some browser don't execute THIS action
setTimeout(restartAction,250);
}
else
{
$("body").html($("#container_connection_lost").html());
connectionLostAction();
}
}
else
{
$.get( "/cgi/cfg_jsonserver", function() {window.watchdog=0}).fail(function() {window.watchdog++;});
}
if ( window.watchdog > 2 )
{
var interval_id = window.setInterval(function(){clearInterval(interval_id);}, 9999); // Get a reference to the last
for (var i = 1; i < interval_id; i++)
window.clearInterval(i);
if(type == 'restart')
{
$("body").html($("#container_restart").html());
// setTimeout delay for probably slower systems, some browser don't execute THIS action
setTimeout(restartAction,250);
}
else
{
$("body").html($("#container_connection_lost").html());
connectionLostAction();
}
}
else
{
$.get( "/cgi/cfg_jsonserver", function() {window.watchdog=0}).fail(function() {window.watchdog++;});
}
}
setInterval(connectionLostDetection, 3000);
@ -72,107 +74,107 @@ setInterval(connectionLostDetection, 3000);
function initWebSocket()
{
if ("WebSocket" in window)
{
if (window.websocket == null)
{
window.jsonPort = '';
if(document.location.port == '' && document.location.protocol == "http:")
window.jsonPort = '80';
else if (document.location.port == '' && document.location.protocol == "https:")
window.jsonPort = '443';
else
window.jsonPort = document.location.port;
window.websocket = (document.location.protocol == "https:") ? new WebSocket('wss://'+document.location.hostname+":"+window.jsonPort) : new WebSocket('ws://'+document.location.hostname+":"+window.jsonPort);
if ("WebSocket" in window)
{
if (window.websocket == null)
{
window.jsonPort = '';
if(document.location.port == '' && document.location.protocol == "http:")
window.jsonPort = '80';
else if (document.location.port == '' && document.location.protocol == "https:")
window.jsonPort = '443';
else
window.jsonPort = document.location.port;
window.websocket = (document.location.protocol == "https:") ? new WebSocket('wss://'+document.location.hostname+":"+window.jsonPort) : new WebSocket('ws://'+document.location.hostname+":"+window.jsonPort);
window.websocket.onopen = function (event) {
$(window.hyperion).trigger({type:"open"});
window.websocket.onopen = function (event) {
$(window.hyperion).trigger({type:"open"});
$(window.hyperion).on("cmd-serverinfo", function(event) {
window.watchdog = 0;
});
};
$(window.hyperion).on("cmd-serverinfo", function(event) {
window.watchdog = 0;
});
};
window.websocket.onclose = function (event) {
// See http://tools.ietf.org/html/rfc6455#section-7.4.1
var reason;
switch(event.code)
{
case 1000: reason = "Normal closure, meaning that the purpose for which the connection was established has been fulfilled."; break;
case 1001: reason = "An endpoint is \"going away\", such as a server going down or a browser having navigated away from a page."; break;
case 1002: reason = "An endpoint is terminating the connection due to a protocol error"; break;
case 1003: reason = "An endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message)."; break;
case 1004: reason = "Reserved. The specific meaning might be defined in the future."; break;
case 1005: reason = "No status code was actually present."; break;
case 1006: reason = "The connection was closed abnormally, e.g., without sending or receiving a Close control frame"; break;
case 1007: reason = "An endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 [http://tools.ietf.org/html/rfc3629] data within a text message)."; break;
case 1008: reason = "An endpoint is terminating the connection because it has received a message that \"violates its policy\". This reason is given either if there is no other sutible reason, or if there is a need to hide specific details about the policy."; break;
case 1009: reason = "An endpoint is terminating the connection because it has received a message that is too big for it to process."; break;
case 1010: reason = "An endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake. <br /> Specifically, the extensions that are needed are: " + event.reason; break;
case 1011: reason = "A server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request."; break;
case 1015: reason = "The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified)."; break;
default: reason = "Unknown reason";
}
$(window.hyperion).trigger({type:"close", reason:reason});
window.watchdog = 10;
connectionLostDetection();
};
window.websocket.onclose = function (event) {
// See http://tools.ietf.org/html/rfc6455#section-7.4.1
var reason;
switch(event.code)
{
case 1000: reason = "Normal closure, meaning that the purpose for which the connection was established has been fulfilled."; break;
case 1001: reason = "An endpoint is \"going away\", such as a server going down or a browser having navigated away from a page."; break;
case 1002: reason = "An endpoint is terminating the connection due to a protocol error"; break;
case 1003: reason = "An endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message)."; break;
case 1004: reason = "Reserved. The specific meaning might be defined in the future."; break;
case 1005: reason = "No status code was actually present."; break;
case 1006: reason = "The connection was closed abnormally, e.g., without sending or receiving a Close control frame"; break;
case 1007: reason = "An endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 [http://tools.ietf.org/html/rfc3629] data within a text message)."; break;
case 1008: reason = "An endpoint is terminating the connection because it has received a message that \"violates its policy\". This reason is given either if there is no other sutible reason, or if there is a need to hide specific details about the policy."; break;
case 1009: reason = "An endpoint is terminating the connection because it has received a message that is too big for it to process."; break;
case 1010: reason = "An endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake. <br /> Specifically, the extensions that are needed are: " + event.reason; break;
case 1011: reason = "A server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request."; break;
case 1015: reason = "The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified)."; break;
default: reason = "Unknown reason";
}
$(window.hyperion).trigger({type:"close", reason:reason});
window.watchdog = 10;
connectionLostDetection();
};
window.websocket.onmessage = function (event) {
try
{
var response = JSON.parse(event.data);
var success = response.success;
var cmd = response.command;
var tan = response.tan
if (success || typeof(success) == "undefined")
{
$(window.hyperion).trigger({type:"cmd-"+cmd, response:response});
}
else
{
// skip tan -1 error handling
if(tan != -1){
var error = response.hasOwnProperty("error")? response.error : "unknown";
$(window.hyperion).trigger({type:"error",reason:error});
console.log("[window.websocket::onmessage] ",error)
}
}
}
catch(exception_error)
{
$(window.hyperion).trigger({type:"error",reason:exception_error});
console.log("[window.websocket::onmessage] ",exception_error)
}
};
window.websocket.onmessage = function (event) {
try
{
var response = JSON.parse(event.data);
var success = response.success;
var cmd = response.command;
var tan = response.tan
if (success || typeof(success) == "undefined")
{
$(window.hyperion).trigger({type:"cmd-"+cmd, response:response});
}
else
{
// skip tan -1 error handling
if(tan != -1){
var error = response.hasOwnProperty("error")? response.error : "unknown";
$(window.hyperion).trigger({type:"error",reason:error});
console.log("[window.websocket::onmessage] ",error)
}
}
}
catch(exception_error)
{
$(window.hyperion).trigger({type:"error",reason:exception_error});
console.log("[window.websocket::onmessage] ",exception_error)
}
};
window.websocket.onerror = function (error) {
$(window.hyperion).trigger({type:"error",reason:error});
console.log("[window.websocket::onerror] ",error)
};
}
}
else
{
$(window.hyperion).trigger("error");
alert("Websocket is not supported by your browser");
return;
}
window.websocket.onerror = function (error) {
$(window.hyperion).trigger({type:"error",reason:error});
console.log("[window.websocket::onerror] ",error)
};
}
}
else
{
$(window.hyperion).trigger("error");
alert("Websocket is not supported by your browser");
return;
}
}
function sendToHyperion(command, subcommand, msg)
{
if (typeof subcommand != 'undefined' && subcommand.length > 0)
subcommand = ',"subcommand":"'+subcommand+'"';
else
subcommand = "";
if (typeof subcommand != 'undefined' && subcommand.length > 0)
subcommand = ',"subcommand":"'+subcommand+'"';
else
subcommand = "";
if (typeof msg != 'undefined' && msg.length > 0)
msg = ","+msg;
else
msg = "";
if (typeof msg != 'undefined' && msg.length > 0)
msg = ","+msg;
else
msg = "";
window.websocket.send('{"command":"'+command+'", "tan":'+window.wsTan+subcommand+msg+'}');
window.websocket.send('{"command":"'+command+'", "tan":'+window.wsTan+subcommand+msg+'}');
}
// Send a json message to Hyperion and wait for a matching response
@ -228,250 +230,256 @@ async function __sendAsync (data) {
// Test if admin requires authentication
function requestRequiresAdminAuth()
{
sendToHyperion("authorize","adminRequired");
sendToHyperion("authorize","adminRequired");
}
// Test if the default password needs to be changed
function requestRequiresDefaultPasswortChange()
{
sendToHyperion("authorize","newPasswordRequired");
sendToHyperion("authorize","newPasswordRequired");
}
// Change password
function requestChangePassword(oldPw, newPw)
{
sendToHyperion("authorize","newPassword",'"password": "'+oldPw+'", "newPassword":"'+newPw+'"');
sendToHyperion("authorize","newPassword",'"password": "'+oldPw+'", "newPassword":"'+newPw+'"');
}
function requestAuthorization(password)
{
sendToHyperion("authorize","login",'"password": "' + password + '"');
sendToHyperion("authorize","login",'"password": "' + password + '"');
}
function requestTokenAuthorization(token)
{
sendToHyperion("authorize","login",'"token": "' + token + '"');
sendToHyperion("authorize","login",'"token": "' + token + '"');
}
function requestToken(comment)
{
sendToHyperion("authorize","createToken",'"comment": "'+comment+'"');
sendToHyperion("authorize","createToken",'"comment": "'+comment+'"');
}
function requestTokenInfo()
{
sendToHyperion("authorize","getTokenList","");
sendToHyperion("authorize","getTokenList","");
}
function requestGetPendingTokenRequests (id, state) {
sendToHyperion("authorize", "getPendingTokenRequests", "");
sendToHyperion("authorize", "getPendingTokenRequests", "");
}
function requestHandleTokenRequest(id, state)
{
sendToHyperion("authorize","answerRequest",'"id":"'+id+'", "accept":'+state);
sendToHyperion("authorize","answerRequest",'"id":"'+id+'", "accept":'+state);
}
function requestTokenDelete(id)
{
sendToHyperion("authorize","deleteToken",'"id":"'+id+'"');
sendToHyperion("authorize","deleteToken",'"id":"'+id+'"');
}
function requestInstanceRename(inst, name)
{
sendToHyperion("instance", "saveName",'"instance": '+inst+', "name": "'+name+'"');
sendToHyperion("instance", "saveName",'"instance": '+inst+', "name": "'+name+'"');
}
function requestInstanceStartStop(inst, start)
{
if(start)
sendToHyperion("instance","startInstance",'"instance": '+inst);
else
sendToHyperion("instance","stopInstance",'"instance": '+inst);
if(start)
sendToHyperion("instance","startInstance",'"instance": '+inst);
else
sendToHyperion("instance","stopInstance",'"instance": '+inst);
}
function requestInstanceDelete(inst)
{
sendToHyperion("instance","deleteInstance",'"instance": '+inst);
sendToHyperion("instance","deleteInstance",'"instance": '+inst);
}
function requestInstanceCreate(name)
{
sendToHyperion("instance","createInstance",'"name": "'+name+'"');
sendToHyperion("instance","createInstance",'"name": "'+name+'"');
}
function requestInstanceSwitch(inst)
{
sendToHyperion("instance","switchTo",'"instance": '+inst);
sendToHyperion("instance","switchTo",'"instance": '+inst);
}
function requestServerInfo()
{
sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update", "instance-update"]');
sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update", "instance-update"]');
}
function requestSysInfo()
{
sendToHyperion("sysinfo");
sendToHyperion("sysinfo");
}
function requestServerConfigSchema()
{
sendToHyperion("config","getschema");
sendToHyperion("config","getschema");
}
function requestServerConfig()
{
sendToHyperion("config", "getconfig");
sendToHyperion("config", "getconfig");
}
function requestServerConfigReload()
{
sendToHyperion("config", "reload");
sendToHyperion("config", "reload");
}
function requestLedColorsStart()
{
window.ledStreamActive=true;
sendToHyperion("ledcolors", "ledstream-start");
window.ledStreamActive=true;
sendToHyperion("ledcolors", "ledstream-start");
}
function requestLedColorsStop()
{
window.ledStreamActive=false;
sendToHyperion("ledcolors", "ledstream-stop");
window.ledStreamActive=false;
sendToHyperion("ledcolors", "ledstream-stop");
}
function requestLedImageStart()
{
window.imageStreamActive=true;
sendToHyperion("ledcolors", "imagestream-start");
window.imageStreamActive=true;
sendToHyperion("ledcolors", "imagestream-start");
}
function requestLedImageStop()
{
window.imageStreamActive=false;
sendToHyperion("ledcolors", "imagestream-stop");
window.imageStreamActive=false;
sendToHyperion("ledcolors", "imagestream-stop");
}
function requestPriorityClear(prio)
{
if(typeof prio !== 'number')
prio = window.webPrio;
if(typeof prio !== 'number')
prio = window.webPrio;
sendToHyperion("clear", "", '"priority":'+prio+'');
sendToHyperion("clear", "", '"priority":'+prio+'');
}
function requestClearAll()
{
requestPriorityClear(-1)
requestPriorityClear(-1)
}
function requestPlayEffect(effectName, duration)
{
sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'"},"priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"');
sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'"},"priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"');
}
function requestSetColor(r,g,b,duration)
{
sendToHyperion("color", "", '"color":['+r+','+g+','+b+'], "priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"');
sendToHyperion("color", "", '"color":['+r+','+g+','+b+'], "priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"');
}
function requestSetImage(data,duration,name)
{
sendToHyperion("image", "", '"imagedata":"'+data+'", "priority":'+window.webPrio+',"duration":'+validateDuration(duration)+', "format":"auto", "origin":"'+window.webOrigin+'", "name":"'+name+'"');
sendToHyperion("image", "", '"imagedata":"'+data+'", "priority":'+window.webPrio+',"duration":'+validateDuration(duration)+', "format":"auto", "origin":"'+window.webOrigin+'", "name":"'+name+'"');
}
function requestSetComponentState(comp, state)
{
var state_str = state ? "true" : "false";
sendToHyperion("componentstate", "", '"componentstate":{"component":"'+comp+'","state":'+state_str+'}');
var state_str = state ? "true" : "false";
sendToHyperion("componentstate", "", '"componentstate":{"component":"'+comp+'","state":'+state_str+'}');
}
function requestSetSource(prio)
{
if ( prio == "auto" )
sendToHyperion("sourceselect", "", '"auto":true');
else
sendToHyperion("sourceselect", "", '"priority":'+prio);
if ( prio == "auto" )
sendToHyperion("sourceselect", "", '"auto":true');
else
sendToHyperion("sourceselect", "", '"priority":'+prio);
}
function requestWriteConfig(config, full)
{
if(full === true)
window.serverConfig = config;
else
{
jQuery.each(config, function(i, val) {
window.serverConfig[i] = val;
});
}
if(full === true)
window.serverConfig = config;
else
{
jQuery.each(config, function(i, val) {
window.serverConfig[i] = val;
});
}
sendToHyperion("config","setconfig", '"config":'+JSON.stringify(window.serverConfig));
sendToHyperion("config","setconfig", '"config":'+JSON.stringify(window.serverConfig));
}
function requestWriteEffect(effectName,effectPy,effectArgs,data)
{
var cutArgs = effectArgs.slice(1, -1);
sendToHyperion("create-effect", "", '"name":"'+effectName+'", "script":"'+effectPy+'", '+cutArgs+',"imageData":"'+data+'"');
var cutArgs = effectArgs.slice(1, -1);
sendToHyperion("create-effect", "", '"name":"'+effectName+'", "script":"'+effectPy+'", '+cutArgs+',"imageData":"'+data+'"');
}
function requestTestEffect(effectName,effectPy,effectArgs,data)
{
sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'", "args":'+effectArgs+'}, "priority":'+window.webPrio+', "origin":"'+window.webOrigin+'", "pythonScript":"'+effectPy+'", "imageData":"'+data+'"');
sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'", "args":'+effectArgs+'}, "priority":'+window.webPrio+', "origin":"'+window.webOrigin+'", "pythonScript":"'+effectPy+'", "imageData":"'+data+'"');
}
function requestDeleteEffect(effectName)
{
sendToHyperion("delete-effect", "", '"name":"'+effectName+'"');
sendToHyperion("delete-effect", "", '"name":"'+effectName+'"');
}
function requestLoggingStart()
{
window.loggingStreamActive=true;
sendToHyperion("logging", "start");
window.loggingStreamActive=true;
sendToHyperion("logging", "start");
}
function requestLoggingStop()
{
window.loggingStreamActive=false;
sendToHyperion("logging", "stop");
window.loggingStreamActive=false;
sendToHyperion("logging", "stop");
}
function requestMappingType(type)
{
sendToHyperion("processing", "", '"mappingType": "'+type+'"');
sendToHyperion("processing", "", '"mappingType": "'+type+'"');
}
function requestVideoMode(newMode)
{
sendToHyperion("videomode", "", '"videoMode": "'+newMode+'"');
sendToHyperion("videomode", "", '"videoMode": "'+newMode+'"');
}
function requestAdjustment(type, value, complete)
{
if(complete === true)
sendToHyperion("adjustment", "", '"adjustment": '+type+'');
else
sendToHyperion("adjustment", "", '"adjustment": {"'+type+'": '+value+'}');
if(complete === true)
sendToHyperion("adjustment", "", '"adjustment": '+type+'');
else
sendToHyperion("adjustment", "", '"adjustment": {"'+type+'": '+value+'}');
}
async function requestLedDeviceDiscovery(type, params)
{
let data = { ledDeviceType: type, params: params };
let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "discover", data, Math.floor(Math.random() * 1000) );
return sendAsyncToHyperion("leddevice", "discover", data, Math.floor(Math.random() * 1000) );
}
async function requestLedDeviceProperties(type, params)
{
let data = { ledDeviceType: type, params: params };
let data = { ledDeviceType: type, params: params };
return sendAsyncToHyperion("leddevice", "getProperties", data, Math.floor(Math.random() * 1000));
return sendAsyncToHyperion("leddevice", "getProperties", data, Math.floor(Math.random() * 1000));
}
function requestLedDeviceIdentification(type, params)
{
//sendToHyperion("leddevice", "identify", '"ledDeviceType": "'+type+'","params": '+JSON.stringify(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,279 +1,329 @@
$(document).ready(function() {
var modalOpened = false;
var ledsim_width = 540;
var ledsim_height = 489;
var dialog;
var leds;
var lC = false;
var imageCanvasNodeCtx;
var ledsCanvasNodeCtx;
var canvas_height;
var canvas_width;
var twoDPaths = [];
var toggleLeds, toggleLedsNum = false;
$(document).ready(function () {
var modalOpened = false;
var ledsim_width = 540;
var ledsim_height = 489;
var dialog;
var leds;
var grabberConfig;
var lC = false;
var imageCanvasNodeCtx;
var ledsCanvasNodeCtx;
var canvas_height;
var canvas_width;
var twoDPaths = [];
var toggleLeds = false;
var toggleLedsNum = false;
var toggleSigDetectArea = false;
/// add prototype for simple canvas clear() method
CanvasRenderingContext2D.prototype.clear = function(){
this.clearRect(0, 0, this.canvas.width, this.canvas.height)
};
var activeComponent = "";
function create2dPaths(){
twoDPaths = [];
for(var idx=0; idx<leds.length; 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) );
}
}
const image = new Image()
image.src = "img/hyperion/logo_negativ.png",
/**
* Draws a rounded rectangle into a new Path2D object, returns the created path.
* If you omit the last three params, it will draw a rectangle
* outline with a 5 pixel border radius
* @param {Number} x The top left x coordinate
* @param {Number} y The top left y coordinate
* @param {Number} width The width of the rectangle
* @param {Number} height The height of the rectangle
* @param {Number} [radius = 5] The corner radius; It can also be an object
* to specify different radii for corners
* @param {Number} [radius.tl = 0] Top left
* @param {Number} [radius.tr = 0] Top right
* @param {Number} [radius.br = 0] Bottom right
* @param {Number} [radius.bl = 0] Bottom left
* @return {Path2D} The final path
*/
function build2DPath(x, y, width, height, radius) {
if (typeof radius == 'number') {
radius = {tl: radius, tr: radius, br: radius, bl: radius};
} else {
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
for (var side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side];
}
}
/// add prototype for simple canvas clear() method
CanvasRenderingContext2D.prototype.clear = function () {
this.clearRect(0, 0, this.canvas.width, this.canvas.height)
};
var path = new Path2D();
function create2dPaths() {
twoDPaths = [];
for (var idx = 0; idx < leds.length; 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));
}
}
path.moveTo(x + radius.tl, y);
path.lineTo(x + width - radius.tr, y);
path.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
path.lineTo(x + width, y + height - radius.br);
path.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
path.lineTo(x + radius.bl, y + height);
path.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
path.lineTo(x, y + radius.tl);
path.quadraticCurveTo(x, y, x + radius.tl, y);
/**
* Draws a rounded rectangle into a new Path2D object, returns the created path.
* If you omit the last three params, it will draw a rectangle
* outline with a 5 pixel border radius
* @param {Number} x The top left x coordinate
* @param {Number} y The top left y coordinate
* @param {Number} width The width of the rectangle
* @param {Number} height The height of the rectangle
* @param {Number} [radius = 5] The corner radius; It can also be an object
* to specify different radii for corners
* @param {Number} [radius.tl = 0] Top left
* @param {Number} [radius.tr = 0] Top right
* @param {Number} [radius.br = 0] Bottom right
* @param {Number} [radius.bl = 0] Bottom left
* @return {Path2D} The final path
*/
function build2DPath(x, y, width, height, radius) {
if (typeof radius == 'number') {
radius = { tl: radius, tr: radius, br: radius, bl: radius };
} else {
var defaultRadius = { tl: 0, tr: 0, br: 0, bl: 0 };
for (var side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side];
}
}
return path;
}
var path = new Path2D();
$(window.hyperion).one("ready",function(){
leds = window.serverConfig.leds;
path.moveTo(x + radius.tl, y);
path.lineTo(x + width - radius.tr, y);
path.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
path.lineTo(x + width, y + height - radius.br);
path.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
path.lineTo(x + radius.bl, y + height);
path.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
path.lineTo(x, y + radius.tl);
path.quadraticCurveTo(x, y, x + radius.tl, y);
if(window.showOptHelp)
{
createHint('intro', $.i18n('main_ledsim_text'), 'ledsim_text');
$('#ledsim_text').css({'margin':'10px 15px 0px 15px'});
$('#ledsim_text .bs-callout').css("margin","0px")
}
return path;
}
if(getStorage('ledsim_width') != null)
{
ledsim_width = getStorage('ledsim_width');
ledsim_height = getStorage('ledsim_height');
}
$(window.hyperion).one("ready", function () {
leds = window.serverConfig.leds;
grabberConfig = window.serverConfig.grabberV4L2;
dialog = $("#ledsim_dialog").dialog({
uiLibrary: 'bootstrap',
resizable: true,
modal: false,
minWidth: 250,
width: ledsim_width,
minHeight: 350,
height: ledsim_height,
closeOnEscape: true,
autoOpen: false,
title: $.i18n('main_ledsim_title'),
resize: function (e) {
updateLedLayout();
},
opened: function (e) {
if(!lC)
{
updateLedLayout();
lC = true;
}
modalOpened = true;
requestLedColorsStart();
if (window.showOptHelp) {
createHint('intro', $.i18n('main_ledsim_text'), 'ledsim_text');
$('#ledsim_text').css({ 'margin': '10px 15px 0px 15px' });
$('#ledsim_text .bs-callout').css("margin", "0px")
}
setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-danger", "btn-success");
if($('#leds_toggle_live_video').hasClass('btn-success'))
requestLedImageStart();
},
closed: function (e) {
modalOpened = false;
},
resizeStop: function (e) {
setStorage("ledsim_width", $("#ledsim_dialog").outerWidth());
setStorage("ledsim_height", $("#ledsim_dialog").outerHeight());
}
});
// apply new serverinfos
$(window.hyperion).on("cmd-config-getconfig",function(event){
leds = event.response.info.leds;
updateLedLayout();
});
});
if (getStorage('ledsim_width') != null) {
ledsim_width = getStorage('ledsim_width');
ledsim_height = getStorage('ledsim_height');
}
function printLedsToCanvas(colors)
{
// toggle leds, do not print
if(toggleLeds)
return;
dialog = $("#ledsim_dialog").dialog({
uiLibrary: 'bootstrap',
resizable: true,
modal: false,
minWidth: 250,
width: ledsim_width,
minHeight: 350,
height: ledsim_height,
closeOnEscape: true,
autoOpen: false,
title: $.i18n('main_ledsim_title'),
resize: function (e) {
updateLedLayout();
},
opened: function (e) {
if (!lC) {
updateLedLayout();
lC = true;
}
modalOpened = true;
requestLedColorsStart();
var useColor = false;
var cPos = 0;
ledsCanvasNodeCtx.clear();
if(typeof colors != "undefined")
useColor = true;
setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-danger", "btn-success");
if ($('#leds_toggle_live_video').hasClass('btn-success'))
requestLedImageStart();
},
closed: function (e) {
modalOpened = false;
lC = false;
},
resizeStop: function (e) {
setStorage("ledsim_width", $("#ledsim_dialog").outerWidth());
setStorage("ledsim_height", $("#ledsim_dialog").outerHeight());
}
});
// apply new serverinfos
$(window.hyperion).on("cmd-config-getconfig", function (event) {
leds = event.response.info.leds;
grabberConfig = event.response.info.grabberV4L2;
updateLedLayout();
});
});
// check size of ledcolors with leds length
if(colors && colors.length/3 < leds.length)
return;
function printLedsToCanvas(colors) {
for(var idx=0; idx<leds.length; idx++)
{
var led = leds[idx];
// 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])
//ledsCanvasNodeCtx.fillRect(led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax-led.hmin) * canvas_width, (led.vmax-led.vmin) * canvas_height);
if (grabberConfig.enable && grabberConfig.signalDetection && toggleSigDetectArea && storedAccess === 'expert') {
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.stroke(twoDPaths[idx]);
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));
}
if(toggleLedsNum)
{
//ledsCanvasNodeCtx.shadowOffsetX = 1;
//ledsCanvasNodeCtx.shadowOffsetY = 1;
//ledsCanvasNodeCtx.shadowColor = "black";
//ledsCanvasNodeCtx.shadowBlur = 4;
ledsCanvasNodeCtx.fillStyle = "white";
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));
}
// toggle leds, do not print
if (toggleLeds)
return;
// increment colorsPosition
cPos += 3;
}
}
var useColor = false;
var cPos = 0;
ledsCanvasNodeCtx.clear();
if (typeof colors != "undefined")
useColor = true;
function updateLedLayout()
{
//calculate body size
canvas_height = $('#ledsim_dialog').outerHeight()-$('#ledsim_text').outerHeight()-$('[data-role=footer]').outerHeight()-$('[data-role=header]').outerHeight()-40;
canvas_width = $('#ledsim_dialog').outerWidth()-30;
// check size of ledcolors with leds length
if (colors && colors.length / 3 < leds.length)
return;
$('#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>';
leds_html += '<canvas id="leds_preview_canv" width="'+canvas_width+'" height="'+canvas_height+'" style="position: absolute; left: 0; top: 0; z-index: 99999;"></canvas>';
for (var idx = 0; idx < leds.length; idx++) {
var led = leds[idx];
// 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])
//ledsCanvasNodeCtx.fillRect(led.hmin * canvas_width, led.vmin * canvas_height, (led.hmax-led.hmin) * canvas_width, (led.vmax-led.vmin) * canvas_height);
$('#leds_canvas').html(leds_html);
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.strokeStyle = '#323232';
ledsCanvasNodeCtx.stroke(twoDPaths[idx]);
imageCanvasNodeCtx = document.getElementById("image_preview_canv").getContext("2d");
ledsCanvasNodeCtx = document.getElementById("leds_preview_canv").getContext("2d");
create2dPaths();
printLedsToCanvas();
resetImage();
}
if (toggleLedsNum) {
//ledsCanvasNodeCtx.shadowOffsetX = 1;
//ledsCanvasNodeCtx.shadowOffsetY = 1;
//ledsCanvasNodeCtx.shadowColor = "black";
//ledsCanvasNodeCtx.shadowBlur = 4;
ledsCanvasNodeCtx.fillStyle = "white";
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));
}
// ------------------------------------------------------------------
$('#leds_toggle_num').off().on("click", function() {
toggleLedsNum = !toggleLedsNum
toggleClass('#leds_toggle_num', "btn-danger", "btn-success");
});
// ------------------------------------------------------------------
// increment colorsPosition
cPos += 3;
}
}
$('#leds_toggle').off().on("click", function() {
toggleLeds = !toggleLeds
ledsCanvasNodeCtx.clear();
toggleClass('#leds_toggle', "btn-success", "btn-danger");
});
function updateLedLayout() {
if (grabberConfig.enable && grabberConfig.signalDetection && storedAccess === 'expert') {
$("#sigDetectArea_toggle").show();
} else {
$("#sigDetectArea_toggle").hide();
}
// ------------------------------------------------------------------
$('#leds_toggle_live_video').off().on("click", function() {
setClassByBool('#leds_toggle_live_video',window.imageStreamActive,"btn-success","btn-danger");
if ( window.imageStreamActive )
{
requestLedImageStop();
resetImage();
}
else
{
requestLedImageStart();
}
});
//calculate body size
canvas_height = $('#ledsim_dialog').outerHeight() - $('#ledsim_text').outerHeight() - $('[data-role=footer]').outerHeight() - $('[data-role=header]').outerHeight() - 40;
canvas_width = $('#ledsim_dialog').outerWidth() - 30;
// ------------------------------------------------------------------
$(window.hyperion).on("cmd-ledcolors-ledstream-update",function(event){
if (!modalOpened)
{
requestLedColorsStop();
}
else
{
printLedsToCanvas(event.response.result.leds)
}
});
$('#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>';
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>';
// ------------------------------------------------------------------
$(window.hyperion).on("cmd-ledcolors-imagestream-update",function(event){
setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-danger", "btn-success");
if (!modalOpened)
{
if ($('#leds_prev_toggle_live_video').length > 0)
return;
requestLedImageStop();
}
else
{
var imageData = (event.response.result.image);
$('#leds_canvas').html(leds_html);
var image = new Image();
image.onload = function() {
imageCanvasNodeCtx.drawImage(image, 0, 0, canvas_width, canvas_height);
};
image.src = imageData;
}
});
imageCanvasNodeCtx = document.getElementById("image_preview_canv").getContext("2d");
ledsCanvasNodeCtx = document.getElementById("leds_preview_canv").getContext("2d");
sigDetectAreaCanvasNodeCtx = document.getElementById("grab_preview_canv").getContext("2d");
create2dPaths();
printLedsToCanvas();
resetImage();
$("#btn_open_ledsim").off().on("click", function(event) {
dialog.open();
});
}
// ------------------------------------------------------------------
$(window.hyperion).on("cmd-settings-update",function(event){
// ------------------------------------------------------------------
$('#leds_toggle_num').off().on("click", function () {
toggleLedsNum = !toggleLedsNum
toggleClass('#leds_toggle_num', "btn-danger", "btn-success");
});
// ------------------------------------------------------------------
var obj = event.response.data
if ( obj.leds) {
console.log("ledsim: cmd-settings-update", event.response.data);
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
window.serverInfo[val] = obj[val];
});
leds = window.serverConfig.leds
updateLedLayout();
}
});
$('#leds_toggle').off().on("click", function () {
toggleLeds = !toggleLeds
ledsCanvasNodeCtx.clear();
toggleClass('#leds_toggle', "btn-success", "btn-danger");
function resetImage(){
if (getStorage("darkMode", false) == "on") {
imageCanvasNodeCtx.clear();
} else {
imageCanvasNodeCtx.fillStyle = "rgb(225,225,225)"
imageCanvasNodeCtx.fillRect(0, 0, canvas_width, canvas_height);
}
if (!toggleLeds) {
$("#leds_toggle_num").show();
} else {
$("#leds_toggle_num").hide();
}
});
var image = document.getElementById('navbar_brand_logo');
imageCanvasNodeCtx.drawImage(image, canvas_width / 2 - image.width / 2, canvas_height / 2 - image.height / 2, image.width, image.height);
}
// ------------------------------------------------------------------
$('#leds_toggle_live_video').off().on("click", function () {
setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-success", "btn-danger");
if (window.imageStreamActive) {
requestLedImageStop();
resetImage();
}
else {
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) {
if (!modalOpened) {
requestLedColorsStop();
}
else {
printLedsToCanvas(event.response.result.leds)
}
});
// ------------------------------------------------------------------
$(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");
if (!modalOpened) {
if ($('#leds_prev_toggle_live_video').length > 0)
return;
requestLedImageStop();
}
else {
var imageData = (event.response.result.image);
var image = new Image();
image.onload = function () {
imageCanvasNodeCtx.drawImage(image, 0, 0, canvas_width, canvas_height);
};
image.src = imageData;
}
});
$("#btn_open_ledsim").off().on("click", function (event) {
dialog.open();
});
// ------------------------------------------------------------------
$(window.hyperion).on("cmd-settings-update", function (event) {
var obj = event.response.data
if (obj.leds || obj.grabberV4L2) {
//console.log("ledsim: cmd-settings-update", event.response.data);
Object.getOwnPropertyNames(obj).forEach(function (val, idx, array) {
window.serverInfo[val] = obj[val];
});
leds = window.serverConfig.leds;
grabberConfig = window.serverConfig.grabberV4L2;
updateLedLayout();
}
});
$(window.hyperion).on("cmd-priorities-update", function (event) {
//console.log("cmd-priorities-update", event.response.data);
var prios = event.response.data.priorities;
if (prios.length > 0)
{
//Clear image when new input
if (prios[0].componentId !== activeComponent) {
resetImage();
activeComponent = prios[0].componentId;
}
else if (!prios[0].active) {
resetImage();
}
}
else {
resetImage();
}
});
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);
}
}
});

View File

@ -1839,7 +1839,14 @@ JSONEditor.AbstractEditor = Class.extend({
this.parent = null;
},
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];
var type = this.schema.type || this.schema.oneOf;

View File

@ -1,149 +1,111 @@
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'];
//$.i18n.debug = true;
var storedAccess;
//Change Password
function changePassword(){
showInfoDialog('changePassword', $.i18n('InfoDialog_changePassword_title'));
showInfoDialog('changePassword', $.i18n('InfoDialog_changePassword_title'));
// fill default pw if default is set
if(window.defaultPasswordIsSet)
$('#oldPw').val('hyperion')
// fill default pw if default is set
if(window.defaultPasswordIsSet)
$('#oldPw').val('hyperion')
$('#id_btn_ok').off().on('click',function() {
var oldPw = $('#oldPw').val();
var newPw = $('#newPw').val();
$('#id_btn_ok').off().on('click',function() {
var oldPw = $('#oldPw').val();
var newPw = $('#newPw').val();
requestChangePassword(oldPw, newPw)
});
requestChangePassword(oldPw, newPw)
});
$('#newPw, #oldPw').off().on('input',function(e) {
($('#oldPw').val().length >= 8 && $('#newPw').val().length >= 8) && !window.readOnlyMode ? $('#id_btn_ok').attr('disabled', false) : $('#id_btn_ok').attr('disabled', true);
});
$('#newPw, #oldPw').off().on('input',function(e) {
($('#oldPw').val().length >= 8 && $('#newPw').val().length >= 8) && !window.readOnlyMode ? $('#id_btn_ok').attr('disabled', false) : $('#id_btn_ok').attr('disabled', true);
});
}
$(document).ready( function() {
$(document).ready(function () {
//i18n
function initTrans(lc){
if (lc == 'auto')
{
$.i18n().load().done(
function() {
performTranslation();
});
}
else
{
$.i18n().locale = lc;
$.i18n().load( "i18n", lc ).done(
function() {
performTranslation();
});
}
}
if (!storageComp()) {
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");
$('#language-select').attr("disabled", true);
$('#btn_setaccess').attr("disabled", true);
}
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");
initTrans('auto');
storedLang = 'auto';
storedAccess = "default";
$('#btn_setlang').attr("disabled", true);
$('#btn_setaccess').attr("disabled", true);
}
initLanguageSelection();
initLanguageSelection();
//access
storedAccess = getStorage("accesslevel");
if (storedAccess == null) {
storedAccess = "default";
setStorage("accesslevel", storedAccess);
}
//access
storedAccess = getStorage("accesslevel");
if (storedAccess == null)
{
setStorage("accesslevel", "default");
storedAccess = "default";
}
if (!storageComp()) {
showInfoDialog('warning', $.i18n('InfoDialog_nostorage_title'), $.i18n('InfoDialog_nostorage_text'));
$('#btn_setlang').attr("disabled", true);
}
$('#btn_setaccess').off().on('click',function() {
var newAccess;
showInfoDialog('select', $.i18n('InfoDialog_access_title'), $.i18n('InfoDialog_access_text'));
$('#btn_setaccess').off().on('click',function() {
var newAccess;
showInfoDialog('select', $.i18n('InfoDialog_access_title'), $.i18n('InfoDialog_access_text'));
for (var lcx = 0; lcx<availAccess.length; lcx++)
{
$('#id_select').append(createSelOpt(availAccess[lcx], $.i18n('general_access_'+availAccess[lcx])));
}
for (var lcx = 0; lcx<availAccess.length; lcx++)
{
$('#id_select').append(createSelOpt(availAccess[lcx], $.i18n('general_access_'+availAccess[lcx])));
}
$('#id_select').val(storedAccess);
$('#id_select').val(storedAccess);
$('#id_select').off().on('change',function() {
newAccess = $('#id_select').val();
if (newAccess == storedAccess)
$('#id_btn_saveset').attr('disabled', true);
else
$('#id_btn_saveset').attr('disabled', false);
});
$('#id_select').off().on('change',function() {
newAccess = $('#id_select').val();
if (newAccess == storedAccess)
$('#id_btn_saveset').attr('disabled', true);
else
$('#id_btn_saveset').attr('disabled', false);
});
$('#id_btn_saveset').off().on('click',function() {
setStorage("accesslevel", newAccess);
reload();
});
$('#id_btn_saveset').off().on('click',function() {
setStorage("accesslevel", newAccess);
reload();
});
$('#id_select').trigger('change');
});
$('#id_select').trigger('change');
});
// change pw btn
$('#btn_changePassword').off().on('click',function() {
changePassword();
});
// change pw btn
$('#btn_changePassword').off().on('click',function() {
changePassword();
});
//Lock Ui
$('#btn_lock_ui').off().on('click',function() {
removeStorage('loginToken', true);
location.replace('/');
});
//Lock Ui
$('#btn_lock_ui').off().on('click',function() {
removeStorage('loginToken', true);
location.replace('/');
});
//hide menu elements
if (storedAccess != 'expert')
$('#load_webconfig').toggle(false);
//hide menu elements
if (storedAccess != 'expert')
$('#load_webconfig').toggle(false);
// instance switcher
$('#btn_instanceswitch').off().on('click',function() {
var lsys = window.sysInfo.system.hostName+':'+window.serverConfig.webConfig.port;
showInfoDialog('iswitch', $.i18n('InfoDialog_iswitch_title'), $.i18n('InfoDialog_iswitch_text'));
// instance switcher
$('#btn_instanceswitch').off().on('click',function() {
var lsys = window.sysInfo.system.hostName+':'+window.serverConfig.webConfig.port;
showInfoDialog('iswitch', $.i18n('InfoDialog_iswitch_title'), $.i18n('InfoDialog_iswitch_text'));
for (var i = 0; i<window.wSess.length; i++)
{
if(lsys != window.wSess[i].host+':'+window.wSess[i].port)
{
var hyperionAddress = window.wSess[i].address;
if(hyperionAddress.indexOf(':') > -1 && hyperionAddress.length == 36) hyperionAddress = '['+hyperionAddress+']';
hyperionAddress = 'http://'+hyperionAddress+':'+window.wSess[i].port;
$('#id_select').append(createSelOpt(hyperionAddress, window.wSess[i].name));
}
}
for (var i = 0; i<window.wSess.length; i++)
{
if(lsys != window.wSess[i].host+':'+window.wSess[i].port)
{
var hyperionAddress = window.wSess[i].address;
if(hyperionAddress.indexOf(':') > -1 && hyperionAddress.length == 36) hyperionAddress = '['+hyperionAddress+']';
hyperionAddress = 'http://'+hyperionAddress+':'+window.wSess[i].port;
$('#id_select').append(createSelOpt(hyperionAddress, window.wSess[i].name));
}
}
$('#id_btn_saveset').off().on('click',function() {
$("#loading_overlay").addClass("overlay");
window.location.href = $('#id_select').val();
});
$('#id_btn_saveset').off().on('click',function() {
$("#loading_overlay").addClass("overlay");
window.location.href = $('#id_select').val();
});
});
});
});

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

@ -67,7 +67,7 @@ function updateSessions() {
function validateDuration(d) {
if (typeof d === "undefined" || d < 0)
return 0;
return ENDLESS;
else
return d *= 1000;
}
@ -162,25 +162,22 @@ function initLanguageSelection() {
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
var langIdx = availLang.indexOf(langLocale);
if (langIdx > -1) {
langText = availLangText[langIdx];
}
else {
} else {
// If language is not supported by hyperion, try fallback language
langLocale = $.i18n().options.fallbackLocale.substring(0, 2);
langIdx = availLang.indexOf(langLocale);
if (langIdx > -1) {
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);
$('#active_instance_dropdown').prop('disabled', false);
$('#active_instance_dropdown').css('cursor', 'pointer');
$("#active_instance_dropdown").css("pointer-events", "auto");
} else {
$('#btn_hypinstanceswitch').toggle(false);
$('#active_instance_dropdown').prop('disabled', true);
$("#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") {
$('#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('<input class="form-control" id="oldPw" placeholder="Old" type="text"> <br />');
$('#id_body_rename').append('<input class="form-control" id="newPw" placeholder="New" type="password">');
$('#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('<h4>' + header + '</h4><br>');
$('#id_body_rename').append('<div class="row"><div class="col-md-3"><p class="text-left">' + $.i18n('infoDialog_password_current_text') +
'</p></div><div class="col-md-8"><input class="form-control" id="oldPw" placeholder="Old" type="password"></div></div><br>');
$('#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>');
}
else if (type == "checklist") {
@ -461,7 +462,8 @@ function createJsonEditor(container, schema, setconfig, usePanel, arrayre) {
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 newSchema = [];
@ -536,13 +538,15 @@ function updateJsonEditorSelection(editor, key, addElements, newEnumVals, newTit
editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
editor.removeObjectProperty(key);
delete editor.cached_editors[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 newSchema = [];
@ -593,36 +597,55 @@ function updateJsonEditorMultiSelection(editor, key, addElements, newEnumVals, n
editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
editor.removeObjectProperty(key);
delete editor.cached_editors[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 newSchema = [];
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;
}
if (maximum) {
if (typeof maximum !== "undefined") {
newSchema[key]["maximum"] = maximum;
}
if (defaultValue) {
if (typeof defaultValue !== "undefined") {
newSchema[key]["default"] = defaultValue;
currentValue = defaultValue;
}
if (step) {
if (typeof step !== "undefined") {
newSchema[key]["step"] = step;
}
editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
editor.removeObjectProperty(key);
delete editor.cached_editors[key];
editor.addObjectProperty(key);
// Restore current (new default) value for new range
rootEditor.getEditor(path + "." + key).setValue(currentValue);
}
function buildWL(link, linkt, cl) {
@ -807,14 +830,15 @@ function createRow(id) {
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;
var pfooter = document.createElement('button');
pfooter.className = "btn btn-primary";
pfooter.setAttribute("id", footerid);
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') {
@ -1141,14 +1165,34 @@ function isAccessLevelCompliant(accessLevel) {
function showInputOptions(path, elements, state) {
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 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) {
if (showForKey !== key) {
if ($.inArray(key, keysToshow) === -1) {
var accessLevel = editor.schema.properties[item].properties[key].access;
//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-compat-libdnssd-dev \
libssl-dev \
libjpeg-dev \
libqt5sql5-sqlite \
libqt5svg5-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
# packages string inserted to cmake cmd
PACKAGES=""
# platform string inserted to cmake cmd
BUILD_PLATFORM=""
#Run build using GitHub code files
BUILD_LOCAL=0
#Build from scratch
@ -73,6 +75,7 @@ echo "########################################################
# docker-compile.sh -p true # If true, build packages with CPack
# 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 -f x11 # cmake PLATFORM parameter
# 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"
while getopts i:t:b:p:lcvh option
while getopts i:t:b:p:f:lcvh option
do
case "${option}"
in
@ -92,6 +95,7 @@ do
t) BUILD_TAG=${OPTARG};;
b) BUILD_TYPE=${OPTARG};;
p) BUILD_PACKAGES=${OPTARG};;
f) BUILD_PLATFORM=${OPTARG,,};;
l) BUILD_LOCAL=1;;
c) BUILD_INCREMENTAL=1;;
v) _VERBOSE=1;;
@ -104,7 +108,12 @@ if [ ${BUILD_PACKAGES} == "true" ]; then
PACKAGES="package"
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}"
CODE_PATH=${BASE_PATH};
@ -155,7 +164,7 @@ $DOCKER run --rm \
-v "${CODE_PATH}/:/source:rw" \
${REGISTRY_URL}/${BUILD_IMAGE}:${BUILD_TAG} \
/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 || : &&
exit 0;
exit 1 " || { echo "---> Hyperion compilation failed! Abort"; exit 4; }
@ -164,7 +173,6 @@ DOCKERRC=${?}
# 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}) ${DEPLOY_PATH}
if [ ${DOCKERRC} == 0 ]; then
if [ ${BUILD_LOCAL} == 1 ]; then
@ -175,6 +183,7 @@ if [ ${DOCKERRC} == 0 ]; then
echo "---> Copying packages to host folder: ${DEPLOY_PATH}" &&
cp -v ${BUILD_PATH}/Hyperion-* ${DEPLOY_PATH} 2>/dev/null
echo "---> Find deployment packages in: ${DEPLOY_PATH}"
sudo chown -fR $(stat -c "%U:%G" ${BASE_PATH}) ${DEPLOY_PATH}
fi
fi
echo "---> Script finished [${DOCKERRC}]"

View File

@ -241,7 +241,7 @@ macro(DeployWindows TARGET)
list(REMOVE_AT DEPENDENCIES 0 1)
endwhile()
# Copy OpenSSL Libs
# Copy libssl/libcrypto to 'hyperion'
if (OPENSSL_FOUND)
string(REGEX MATCHALL "[0-9]+" openssl_versions "${OPENSSL_VERSION}")
list(GET openssl_versions 0 openssl_version_major)
@ -271,6 +271,27 @@ macro(DeployWindows TARGET)
)
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
file(WRITE "${CMAKE_BINARY_DIR}/qt.conf" "[Paths]\nPlugins=../lib/\n")
install(
@ -318,6 +339,30 @@ macro(DeployWindows TARGET)
)
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()
# Run CMake after target was built
add_custom_command(

View File

@ -3,15 +3,31 @@
# TurboJPEG_INCLUDE_DIRS
# TurboJPEG_LIBRARY
find_path(TurboJPEG_INCLUDE_DIRS
NAMES turbojpeg.h
PATH_SUFFIXES include
)
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
PATH_SUFFIXES bin lib
)
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
PATH_SUFFIXES include
)
find_library(TurboJPEG_LIBRARY
NAMES turbojpeg turbojpeg-static
PATH_SUFFIXES bin lib
)
endif()
if(TurboJPEG_INCLUDE_DIRS AND TurboJPEG_LIBRARY)
include(CheckCSourceCompiles)
@ -26,7 +42,7 @@ if(TurboJPEG_INCLUDE_DIRS AND TurboJPEG_LIBRARY)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(TurboJpeg
find_package_handle_standard_args(TurboJPEG
FOUND_VAR TURBOJPEG_FOUND
REQUIRED_VARS TurboJPEG_LIBRARY TurboJPEG_INCLUDE_DIRS TURBOJPEG_WORKS
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
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:
; 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

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

View File

@ -11,7 +11,7 @@ Note: call the script with `./docker-compile.sh -h` for more options.
```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
```
**Raspbian Buster**
**Raspbian Buster/Raspberry Pi OS**
```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
```
@ -58,7 +58,7 @@ cd $HYPERION_HOME
```console
sudo apt-get update
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev 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**
@ -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.
```console
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..).
@ -98,18 +98,20 @@ brew install qt5 python3 cmake libusb doxygen zlib
## Windows (WIP)
We assume a 64bit Windows 10. Install the following;
- [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))
- 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.
- [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)
- 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`
- [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:
- 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:
- [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);
/// 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
///

View File

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

View File

@ -14,12 +14,16 @@ public:
///
/// 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;
///
/// @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
/// provided image should have the same dimensions as the configured values (_width and
@ -31,14 +35,51 @@ public:
///
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:
/**
* Returns true if video is playing over the amlogic chip
* @return True if video is playing else false
*/
bool isVideoPlaying();
void closeDev(int &fd);
bool openDev(int &fd, const char* dev);
void closeDevice(int &fd);
bool openDevice(int &fd, const char* dev);
int grabFrame_amvideocap(Image<ColorRgb> & image);

View File

@ -4,8 +4,8 @@
#include <grabber/AmlogicGrabber.h>
///
/// The DispmanxWrapper uses an instance of the DispmanxFrameGrabber to obtain ImageRgb's from the
/// displayed content. This ImageRgb is processed to a ColorRgb for each led and commmited to 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 committed to the
/// attached Hyperion.
///
class AmlogicWrapper : public GrabberWrapper
@ -13,12 +13,14 @@ class AmlogicWrapper : public GrabberWrapper
Q_OBJECT
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] 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:
///

View File

@ -8,8 +8,14 @@
// Hyperion-utils includes
#include <utils/ColorRgb.h>
#include <hyperion/GrabberWrapper.h>
#include <hyperion/Grabber.h>
// qt includes
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
///
/// @brief The DirectX9 capture implementation
///
@ -17,33 +23,32 @@ class DirectXGrabber : public Grabber
{
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();
///
/// Captures a single snapshot of the display and writes the data to the given image. The
/// provided image should have the same dimensions as the configured values (_width and
/// _height)
/// provided image should have the same dimensions as the configured values (_width and _height)
///
/// @param[out] image The snapped screenshot
///
virtual int grabFrame(Image<ColorRgb> & image);
int grabFrame(Image<ColorRgb> & image);
///
/// @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
///
virtual bool setWidthHeight(int width, int height) { return true; };
bool setWidthHeight(int /* width */, int /*height*/) override { return true; }
///
/// @brief Apply new pixelDecimation
///
virtual void setPixelDecimation(int pixelDecimation);
bool setPixelDecimation(int pixelDecimation) override;
///
/// Set the crop values
@ -52,12 +57,20 @@ public:
/// @param cropTop Top 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
///
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:
///
@ -72,7 +85,6 @@ private:
void freeResources();
private:
int _pixelDecimation;
unsigned _display;
unsigned _displayWidth;
unsigned _displayHeight;

View File

@ -9,15 +9,21 @@ public:
///
/// 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] cropRight Remove from right [pixels]
/// @param[in] cropTop Remove from top [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.

View File

@ -14,8 +14,7 @@
#include <hyperion/Grabber.h>
///
/// The DispmanxFrameGrabber is used for creating snapshots of the display (screenshots) with a
/// downsized and scaled resolution.
/// The DispmanxFrameGrabber is used for creating snapshots of the display (screenshots) with a downsized and scaled resolution.
///
class DispmanxFrameGrabber : public Grabber
{
@ -23,12 +22,16 @@ public:
///
/// Construct a DispmanxFrameGrabber 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
///
DispmanxFrameGrabber(unsigned width, unsigned height);
DispmanxFrameGrabber();
~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
@ -44,13 +47,24 @@ public:
///@brief Set new width and height for dispmanx, overwrite Grabber.h impl
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:
///
/// Updates the frame-grab flags as used by the VC library for frame grabbing
///
/// @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
@ -63,11 +77,11 @@ private:
/// Handle to the resource for storing the captured snapshot
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;
/// Flags (transforms) for creating snapshots
int _vc_flags;
DISPMANX_TRANSFORM_T _vc_flags;
// temp buffer when capturing with unsupported pitch size or
// when we need to crop the image
@ -78,5 +92,4 @@ private:
// rgba output buffer
Image<ColorRgba> _image_rgba;
};

View File

@ -16,6 +16,7 @@ typedef int DISPMANX_TRANSFORM_T;
struct DISPMANX_MODEINFO_T {
int width;
int height;
uint32_t display_num;
};
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);
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);
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

View File

@ -16,11 +16,14 @@ public:
///
/// 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] grabHeight The height of the grabbed images [pixels]
/// @param[in] pixelDecimation Decimation factor for image [pixels]
/// @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:
///

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
#include <linux/fb.h>
// Utils includes
#include <utils/ColorRgb.h>
#include <hyperion/Grabber.h>
@ -14,10 +16,10 @@ public:
/// Construct a FramebufferFrameGrabber that will capture snapshots with specified dimensions.
///
/// @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
@ -30,11 +32,42 @@ public:
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:
bool openDevice();
bool closeDevice();
bool getScreenInfo();
/// Framebuffer device e.g. /dev/fb0
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
/// 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.
///
class FramebufferWrapper: public GrabberWrapper
@ -15,12 +15,14 @@ public:
///
/// 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] 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:
///

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.
///
/// @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;
///
/// @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
/// provided image should have the same dimensions as the configured values (_width and
@ -40,12 +45,21 @@ public:
///
/// @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:
/// display
unsigned _screenIndex;
int _screenIndex;
/// Reference to the captured diaplay
/// Reference to the captured display
CGDirectDisplayID _display;
};

View File

@ -4,31 +4,90 @@
/*
* 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.
*
* 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/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 CGImage* CGImageRef;
typedef unsigned char CFData;
typedef CFData* CFDataRef;
typedef unsigned CGDisplayCount;
const int kCGDirectMainDisplay = 0;
void CGGetActiveDisplayList(int max, CGDirectDisplayID *displays, CGDisplayCount *displayCount);
CGImageRef CGDisplayCreateImage(CGDirectDisplayID display);
void CGImageRelease(CGImageRef image);
CGImageRef CGImageGetDataProvider(CGImageRef image);
CFDataRef CGDataProviderCopyData(CGImageRef image);
unsigned char* CFDataGetBytePtr(CFDataRef imgData);
unsigned CGImageGetWidth(CGImageRef image);
unsigned CGImageGetHeight(CGImageRef image);
unsigned CGImageGetBitsPerPixel(CGImageRef image);
unsigned CGImageGetBytesPerRow(CGImageRef image);
void CFRelease(CFDataRef imgData);
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);
void CGImageRelease(CGImageRef image);
CGImageRef CGImageGetDataProvider(CGImageRef image);
CFDataRef CGDataProviderCopyData(CGImageRef image);
unsigned char* CFDataGetBytePtr(CFDataRef imgData);
unsigned CGImageGetWidth(CGImageRef image);
unsigned CGImageGetHeight(CGImageRef image);
unsigned CGImageGetBitsPerPixel(CGImageRef image);
unsigned CGImageGetBytesPerRow(CGImageRef image);
void CFRelease(CFDataRef imgData);
#endif

View File

@ -4,9 +4,8 @@
#include <grabber/OsxFrameGrabber.h>
///
/// The OsxWrapper uses an instance of the OsxFrameGrabber to obtain ImageRgb's from the
/// displayed content. This ImageRgb is processed to a ColorRgb for each led and commmited to the
/// attached Hyperion.
/// The OsxWrapper uses an instance of the OsxFrameGrabber to obtain ImageRgb's from the displayed content.
/// This ImageRgb is processed to a ColorRgb for each led and committed to the attached Hyperion.
///
class OsxWrapper: public GrabberWrapper
{
@ -15,12 +14,14 @@ public:
///
/// 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] 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:
///

View File

@ -15,14 +15,13 @@ class QtGrabber : public Grabber
{
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;
///
/// Captures a single snapshot of the display and writes the data to the given image. The
/// provided image should have the same dimensions as the configured values (_width and
/// _height)
/// provided image should have the same dimensions as the configured values (_width and _height)
///
/// @param[out] image The snapped screenshot (should be initialized with correct width and
/// 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
///
bool setWidthHeight(int width, int height) override { return true; }
bool setWidthHeight(int /*width*/, int /*height*/) override { return true; }
///
/// @brief Apply new pixelDecimation
///
void setPixelDecimation(int pixelDecimation) override;
bool setPixelDecimation(int pixelDecimation) override;
///
/// Set the crop values
@ -51,14 +50,37 @@ public:
/// @param cropTop Top 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
///
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:
///
/// @brief is called whenever the current _screen changes it's geometry
/// @param geo The new geometry
@ -66,11 +88,6 @@ private slots:
void geometryChanged(const QRect &geo);
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
@ -84,13 +101,19 @@ private:
private:
unsigned _display;
int _pixelDecimation;
unsigned _screenWidth;
unsigned _screenHeight;
unsigned _src_x;
unsigned _src_y;
unsigned _src_x_max;
unsigned _src_y_max;
int _display;
int _numberOfSDisplays;
int _calculatedWidth;
int _calculatedHeight;
int _src_x;
int _src_y;
int _src_x_max;
int _src_y_max;
bool _isWayland;
QScreen* _screen;
bool _isVirtual;
Logger * _logger;
};

View File

@ -10,16 +10,28 @@ class QtWrapper: public GrabberWrapper
{
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] cropRight Remove from right [pixels]
/// @param[in] cropTop Remove from top [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:
///

View File

@ -14,30 +14,24 @@
// util includes
#include <utils/PixelFormat.h>
#include <hyperion/Grabber.h>
#include <grabber/VideoStandard.h>
#include <hyperion/GrabberWrapper.h>
#include <utils/VideoStandard.h>
#include <utils/Components.h>
#include <cec/CECEvent.h>
// general JPEG decoder includes
#ifdef HAVE_JPEG_DECODER
#include <QImage>
#include <QColor>
#endif
// System JPEG decoder
#ifdef HAVE_JPEG
#include <jpeglib.h>
#include <csetjmp>
#endif
// TurboJPEG decoder
#ifdef HAVE_TURBO_JPEG
#include <turbojpeg.h>
// decoder thread includes
#include <grabber/EncoderThread.h>
// Determine the cmake options
#include <HyperionConfig.h>
#if defined(ENABLE_CEC)
#include <cec/CECEvent.h>
#endif
///
/// Capture class for V4L2 devices
///
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
class V4L2Grabber : public Grabber
{
Q_OBJECT
@ -45,118 +39,67 @@ class V4L2Grabber : public Grabber
public:
struct DeviceProperties
{
QString name = QString();
QMultiMap<QString, int> inputs = QMultiMap<QString, int>();
QStringList resolutions = QStringList();
QStringList framerates = QStringList();
QString name = QString();
struct InputProperties
{
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,
const unsigned width,
const unsigned height,
const unsigned fps,
const unsigned input,
VideoStandard videoStandard,
PixelFormat pixelFormat,
int pixelDecimation
);
struct DeviceControls
{
QString property = QString();
int minValue = 0;
int maxValue = 0;
int step = 0;
int defaultValue = 0;
int currentValue = 0;
};
V4L2Grabber();
~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> &);
///
/// @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
///
void setDevice(const QString& devicePath, const QString& deviceName);
bool setInput(int input) override;
///
/// @brief overwrite Grabber.h implementation
///
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;
///
/// @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;
QJsonArray discover(const QJsonObject& params);
public slots:
bool prepare();
bool start();
void stop();
void newThreadFrame(Image<ColorRgb> image);
#if defined(ENABLE_CEC)
void handleCecEvent(CECEvent event);
#endif
signals:
void newFrame(const Image<ColorRgb> & image);
@ -166,36 +109,19 @@ private slots:
int read_frame();
private:
void getV4Ldevices();
bool init();
void uninit();
bool open_device();
void close_device();
void init_read(unsigned int buffer_size);
void init_mmap();
void init_userp(unsigned int buffer_size);
void init_device(VideoStandard videoStandard);
void uninit_device();
void start_capturing();
void stop_capturing();
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 fileDescriptor, int request, void *arg);
void throw_exception(const QString & error)
@ -222,56 +148,28 @@ private:
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:
QString _deviceName;
std::map<QString, QString> _v4lDevices;
QString _currentDevicePath, _currentDeviceName;
EncoderThreadManager* _threadManager;
QMap<QString, V4L2Grabber::DeviceProperties> _deviceProperties;
QMap<QString, QList<DeviceControls>> _deviceControls;
VideoStandard _videoStandard;
io_method _ioMethod;
int _fileDescriptor;
std::vector<buffer> _buffers;
PixelFormat _pixelFormat;
int _pixelDecimation;
PixelFormat _pixelFormat, _pixelFormatConfig;
int _lineLength;
int _frameByteSize;
QAtomicInt _currentFrame;
// signal detection
int _noSignalCounterThreshold;
ColorRgb _noSignalThresholdColor;
bool _signalDetectionEnabled;
bool _cecDetectionEnabled;
bool _cecStandbyActivated;
bool _noSignalDetected;
bool _cecDetectionEnabled, _cecStandbyActivated, _signalDetectionEnabled, _noSignalDetected;
int _noSignalCounter;
int _brightness, _contrast, _saturation, _hue;
double _x_frac_min;
double _y_frac_min;
double _x_frac_max;
@ -279,9 +177,9 @@ private:
QSocketNotifier *_streamNotifier;
bool _initialized;
bool _deviceAutoDiscoverEnabled;
bool _initialized, _reload;
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 <QAbstractNativeEventFilter>
#include <QCoreApplication>
// QT includes
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
// Hyperion-utils includes
#include <utils/ColorRgb.h>
@ -20,11 +26,13 @@ class X11Grabber : public Grabber , public QAbstractNativeEventFilter
{
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;
bool Setup();
bool open();
bool setupDisplay();
///
/// 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
///
void setPixelDecimation(int pixelDecimation) override;
bool setPixelDecimation(int pixelDecimation) override;
///
/// Set the crop values
@ -59,22 +67,33 @@ public:
/// @param cropTop Top 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:
bool nativeEventFilter(const QByteArray & eventType, void * message, long int * result) override;
private:
bool _XShmAvailable, _XShmPixmapAvailable, _XRenderAvailable, _XRandRAvailable;
XImage* _xImage;
XShmSegmentInfo _shminfo;
void freeResources();
void setupResources();
/// Reference to the X11 display (nullptr if not opened)
Display* _x11Display;
Window _window;
XWindowAttributes _windowAttr;
XImage* _xImage;
XShmSegmentInfo _shminfo;
Pixmap _pixmap;
XRenderPictFormat* _srcFormat;
XRenderPictFormat* _dstFormat;
@ -85,15 +104,19 @@ private:
int _XRandREventBase;
XTransform _transform;
int _pixelDecimation;
unsigned _screenWidth;
unsigned _screenHeight;
unsigned _calculatedWidth;
unsigned _calculatedHeight;
unsigned _src_x;
unsigned _src_y;
Image<ColorRgb> _image;
bool _XShmAvailable;
bool _XShmPixmapAvailable;
bool _XRenderAvailable;
bool _XRandRAvailable;
bool _isWayland;
void freeResources();
void setupResources();
Logger * _logger;
Image<ColorRgb> _image;
};

View File

@ -9,25 +9,26 @@
///
/// The X11Wrapper uses an instance of the X11Grabber to obtain ImageRgb's from the
/// displayed content. This ImageRgb is processed to a ColorRgb for each led and commmited to the
/// attached Hyperion.
/// The X11Wrapper uses an instance of the X11Grabber to obtain ImageRgb's from the displayed content.
/// This ImageRgb is processed to a ColorRgb for each led and committed to the attached Hyperion.
///
class X11Wrapper: public GrabberWrapper
{
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] 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;

View File

@ -1,7 +1,11 @@
#pragma once
#include <QAbstractNativeEventFilter>
// QT includes
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <utils/ColorRgb.h>
#include <hyperion/Grabber.h>
@ -21,16 +25,28 @@ class XcbGrabber : public Grabber, public QAbstractNativeEventFilter
Q_OBJECT
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;
bool Setup();
bool open();
bool setupDisplay();
int grabFrame(Image<ColorRgb> & image, bool forceUpdate = false);
int updateScreenDimensions(bool force = false);
void setVideoMode(VideoMode mode) override;
bool setWidthHeight(int width, int height) override { return true; }
void setPixelDecimation(int pixelDecimation) override;
void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) override;
bool setPixelDecimation(int pixelDecimation) 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:
bool nativeEventFilter(const QByteArray & eventType, void * message, long int * result) override;
@ -52,8 +68,7 @@ private:
xcb_render_transform_t _transform;
xcb_shm_seg_t _shminfo;
int _pixelDecimation;
int _screen_num;
unsigned _screenWidth;
unsigned _screenHeight;
unsigned _src_x;
@ -63,6 +78,8 @@ private:
bool _XcbRandRAvailable;
bool _XcbShmAvailable;
bool _XcbShmPixmapAvailable;
bool _isWayland;
Logger * _logger;
uint8_t * _shmData;

View File

@ -11,7 +11,12 @@
class XcbWrapper: public GrabberWrapper
{
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;
public slots:

View File

@ -6,23 +6,21 @@
#include <utils/ColorRgb.h>
#include <utils/Image.h>
#include <utils/VideoMode.h>
#include <grabber/VideoStandard.h>
#include <utils/VideoStandard.h>
#include <utils/ImageResampler.h>
#include <utils/Logger.h>
#include <utils/Components.h>
#include <QMultiMap>
///
/// @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
{
Q_OBJECT
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)
@ -31,12 +29,18 @@ public:
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
///
virtual bool setInput(int input);
@ -48,114 +52,95 @@ public:
virtual bool setWidthHeight(int width, int height);
///
/// @brief Apply new framerate (used from v4l)
/// @brief Apply new capture framerate in Hz
/// @param fps framesPerSecond
///
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(
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) {}
virtual void setVideoStandard(VideoStandard videoStandard);
///
/// @brief Apply SignalDetectionEnable (used from v4l)
/// @brief Apply new pixelDecimation
///
virtual void setSignalDetectionEnable(bool enable) {}
///
/// @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) {}
virtual bool setPixelDecimation(int pixelDecimation);
///
/// @brief Apply display index (used from qt)
///
virtual void setDisplayIndex(int index) {}
///
/// @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; }
virtual bool setDisplayIndex(int /*index*/) { return true; }
///
/// @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
/// @return List of all available V4L devices on success else empty List
/// @brief get current resulting height of image (after crop)
///
virtual QStringList getV4L2devices() const { return QStringList(); }
int getImageWidth() const { return _width; }
///
/// @brief Get the V4L device name
/// @param devicePath The device path
/// @return The name of the V4L device on success else empty String
/// @brief get current resulting width of image (after crop)
///
virtual QString getV4L2deviceName(const QString& /*devicePath*/) const { return QString(); }
int getImageHeight() const { return _height; }
///
/// @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
/// @brief Get current capture framerate in Hz
/// @param fps framesPerSecond
///
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
/// @param devicePath The device path
/// @return List of resolutions on success else empty List
/// @brief Get capture interval in ms
///
virtual QStringList getResolutions(const QString& /*devicePath*/) const { return QStringList(); }
int getUpdateInterval() const { return 1000/_fps; }
///
/// @brief Get a list of supported device framerates
/// @param devicePath The device path
/// @return List of framerates on success else empty List
/// @brief Get pixelDecimation
///
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:
QString _grabberName;
/// logger instance
Logger * _log;
ImageResampler _imageResampler;
bool _useImageResampler;
/// 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]
int _width;
@ -166,15 +151,21 @@ protected:
/// frame per second
int _fps;
/// fps software decimation
int _fpsSoftwareDecimation;
/// device input
int _input;
/// number of pixels to crop after capturing
int _cropLeft, _cropRight, _cropTop, _cropBottom;
bool _enabled;
// Device states
/// logger instance
Logger * _log;
/// Is the device enabled?
bool _isEnabled;
/// Is the device in error state and stopped?
bool _isDeviceInError;
};

View File

@ -12,30 +12,38 @@
#include <utils/Image.h>
#include <utils/ColorRgb.h>
#include <utils/VideoMode.h>
#include <utils/PixelFormat.h>
#include <utils/settings.h>
#include <utils/VideoStandard.h>
class Grabber;
class GlobalSignals;
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
{
Q_OBJECT
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;
static GrabberWrapper* 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
///
@ -56,45 +64,17 @@ public:
///
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
/// @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();
@ -130,6 +110,12 @@ public slots:
///
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
/// @param cropLeft Left pixel crop
@ -137,11 +123,11 @@ public slots:
/// @param cropTop Top 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
/// @param type settingyType from enum
/// @param type settingsType from enum
/// @param config configuration object
///
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
@ -159,22 +145,38 @@ private slots:
///
/// @brief Update Update capture rate
/// @param type interval between frames in millisecons
/// @param type interval between frames in milliseconds
///
void updateTimer(int interval);
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;
/// The Logger instance
Logger * _log;
/// The timer for generating events with the specified update rate
QTimer* _timer;
/// The calced update rate [ms]
/// The calculated update rate [ms]
int _updateInterval_ms;
/// The Logger instance
Logger * _log;
Grabber *_ggrabber;
/// The image used for grabbing frames

View File

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

View File

@ -57,8 +57,11 @@ public:
//Foreground and Background priorities
const static int FG_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
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
@ -84,7 +87,7 @@ public:
/// @param update True to update _currentPriority - INTERNAL usage.
/// @return True if changed has been applied, false if the state is unchanged
///
bool setSourceAutoSelectEnabled(bool enabel, bool update = true);
bool setSourceAutoSelectEnabled(bool enable, bool update = true);
///
/// @brief Get the state of source auto selection

View File

@ -9,12 +9,13 @@ class ImageResampler
{
public:
ImageResampler();
~ImageResampler();
~ImageResampler() {}
void setHorizontalPixelDecimation(int decimator);
void setVerticalPixelDecimation(int decimator);
void setHorizontalPixelDecimation(int decimator) { _horizontalDecimation = decimator; }
void setVerticalPixelDecimation(int decimator) { _verticalDecimation = decimator; }
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;
private:
@ -25,5 +26,6 @@ private:
int _cropTop;
int _cropBottom;
VideoMode _videoMode;
FlipMode _flipMode;
};

View File

@ -12,7 +12,9 @@ enum class PixelFormat {
BGR24,
RGB32,
BGR32,
#ifdef HAVE_JPEG_DECODER
NV12,
I420,
#ifdef HAVE_TURBO_JPEG
MJPEG,
#endif
NO_CHANGE
@ -23,32 +25,40 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat)
// convert to lower case
QString format = pixelFormat.toLower();
if (format.compare("yuyv") )
if (format.compare("yuyv") == 0)
{
return PixelFormat::YUYV;
}
else if (format.compare("uyvy") )
else if (format.compare("uyvy") == 0)
{
return PixelFormat::UYVY;
}
else if (format.compare("bgr16") )
else if (format.compare("bgr16") == 0)
{
return PixelFormat::BGR16;
}
else if (format.compare("bgr24") )
else if (format.compare("bgr24") == 0)
{
return PixelFormat::BGR24;
}
else if (format.compare("rgb32") )
else if (format.compare("rgb32") == 0)
{
return PixelFormat::RGB32;
}
else if (format.compare("bgr32") )
else if (format.compare("bgr32") == 0)
{
return PixelFormat::BGR32;
}
#ifdef HAVE_JPEG_DECODER
else if (format.compare("mjpeg") )
else if (format.compare("i420") == 0)
{
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;
}
@ -57,3 +67,102 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat)
// return the default 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
#include <QString>
/**
* 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)
{
// convert to lower case
QString standard = videoStandard.toLower();
QString standard = videoStandard.toUpper();
if (standard == "pal")
if (standard == "PAL")
{
return VideoStandard::PAL;
}
else if (standard == "ntsc")
else if (standard == "NTSC")
{
return VideoStandard::NTSC;
}
else if (standard == "secam")
else if (standard == "SECAM")
{
return VideoStandard::SECAM;
}
@ -31,3 +33,14 @@ inline VideoStandard parseVideoStandard(const QString& videoStandard)
// return the default 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
#define QSTRING_CSTR(str) str.toLocal8Bit().constData()
typedef QList< int > QIntList;

View File

@ -8,6 +8,7 @@
// fg effect
#include <hyperion/Hyperion.h>
#include <hyperion/PriorityMuxer.h>
#include <effectengine/Effect.h>
///
/// @brief Provide utility methods for Hyperion class
@ -17,7 +18,6 @@ namespace hyperion {
void handleInitialEffect(Hyperion* hyperion, const QJsonObject& FGEffectConfig)
{
#define FGCONFIG_ARRAY fgColorConfig.toArray()
const int DURATION_INFINITY = 0;
// initial foreground effect/color
if (FGEffectConfig["enable"].toBool(true))
@ -27,7 +27,7 @@ namespace hyperion {
const QJsonValue fgColorConfig = FGEffectConfig["color"];
int default_fg_duration_ms = 3000;
int fg_duration_ms = FGEffectConfig["duration_ms"].toInt(default_fg_duration_ms);
if (fg_duration_ms == DURATION_INFINITY)
if (fg_duration_ms <= Effect::ENDLESS)
{
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);

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
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/api)
@ -12,6 +21,11 @@ add_library(hyperion-api
${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
hyperion
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": {
"type" : "string",
"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

@ -20,7 +20,8 @@
<file alias="schema-videomode">JSONRPC_schema/schema-videomode.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-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-->
<file alias="schema-transform">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/LedDeviceFactory.h>
#include <HyperionConfig.h> // Required to determine the cmake options
#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/QJsonSchemaChecker.h>
#include <HyperionConfig.h>
@ -41,7 +79,10 @@
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)
{
_noListener = noListener;
@ -86,7 +127,7 @@ bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced)
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;
QJsonObject message;
@ -174,6 +215,8 @@ proceed:
handleInstanceCommand(message, command, tan);
else if (command == "leddevice")
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
else if (command == "clearall")
@ -187,17 +230,17 @@ proceed:
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);
int priority = message["priority"].toInt();
int duration = message["duration"].toInt(-1);
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;
// TODO faster copy
for (const auto& entry : jsonColor)
for (const auto &entry : jsonColor)
{
colors.emplace_back(uint8_t(entry.toInt()));
}
@ -206,7 +249,7 @@ void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& comm
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);
@ -230,7 +273,7 @@ void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& comm
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);
@ -249,19 +292,19 @@ void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& com
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);
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());
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
QJsonObject result;
@ -304,7 +347,7 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, i
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;
@ -315,9 +358,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
activePriorities.removeAll(255);
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;
item["priority"] = priority;
if (priorityInfo.timeoutTime_ms > 0)
@ -349,9 +392,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// add HSL Value to Array
QJsonArray HSLValue;
ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red,
priorityInfo.ledColors.begin()->green,
priorityInfo.ledColors.begin()->blue,
Hue, Saturation, Luminace);
priorityInfo.ledColors.begin()->green,
priorityInfo.ledColors.begin()->blue,
Hue, Saturation, Luminace);
HSLValue.append(Hue);
HSLValue.append(Saturation);
@ -362,8 +405,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
}
(priority == currentPriority)
? priorities.prepend(item)
: priorities.append(item);
? priorities.prepend(item)
: priorities.append(item);
}
info["priorities"] = priorities;
@ -371,9 +414,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// collect adjustment information
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)
{
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
QJsonArray effects;
const std::list<EffectDefinition>& effectsDefinitions = _hyperion->getEffects();
for (const EffectDefinition& effectDefinition : effectsDefinitions)
const std::list<EffectDefinition> &effectsDefinitions = _hyperion->getEffects();
for (const EffectDefinition &effectDefinition : effectsDefinitions)
{
QJsonObject effect;
effect["name"] = effectDefinition.name;
@ -467,11 +510,18 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
QJsonObject grabbers;
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
@ -480,55 +530,20 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
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
grabbers["available"] = availableGrabbers;
info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode()));
info["grabbers"] = grabbers;
QJsonObject cecInfo;
#if defined(ENABLE_CEC)
cecInfo["enabled"] = true;
#else
cecInfo["enabled"] = false;
#endif
info["cec"] = cecInfo;
// get available components
QJsonArray component;
std::map<hyperion::Components, bool> components = _hyperion->getComponentRegister().getRegister();
@ -547,7 +562,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// add sessions
QJsonArray sessions;
#ifdef ENABLE_AVAHI
for (auto session : BonjourBrowserWrapper::getInstance()->getAllServices())
for (auto session: BonjourBrowserWrapper::getInstance()->getAllServices())
{
if (session.port < 0)
continue;
@ -564,7 +579,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
#endif
// add instance info
QJsonArray instanceInfo;
for (const auto& entry : API::getAllInstanceData())
for (const auto &entry : API::getAllInstanceData())
{
QJsonObject obj;
obj.insert("friendly_name", entry["friendly_name"].toString());
@ -586,7 +601,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// TRANSFORM INFORMATION (DEFAULT VALUES)
QJsonArray transformArray;
for (const QString& transformId : _hyperion->getAdjustmentIds())
for (const QString &transformId : _hyperion->getAdjustmentIds())
{
QJsonObject transform;
QJsonArray blacklevel, whitelevel, gamma, threshold;
@ -617,7 +632,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// ACTIVE EFFECT INFO
QJsonArray activeEffects;
for (const ActiveEffectDefinition& activeEffectDefinition : _hyperion->getActiveEffects())
for (const ActiveEffectDefinition &activeEffectDefinition : _hyperion->getActiveEffects())
{
if (activeEffectDefinition.priority != PriorityMuxer::LOWEST_PRIORITY - 1)
{
@ -634,15 +649,15 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// ACTIVE STATIC LED COLOR
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())
{
QJsonObject LEDcolor;
// check if LED Color not Black (0,0,0)
if ((priorityInfo.ledColors.begin()->red +
priorityInfo.ledColors.begin()->green +
priorityInfo.ledColors.begin()->blue !=
0))
priorityInfo.ledColors.begin()->green +
priorityInfo.ledColors.begin()->blue !=
0))
{
QJsonObject LEDcolor;
@ -659,9 +674,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
// add HSL Value to Array
QJsonArray HSLValue;
ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red,
priorityInfo.ledColors.begin()->green,
priorityInfo.ledColors.begin()->blue,
Hue, Saturation, Luminace);
priorityInfo.ledColors.begin()->green,
priorityInfo.ledColors.begin()->blue,
Hue, Saturation, Luminace);
HSLValue.append(Hue);
HSLValue.append(Saturation);
@ -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);
int priority = message["priority"].toInt();
@ -720,7 +735,7 @@ void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& comm
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);
QString replyMsg;
@ -728,12 +743,12 @@ void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& c
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());
ColorAdjustment* colorAdjustment = _hyperion->getAdjustment(adjustmentId);
ColorAdjustment *colorAdjustment = _hyperion->getAdjustment(adjustmentId);
if (colorAdjustment == nullptr)
{
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"))
{
const QJsonArray& values = adjustment["red"].toArray();
const QJsonArray &values = adjustment["red"].toArray();
colorAdjustment->_rgbRedAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
}
if (adjustment.contains("green"))
{
const QJsonArray& values = adjustment["green"].toArray();
const QJsonArray &values = adjustment["green"].toArray();
colorAdjustment->_rgbGreenAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
}
if (adjustment.contains("blue"))
{
const QJsonArray& values = adjustment["blue"].toArray();
const QJsonArray &values = adjustment["blue"].toArray();
colorAdjustment->_rgbBlueAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
}
if (adjustment.contains("cyan"))
{
const QJsonArray& values = adjustment["cyan"].toArray();
const QJsonArray &values = adjustment["cyan"].toArray();
colorAdjustment->_rgbCyanAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
}
if (adjustment.contains("magenta"))
{
const QJsonArray& values = adjustment["magenta"].toArray();
const QJsonArray &values = adjustment["magenta"].toArray();
colorAdjustment->_rgbMagentaAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
}
if (adjustment.contains("yellow"))
{
const QJsonArray& values = adjustment["yellow"].toArray();
const QJsonArray &values = adjustment["yellow"].toArray();
colorAdjustment->_rgbYellowAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt());
}
if (adjustment.contains("white"))
{
const QJsonArray& values = adjustment["white"].toArray();
const QJsonArray &values = adjustment["white"].toArray();
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);
}
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"))
{
@ -832,7 +847,7 @@ void JsonAPI::handleSourceSelectCommand(const QJsonObject& message, const QStrin
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 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"))
{
QJsonObject config = message["config"].toObject();
if (API::isHyperionEnabled())
{
if (API::saveSettings(config))
if ( API::saveSettings(config) )
{
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
QJsonObject schemaJson, alldevices, properties;
@ -912,7 +927,7 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString&
{
schemaJson = QJsonFactory::readSchema(schemaFile);
}
catch (const std::runtime_error& error)
catch (const std::runtime_error &error)
{
throw std::runtime_error(error.what());
}
@ -949,9 +964,9 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString&
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");
bool compState = componentState["state"].toBool(true);
QString replyMsg;
@ -964,7 +979,7 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QStr
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
QString subcommand = message["subcommand"].toString("");
@ -978,22 +993,22 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString&
_streaming_leds_reply["command"] = command + "-ledstream-update";
_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;
// necessary because Qt::UniqueConnection for lambdas does not work until 5.9
// see: https://bugreports.qt.io/browse/QTBUG-52438
if (!_ledStreamConnection)
_ledStreamConnection = connect(_ledStreamTimer, &QTimer::timeout, this, [=]() {
emit streamLedcolorsUpdate(_currentLedValues);
},
Qt::UniqueConnection);
emit streamLedcolorsUpdate(_currentLedValues);
},
Qt::UniqueConnection);
// start the timer
if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval)
_ledStreamTimer->start(streaming_interval);
},
Qt::UniqueConnection);
},
Qt::UniqueConnection);
// push once
_hyperion->update();
}
@ -1023,7 +1038,7 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString&
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
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")));
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")));
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& id = message["id"].toString().trimmed();
const QString& password = message["password"].toString().trimmed();
const QString& newPassword = message["newPassword"].toString().trimmed();
const QString& comment = message["comment"].toString().trimmed();
const QString &subc = message["subcommand"].toString().trimmed();
const QString &id = message["id"].toString().trimmed();
const QString &password = message["password"].toString().trimmed();
const QString &newPassword = message["newPassword"].toString().trimmed();
const QString &comment = message["comment"].toString().trimmed();
// catch test if auth is required
if (subc == "tokenRequired")
@ -1194,8 +1209,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (subc == "requestToken")
{
// use id/comment
const QString& comment = message["comment"].toString().trimmed();
const bool& acc = message["accept"].toBool(true);
const QString &comment = message["comment"].toString().trimmed();
const bool &acc = message["accept"].toBool(true);
if (acc)
API::setNewTokenRequest(comment, id, tan);
else
@ -1211,7 +1226,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (API::getPendingTokenRequests(vec))
{
QJsonArray arr;
for (const auto& entry : vec)
for (const auto &entry : vec)
{
QJsonObject obj;
obj["comment"] = entry.comment;
@ -1233,7 +1248,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (subc == "answerRequest")
{
// use id
const bool& accept = message["accept"].toBool(false);
const bool &accept = message["accept"].toBool(false);
if (!API::handlePendingTokenRequest(id, accept))
sendErrorReply("No Authorization", command + "-" + subc, tan);
return;
@ -1246,7 +1261,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
if (API::getTokenList(defVect))
{
QJsonArray tArr;
for (const auto& entry : defVect)
for (const auto &entry : defVect)
{
QJsonObject subO;
subO["comment"] = entry.comment;
@ -1265,7 +1280,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString&
// login
if (subc == "login")
{
const QString& token = message["token"].toString().trimmed();
const QString &token = message["token"].toString().trimmed();
// catch token
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 quint8& inst = message["instance"].toInt();
const QString& name = message["name"].toString();
const QString &subc = message["subcommand"].toString();
const quint8 &inst = message["instance"].toInt();
const QString &name = message["name"].toString();
if (subc == "switchTo")
{
@ -1334,7 +1349,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& c
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))
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& devType = message["ledDeviceType"].toString().trimmed();
const QString &subc = message["subcommand"].toString().trimmed();
const QString &devType = message["ledDeviceType"].toString().trimmed();
QString full_command = command + "-" + subc;
@ -1399,7 +1414,7 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString&
sendErrorReply("Unknown device", full_command, tan);
}
else
*/ {
*/ {
QJsonObject config;
config.insert("type", devType);
LedDevice* ledDevice = nullptr;
@ -1407,27 +1422,27 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString&
if (subc == "discover")
{
ledDevice = LedDeviceFactory::construct(config);
const QJsonObject& params = message["params"].toObject();
const QJsonObject &params = message["params"].toObject();
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);
}
else if (subc == "getProperties")
{
ledDevice = LedDeviceFactory::construct(config);
const QJsonObject& params = message["params"].toObject();
const QJsonObject &params = message["params"].toObject();
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);
}
else if (subc == "identify")
{
ledDevice = LedDeviceFactory::construct(config);
const QJsonObject& params = message["params"].toObject();
const QJsonObject &params = message["params"].toObject();
ledDevice->identify(params);
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);
}
void JsonAPI::sendSuccessReply(const QString& command, int tan)
void JsonAPI::sendSuccessReply(const QString &command, int tan)
{
// create reply
QJsonObject reply;
@ -1458,7 +1613,7 @@ void JsonAPI::sendSuccessReply(const QString& command, int tan)
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;
reply["success"] = true;
@ -1472,7 +1627,7 @@ void JsonAPI::sendSuccessDataReply(const QJsonDocument& doc, const QString& comm
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
QJsonObject reply;
@ -1485,12 +1640,12 @@ void JsonAPI::sendErrorReply(const QString& error, const QString& command, int t
emit callbackMessage(reply);
}
void JsonAPI::streamLedcolorsUpdate(const std::vector<ColorRgb>& ledColors)
void JsonAPI::streamLedcolorsUpdate(const std::vector<ColorRgb> &ledColors)
{
QJsonObject result;
QJsonArray leds;
for (const auto& color : ledColors)
for (const auto &color : ledColors)
{
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);
}
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;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
@ -1516,7 +1671,7 @@ void JsonAPI::setImage(const Image<ColorRgb>& image)
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;
QJsonArray messageArray;
@ -1524,7 +1679,7 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg)
if (!_streaming_logging_activated)
{
_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++)
{
message["appName"] = logBuffer->at(i).appName;
@ -1560,7 +1715,7 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg)
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;
obj["comment"] = comment;
@ -1570,7 +1725,7 @@ void JsonAPI::newPendingTokenRequest(const QString& id, const QString& comment)
sendSuccessDataReply(QJsonDocument(obj), "authorize-tokenRequest", 1);
}
void JsonAPI::handleTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan)
void JsonAPI::handleTokenResponse(bool success, const QString &token, const QString &comment, const QString &id, const int &tan)
{
const QString cmd = "authorize-requestToken";
QJsonObject result;
@ -1584,7 +1739,7 @@ void JsonAPI::handleTokenResponse(bool success, const QString& token, const QStr
sendErrorReply("Token request timeout or denied", cmd, tan);
}
void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name)
void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString &name)
{
switch (state)
{

View File

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

View File

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

View File

@ -2,7 +2,6 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <QFile>
// Linux includes
#include <errno.h>
@ -12,156 +11,323 @@
#include <sys/stat.h>
#include <sys/types.h>
// qt
#include <QFile>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSize>
// Local includes
#include <utils/Logger.h>
#include <grabber/AmlogicGrabber.h>
#include "Amvideocap.h"
#define VIDEO_DEVICE "/dev/amvideo"
#define CAPTURE_DEVICE "/dev/amvideocap0"
// Constants
namespace {
const bool verbose = false;
AmlogicGrabber::AmlogicGrabber(unsigned width, unsigned height)
: Grabber("AMLOGICGRABBER", qMax(160u, width), qMax(160u, height)) // Minimum required width or height is 160
, _captureDev(-1)
, _videoDev(-1)
, _lastError(0)
, _fbGrabber("/dev/fb0",width,height)
, _grabbingModeNotification(0)
const char DEFAULT_FB_DEVICE[] = "/dev/fb0";
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)
, _videoDev(-1)
, _lastError(0)
, _fbGrabber(DEFAULT_FB_DEVICE)
, _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();
_useImageResampler = true;
}
AmlogicGrabber::~AmlogicGrabber()
{
closeDev(_captureDev);
closeDev(_videoDev);
closeDevice(_captureDev);
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)
{
fd = open(dev, O_RDWR);
fd = ::open(dev, O_RDWR);
}
return fd >= 0;
}
void AmlogicGrabber::closeDev(int &fd)
void AmlogicGrabber::closeDevice(int &fd)
{
if (fd >= 0)
{
close(fd);
::close(fd);
fd = -1;
}
}
bool AmlogicGrabber::isVideoPlaying()
{
if(!QFile::exists(VIDEO_DEVICE)) return false;
bool rc = false;
if(QFile::exists(DEFAULT_VIDEO_DEVICE))
{
int videoDisabled = 1;
if (!openDev(_videoDev, VIDEO_DEVICE))
{
Error(_log, "Failed to open video device(%s): %d - %s", VIDEO_DEVICE, errno, strerror(errno));
return false;
}
else
{
// Check the video disabled flag
if(ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0)
int videoDisabled = 1;
if (!openDevice(_videoDev, DEFAULT_VIDEO_DEVICE))
{
Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno));
closeDev(_videoDev);
return false;
Error(_log, "Failed to open video device(%s): %d - %s", DEFAULT_VIDEO_DEVICE, errno, strerror(errno));
}
else
{
// Check the video disabled flag
if(ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0)
{
Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno));
closeDevice(_videoDev);
}
else
{
if ( videoDisabled == 0 )
{
rc = true;
}
}
}
}
return videoDisabled == 0;
}
return rc;
}
int AmlogicGrabber::grabFrame(Image<ColorRgb> & image)
{
if (!_enabled) return 0;
// Make sure video is playing, else there is nothing to grab
if (isVideoPlaying())
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
if (_grabbingModeNotification!=1)
// Make sure video is playing, else there is nothing to grab
if (isVideoPlaying())
{
Info(_log, "VPU mode");
_grabbingModeNotification = 1;
_lastError = 0;
}
if (_grabbingModeNotification!=1)
{
Info(_log, "Switch to VPU capture mode");
_grabbingModeNotification = 1;
_lastError = 0;
}
if (grabFrame_amvideocap(image) < 0)
closeDev(_captureDev);
}
else
{
if (_grabbingModeNotification!=2)
if (grabFrame_amvideocap(image) < 0) {
closeDevice(_captureDev);
rc = -1;
}
}
else
{
Info( _log, "FB mode");
_grabbingModeNotification = 2;
_lastError = 0;
if (_grabbingModeNotification!=2)
{
Info( _log, "Switch to Framebuffer capture mode");
_grabbingModeNotification = 2;
_lastError = 0;
}
rc = _fbGrabber.grabFrame(image);
//usleep(50 * 1000);
}
_fbGrabber.grabFrame(image);
usleep(50 * 1000);
}
return 0;
return rc;
}
int AmlogicGrabber::grabFrame_amvideocap(Image<ColorRgb> & image)
{
int rc = 0;
// If the device is not open, attempt to open it
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));
_lastError = 1;
return -1;
rc = -1;
return rc;
}
}
long r1 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, _width);
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 r4 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WAIT_MAX_MS, 500);
long r1 = ioctl(_captureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, _width);
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 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));
_lastError = 2;
rc = -1;
}
else
{
int linelen = ((_width + 31) & ~31) * 3;
size_t _bytesToRead = linelen * _height;
// Read the snapshot into the memory
ssize_t bytesRead = pread(_captureDev, _image_ptr, _bytesToRead, 0);
if (bytesRead < 0)
{
ErrorIf(_lastError != 2,_log,"Failed to configure capture device (%d - %s)", errno, strerror(errno));
_lastError = 2;
return -1;
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));
_lastError = 3;
}
rc = -1;
}
else
{
if (static_cast<ssize_t>(_bytesToRead) != bytesRead)
{
// Read of snapshot failed
ErrorIf(_lastError != 4, _log,"Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", _bytesToRead, bytesRead);
_lastError = 4;
rc = -1;
}
else {
_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);
}
if (!video_inputs.isEmpty())
{
inputsDiscovered["device"] = "amlogic";
inputsDiscovered["device_name"] = "AmLogic";
inputsDiscovered["type"] = "screen";
inputsDiscovered["video_inputs"] = video_inputs;
}
}
// Read the snapshot into the memory
ssize_t bytesRead = pread(_captureDev, _image_ptr, _bytesToRead, 0);
if (bytesRead < 0)
if (inputsDiscovered.isEmpty())
{
ErrorIf(_lastError != 3, _log,"Read of device failed: %d - %s", errno, strerror(errno));
_lastError = 3;
return -1;
}
else if (_bytesToRead != bytesRead)
{
// Read of snapshot failed
ErrorIf(_lastError != 4, _log,"Capture failed to grab entire image [bytesToRead(%d) != bytesRead(%d)]", _bytesToRead, bytesRead);
_lastError = 4;
return -1;
DebugIf(verbose, _log, "No displays found to capture from!");
}
_useImageResampler = true;
_imageResampler.processImage((const uint8_t*)_image_ptr, _width, _height, (_width << 1) + _width, PixelFormat::BGR24, image);
_lastError = 0;
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return 0;
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>
AmlogicWrapper::AmlogicWrapper(unsigned grabWidth, unsigned grabHeight)
: GrabberWrapper("AmLogic", &_grabber, grabWidth, grabHeight)
, _grabber(grabWidth, grabHeight)
{}
AmlogicWrapper::AmlogicWrapper(int pixelDecimation, int updateRate_Hz)
: GrabberWrapper("Amlogic", &_grabber, updateRate_Hz)
, _grabber()
{
_grabber.setPixelDecimation(pixelDecimation);
}
void AmlogicWrapper::action()
{

View File

@ -11,11 +11,35 @@
#define CAP_FLAG_AT_TIME_WINDOW 1
#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_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_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 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,"d3dx9.lib")
DirectXGrabber::DirectXGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display)
: Grabber("DXGRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom)
, _pixelDecimation(pixelDecimation)
// Constants
namespace {
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))
, _displayWidth(0)
, _displayHeight(0)
@ -15,8 +19,6 @@ DirectXGrabber::DirectXGrabber(int cropLeft, int cropRight, int cropTop, int cro
, _device(nullptr)
, _surface(nullptr)
{
// init
setupDisplay();
}
DirectXGrabber::~DirectXGrabber()
@ -140,15 +142,24 @@ bool DirectXGrabber::setupDisplay()
int DirectXGrabber::grabFrame(Image<ColorRgb> & image)
{
if (!_enabled)
if (!_isEnabled)
{
qDebug() << "AUS";
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)))
{
// reinit, this will disable capture on failure
Error(_log, "Unable to get Buffer Surface Data");
setEnabled(setupDisplay());
return -1;
return 0;
}
D3DXLoadSurfaceFromSurface(_surfaceDest, nullptr, nullptr, _surface, nullptr, _srcRect, D3DX_DEFAULT, 0);
@ -181,22 +192,91 @@ void DirectXGrabber::setVideoMode(VideoMode mode)
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);
setupDisplay();
}
void DirectXGrabber::setDisplayIndex(int index)
bool DirectXGrabber::setDisplayIndex(int index)
{
bool rc (true);
if(_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>
DirectXWrapper::DirectXWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, const unsigned updateRate_Hz)
: GrabberWrapper("DirectX", &_grabber, 0, 0, updateRate_Hz)
, _grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation, display)
{}
DirectXWrapper::DirectXWrapper( int updateRate_Hz,
int 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()
{

View File

@ -3,48 +3,34 @@
#include <cassert>
#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
#include "grabber/DispmanxFrameGrabber.h"
DispmanxFrameGrabber::DispmanxFrameGrabber(unsigned width, unsigned height)
: Grabber("DISPMANXGRABBER", 0, 0)
, _vc_display(0)
, _vc_resource(0)
, _vc_flags(0)
, _captureBuffer(new ColorRgba[0])
, _captureBufferSize(0)
, _image_rgba(width, height)
DispmanxFrameGrabber::DispmanxFrameGrabber()
: Grabber("DISPMANXGRABBER")
, _vc_display(0)
, _vc_resource(0)
, _vc_flags(DISPMANX_TRANSFORM_T(0))
, _captureBuffer(new ColorRgba[0])
, _captureBufferSize(0)
, _image_rgba()
{
_useImageResampler = false;
// Initiase BCM
_useImageResampler = true;
// Initialise BCM
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()
@ -55,6 +41,28 @@ DispmanxFrameGrabber::~DispmanxFrameGrabber()
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()
{
delete[] _captureBuffer;
@ -64,152 +72,219 @@ void DispmanxFrameGrabber::freeResources()
bool DispmanxFrameGrabber::setWidthHeight(int width, int height)
{
bool rc = false;
if(Grabber::setWidthHeight(width, height))
{
if(_vc_resource != 0)
if(_vc_resource != 0) {
vc_dispmanx_resource_delete(_vc_resource);
// Create the resources for capturing image
}
Debug(_log,"Create the resources for capturing image");
uint32_t vc_nativeImageHandle;
_vc_resource = vc_dispmanx_resource_create(
VC_IMAGE_RGBA32,
width,
height,
&vc_nativeImageHandle);
VC_IMAGE_RGBA32,
width,
height,
&vc_nativeImageHandle);
assert(_vc_resource);
// Define the capture rectangle with the same size
vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height);
return true;
if (_vc_resource != 0)
{
Debug(_log,"Define the capture rectangle with the same size");
vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height);
rc = true;
}
}
return false;
return rc;
}
void DispmanxFrameGrabber::setFlags(int vc_flags)
void DispmanxFrameGrabber::setFlags(DISPMANX_TRANSFORM_T vc_flags)
{
_vc_flags = vc_flags;
}
int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
{
if (!_enabled) return 0;
int ret;
// vc_dispmanx_resource_read_data doesn't seem to work well
// with arbitrary positions so we have to handle cropping by ourselves
unsigned cropLeft = _cropLeft;
unsigned cropRight = _cropRight;
unsigned cropTop = _cropTop;
unsigned cropBottom = _cropBottom;
if (_vc_flags & DISPMANX_SNAPSHOT_FILL)
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
// disable cropping, we are capturing the video overlay window
cropLeft = cropRight = cropTop = cropBottom = 0;
}
// vc_dispmanx_resource_read_data doesn't seem to work well
// with arbitrary positions so we have to handle cropping by ourselves
int cropLeft = _cropLeft;
int cropRight = _cropRight;
int cropTop = _cropTop;
int cropBottom = _cropBottom;
unsigned imageWidth = _width - cropLeft - cropRight;
unsigned imageHeight = _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
if (image.width() != imageWidth || image.height() != imageHeight)
{
image.resize(imageWidth, imageHeight);
}
if (_image_rgba.width() != imageWidth || _image_rgba.height() != imageHeight)
{
_image_rgba.resize(imageWidth, imageHeight);
}
// Open the connection to the display
_vc_display = vc_dispmanx_display_open(0);
if (_vc_display < 0)
{
Error(_log, "Cannot open display: %d", _vc_display);
return -1;
}
// Create the snapshot (incl down-scaling)
ret = vc_dispmanx_snapshot(_vc_display, _vc_resource, (DISPMANX_TRANSFORM_T) _vc_flags);
if (ret < 0)
{
Error(_log, "Snapshot failed: %d", ret);
vc_dispmanx_display_close(_vc_display);
return ret;
}
// Read the snapshot into the memory
void* imagePtr = _image_rgba.memptr();
void* capturePtr = imagePtr;
unsigned imagePitch = imageWidth * sizeof(ColorRgba);
// dispmanx seems to require the pitch to be a multiple of 64
unsigned capturePitch = (_rectangle.width * sizeof(ColorRgba) + 63) & (~63);
// grab to temp buffer if image pitch isn't valid or if we are cropping
if (imagePitch != capturePitch
|| (unsigned)_rectangle.width != imageWidth
|| (unsigned)_rectangle.height != imageHeight)
{
// check if we need to resize the capture buffer
unsigned captureSize = capturePitch * _rectangle.height / sizeof(ColorRgba);
if (_captureBufferSize != captureSize)
if (_vc_flags & DISPMANX_SNAPSHOT_FILL)
{
delete[] _captureBuffer;
_captureBuffer = new ColorRgba[captureSize];
_captureBufferSize = captureSize;
// 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;
}
capturePtr = &_captureBuffer[0];
}
unsigned imageWidth = static_cast<unsigned>(_width - cropLeft - cropRight);
unsigned imageHeight = static_cast<unsigned>(_height - cropTop - cropBottom);
ret = vc_dispmanx_resource_read_data(_vc_resource, &_rectangle, capturePtr, capturePitch);
if (ret < 0)
{
Error(_log, "vc_dispmanx_resource_read_data failed: %d", ret);
vc_dispmanx_display_close(_vc_display);
return ret;
}
// copy capture data to image if we captured to temp buffer
if (imagePtr != capturePtr)
{
// adjust source pointer to top/left cropping
uint8_t* src_ptr = (uint8_t*) capturePtr
+ cropLeft * sizeof(ColorRgba)
+ cropTop * capturePitch;
for (unsigned y = 0; y < imageHeight; y++)
// resize the given image if needed
if (image.width() != imageWidth || image.height() != imageHeight)
{
memcpy((uint8_t*)imagePtr + y * imagePitch,
src_ptr + y * capturePitch,
imagePitch);
image.resize(imageWidth, imageHeight);
}
if (_image_rgba.width() != imageWidth || _image_rgba.height() != imageHeight)
{
_image_rgba.resize(imageWidth, imageHeight);
}
// Open the connection to the display
_vc_display = vc_dispmanx_display_open(DEFAULT_DEVICE);
if (_vc_display < 0)
{
Error(_log, "Cannot open display: %d", DEFAULT_DEVICE);
rc = -1;
}
else {
// Create the snapshot (incl down-scaling)
int ret = vc_dispmanx_snapshot(_vc_display, _vc_resource, _vc_flags);
if (ret < 0)
{
Error(_log, "Snapshot failed: %d", ret);
rc = ret;
}
else
{
// Read the snapshot into the memory
void* imagePtr = _image_rgba.memptr();
void* capturePtr = imagePtr;
unsigned imagePitch = imageWidth * sizeof(ColorRgba);
// dispmanx seems to require the pitch to be a multiple of 64
unsigned capturePitch = (_rectangle.width * sizeof(ColorRgba) + 63) & (~63);
// grab to temp buffer if image pitch isn't valid or if we are cropping
if (imagePitch != capturePitch
|| static_cast<unsigned>(_rectangle.width) != imageWidth
|| static_cast<unsigned>(_rectangle.height) != imageHeight)
{
// check if we need to resize the capture buffer
unsigned captureSize = capturePitch * static_cast<unsigned>(_rectangle.height) / sizeof(ColorRgba);
if (_captureBufferSize != captureSize)
{
delete[] _captureBuffer;
_captureBuffer = new ColorRgba[captureSize];
_captureBufferSize = captureSize;
}
capturePtr = &_captureBuffer[0];
}
ret = vc_dispmanx_resource_read_data(_vc_resource, &_rectangle, capturePtr, capturePitch);
if (ret < 0)
{
Error(_log, "vc_dispmanx_resource_read_data failed: %d", ret);
rc = ret;
}
else
{
_imageResampler.processImage(static_cast<uint8_t*>(capturePtr),
_width,
_height,
static_cast<int>(capturePitch),
PixelFormat::RGB32,
image);
}
}
vc_dispmanx_display_close(_vc_display);
}
}
// Close the displaye
vc_dispmanx_display_close(_vc_display);
// image to output image
_image_rgba.toRgb(image);
return 0;
return rc;
}
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;
const int __screenWidth = 800;
const int __screenHeight = 600;
const int __display_num = 0;
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->height = __screenHeight;
vc_info->display_num = __display_num;
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;
}
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++;
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)
{
color[0] = ColorRgba::WHITE;
0 color[1] = ColorRgba::RED;
color[1] = ColorRgba::RED;
color[2] = ColorRgba::BLUE;
color[3] = ColorRgba::GREEN;
}

View File

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

View File

@ -10,102 +10,261 @@
// STL includes
#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
#include <grabber/FramebufferFrameGrabber.h>
FramebufferFrameGrabber::FramebufferFrameGrabber(const QString & device, unsigned width, unsigned height)
: Grabber("FRAMEBUFFERGRABBER", width, height)
, _fbDevice()
FramebufferFrameGrabber::FramebufferFrameGrabber(const QString & device)
: Grabber("FRAMEBUFFERGRABBER")
, _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)
{
if (!_enabled) return 0;
int rc = 0;
struct fb_var_screeninfo vinfo;
unsigned capSize, bytesPerPixel;
PixelFormat pixelFormat;
/* Open the framebuffer device */
int fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (fbfd == -1)
if (_isEnabled && !_isDeviceInError)
{
Error(_log, "Error opening %s, %s : ", QSTRING_CSTR(_fbDevice), std::strerror(errno));
return -1;
}
/* get variable screen information */
ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo);
bytesPerPixel = vinfo.bits_per_pixel / 8;
capSize = vinfo.xres * vinfo.yres * bytesPerPixel;
switch (vinfo.bits_per_pixel)
{
case 16: pixelFormat = PixelFormat::BGR16; break;
case 24: pixelFormat = PixelFormat::BGR24; break;
#ifdef ENABLE_AMLOGIC
case 32: pixelFormat = PixelFormat::PIXELFORMAT_RGB32; break;
#else
case 32: pixelFormat = PixelFormat::BGR32; break;
#endif
default:
Error(_log, "Unknown pixel format: %d bits per pixel", vinfo.bits_per_pixel);
close(fbfd);
return -1;
}
/* 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);
_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)
{
if(_fbDevice != path)
{
_fbDevice = path;
int result;
struct fb_var_screeninfo vinfo;
// Check if the framebuffer device can be opened and display the current resolution
int fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (fbfd == -1)
if ( getScreenInfo() )
{
Error(_log, "Error opening %s, %s : ", QSTRING_CSTR(_fbDevice), std::strerror(errno));
}
else
{
// get variable screen information
result = ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo);
if (result != 0)
{
Error(_log, "Could not get screen information, %s", std::strerror(errno));
/* 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
{
Info(_log, "Display opened with resolution: %dx%d@%dbit", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
_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 */
_fbfd = ::open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (_fbfd < 0)
{
QString errorReason = QString ("Error opening %1, [%2] %3").arg(_fbDevice).arg(errno).arg(std::strerror(errno));
this->setInError ( errorReason );
rc = false;
}
return rc;
}
bool FramebufferFrameGrabber::closeDevice()
{
bool rc = false;
if (_fbfd >= 0)
{
if( ::close(_fbfd) == 0) {
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
case 32: _pixelFormat = PixelFormat::PIXELFORMAT_RGB32;
break;
#else
case 32: _pixelFormat = PixelFormat::BGR32;
break;
#endif
default:
rc= false;
QString errorReason = QString ("Unknown pixel format: %1 bits per pixel").arg(static_cast<int>(_varInfo.bits_per_pixel));
this->setInError ( errorReason );
closeDevice();
}
close(fbfd);
}
}
return rc;
}
QJsonObject FramebufferFrameGrabber::discover(const QJsonObject& params)
{
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject inputsDiscovered;
//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)
{
fbIdx = (*deviceFileIterator).fileName().rightRef(1).toInt();
QString device = (*deviceFileIterator).absoluteFilePath();
DebugIf(verbose, _log, "FB device [%s] found", QSTRING_CSTR(device));
QSize screenSize = getScreenSize(device);
if ( !screenSize.isEmpty() )
{
QJsonArray fps = { "1", "5", "10", "15", "20", "25", "30", "40", "50", "60" };
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())
{
inputsDiscovered["device"] = "framebuffer";
inputsDiscovered["device_name"] = "Framebuffer";
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

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

View File

@ -5,94 +5,204 @@
// Local includes
#include <grabber/OsxFrameGrabber.h>
OsxFrameGrabber::OsxFrameGrabber(unsigned display, unsigned width, unsigned height)
: Grabber("OSXGRABBER", width, height)
, _screenIndex(100)
//Qt
#include <QJsonObject>
#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
setDisplayIndex(display);
_isEnabled = false;
_useImageResampler = true;
}
OsxFrameGrabber::~OsxFrameGrabber()
{
}
int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image)
bool OsxFrameGrabber::setupDisplay()
{
if (!_enabled) return 0;
bool rc (false);
CGImageRef dispImage;
CFDataRef imgData;
unsigned char * pImgData;
unsigned dspWidth, dspHeight;
rc = setDisplayIndex(_screenIndex);
dispImage = CGDisplayCreateImage(_display);
// display lost, use main
if (dispImage == NULL && _display)
{
dispImage = CGDisplayCreateImage(kCGDirectMainDisplay);
// no displays connected, return
if (dispImage == NULL)
{
Error(_log, "No display connected...");
return -1;
}
}
imgData = CGDataProviderCopyData(CGImageGetDataProvider(dispImage));
pImgData = (unsigned char*) CFDataGetBytePtr(imgData);
dspWidth = CGImageGetWidth(dispImage);
dspHeight = CGImageGetHeight(dispImage);
_imageResampler.setHorizontalPixelDecimation(dspWidth/_width);
_imageResampler.setVerticalPixelDecimation(dspHeight/_height);
_imageResampler.processImage( pImgData,
dspWidth,
dspHeight,
CGImageGetBytesPerRow(dispImage),
PixelFormat::BGR32,
image);
CFRelease(imgData);
CGImageRelease(dispImage);
return 0;
return rc;
}
void OsxFrameGrabber::setDisplayIndex(int index)
int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image)
{
if(_screenIndex != index)
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
CGImageRef dispImage;
CFDataRef imgData;
unsigned char * pImgData;
unsigned dspWidth;
unsigned dspHeight;
dispImage = CGDisplayCreateImage(_display);
// display lost, use main
if (dispImage == nullptr && _display != 0)
{
dispImage = CGDisplayCreateImage(kCGDirectMainDisplay);
// no displays connected, return
if (dispImage == nullptr)
{
Error(_log, "No display connected...");
return -1;
}
}
imgData = CGDataProviderCopyData(CGImageGetDataProvider(dispImage));
pImgData = (unsigned char*) CFDataGetBytePtr(imgData);
dspWidth = CGImageGetWidth(dispImage);
dspHeight = CGImageGetHeight(dispImage);
_imageResampler.processImage( pImgData,
static_cast<int>(dspWidth),
static_cast<int>(dspHeight),
static_cast<int>(CGImageGetBytesPerRow(dispImage)),
PixelFormat::BGR32,
image);
CFRelease(imgData);
CGImageRelease(dispImage);
}
return rc;
}
bool OsxFrameGrabber::setDisplayIndex(int index)
{
bool rc (true);
if(_screenIndex != index || !_isEnabled)
{
_screenIndex = index;
CGImageRef image;
CGDisplayCount displayCount;
CGDirectDisplayID displays[8];
// get list of displays
CGGetActiveDisplayList(8, displays, &displayCount);
if (_screenIndex + 1 > displayCount)
CGDisplayCount dspyCnt = 0 ;
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);
_display = kCGDirectMainDisplay;
CGDirectDisplayID *activeDspys = new CGDirectDisplayID [dspyCnt] ;
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
{
_display = activeDspys[_screenIndex];
image = CGDisplayCreateImage(_display);
if(image == nullptr)
{
setEnabled(false);
Error(_log, "Failed to open main display, disable capture interface");
rc = false;
}
else
{
setEnabled(true);
rc = true;
Info(_log, "Display [%u] opened with resolution: %ux%u@%ubit", _display, CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerPixel(image));
}
CGImageRelease(image);
}
}
}
else
{
_display = displays[_screenIndex];
rc=false;
}
image = CGDisplayCreateImage(_display);
if(image == NULL)
{
Error(_log, "Failed to open main display, disable capture interface");
setEnabled(false);
return;
}
else
setEnabled(true);
Info(_log, "Display opened with resolution: %dx%d@%dbit", CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerPixel(image));
CGImageRelease(image);
}
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 __screenHeight = 600;
void CGGetActiveDisplayList(int max, CGDirectDisplayID *displays, CGDisplayCount *displayCount)
CGError CGGetActiveDisplayList(uint32_t maxDisplays, CGDirectDisplayID *activeDisplays, uint32_t *displayCount)
{
*displayCount = 1;
displays[0] = 1;
if (maxDisplays == 0 || activeDisplays == nullptr)
{
*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 image = new CGImage(__screenWidth, __screenHeight);
CGImageRef image = new CGImage(__screenWidth / (display+1), __screenHeight / (display+1));
return image;
}
@ -123,4 +141,19 @@ void CFRelease(CFDataRef 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

View File

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

View File

@ -7,23 +7,30 @@
#include <QGuiApplication>
#include <QWidget>
#include <QScreen>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
QtGrabber::QtGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display)
: Grabber("QTGRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom)
, _display(unsigned(display))
, _pixelDecimation(pixelDecimation)
, _screenWidth(0)
, _screenHeight(0)
, _src_x(0)
, _src_y(0)
, _src_x_max(0)
, _src_y_max(0)
, _screen(nullptr)
// Constants
namespace {
const bool verbose = false;
} //End of constants
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_y(0)
, _src_x_max(0)
, _src_y_max(0)
, _isWayland(false)
, _screen(nullptr)
, _isVirtual(false)
{
_logger = Logger::getInstance("Qt");
_useImageResampler = false;
// init
setupDisplay();
}
QtGrabber::~QtGrabber()
@ -36,51 +43,111 @@ void QtGrabber::freeResources()
// 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()
{
// cleanup last screen
freeResources();
QScreen* primary = QGuiApplication::primaryScreen();
QList<QScreen *> screens = QGuiApplication::screens();
// inject main screen at 0, if not nullptr
if(primary != nullptr)
bool result = false;
if ( ! open() )
{
screens.prepend(primary);
// remove last main screen if twice in list
if(screens.lastIndexOf(primary) > 0)
screens.removeAt(screens.lastIndexOf(primary));
if ( _isWayland )
{
Error(_log, "Grabber does not work under Wayland!");
}
}
if(screens.isEmpty())
else
{
Error(_log, "No displays found to capture from!");
return false;
// cleanup last screen
freeResources();
_numberOfSDisplays = 0;
QScreen* primary = QGuiApplication::primaryScreen();
QList<QScreen *> screens = QGuiApplication::screens();
// inject main screen at 0, if not nullptr
if(primary != nullptr)
{
screens.prepend(primary);
// remove last main screen if twice in list
if(screens.lastIndexOf(primary) > 0)
{
screens.removeAt(screens.lastIndexOf(primary));
}
}
if(screens.isEmpty())
{
Error(_log, "No displays found to capture from!");
result = false;
}
else
{
_numberOfSDisplays = screens.size();
Info(_log,"Available Displays:");
int index = 0;
for(auto * screen : qAsConst(screens))
{
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());
++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
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);
_display = 0;
}
}
// init the requested display
_screen = screens.at(_display);
connect(_screen, &QScreen::geometryChanged, this, &QtGrabber::geometryChanged);
updateScreenDimensions(true);
if (_isVirtual)
{
Info(_log, "Using virtual display across all screens");
}
else
{
Info(_log,"Initialized display %d", _display);
}
result = true;
}
}
Info(_log,"Available Displays:");
int index = 0;
for(auto screen : screens)
{
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());
index++;
}
// be sure the index is available
if(_display > unsigned(screens.size()-1))
{
Info(_log, "The requested display index '%d' is not available, falling back to display 0", _display);
_display = 0;
}
// init the requested display
_screen = screens.at(_display);
connect(_screen, &QScreen::geometryChanged, this, &QtGrabber::geometryChanged);
updateScreenDimensions(true);
Info(_log,"Initialized display %d", _display);
return true;
return result;
}
void QtGrabber::geometryChanged(const QRect &geo)
@ -91,90 +158,109 @@ void QtGrabber::geometryChanged(const QRect &geo)
int QtGrabber::grabFrame(Image<ColorRgb> & image)
{
if (!_enabled) return 0;
if(_screen == nullptr)
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
// reinit, this will disable capture on failure
setEnabled(setupDisplay());
return -1;
}
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)
for (int x=0; x<imageFrame.width(); ++x)
if(_screen == nullptr)
{
QColor inPixel(imageFrame.pixel(x,y));
ColorRgb & outPixel = image(x,y);
outPixel.red = inPixel.red();
outPixel.green = inPixel.green();
outPixel.blue = inPixel.blue();
// reinit, this will disable capture on failure
bool result = setupDisplay();
setEnabled(result);
}
return 0;
if (_isEnabled)
{
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
if (originalPixmap.isNull())
{
rc = -1;
}
else
{
QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat( QImage::Format_RGB888);
image.resize(static_cast<uint>(_calculatedWidth), static_cast<uint>(_calculatedHeight));
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)
{
if(!_screen)
if(_screen == nullptr)
{
return -1;
}
const QRect& geo = _screen->geometry();
if (!force && _screenWidth == unsigned(geo.right()) && _screenHeight == unsigned(geo.bottom()))
QRect geo;
if (_isVirtual)
{
geo = _screen->virtualGeometry();
}
else
{
geo = _screen->geometry();
}
if (!force && _width == geo.width() && _height == geo.height())
{
// No update required
return 0;
}
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, geo.right(), geo.bottom());
_screenWidth = geo.right() - geo.left();
_screenHeight = geo.bottom() - geo.top();
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _width, _height, geo.width(), geo.height());
_width = geo.width();
_height = geo.height();
int width=0, height=0;
int width=0;
int height=0;
// Image scaling is performed by Qt
width = (_screenWidth > unsigned(_cropLeft + _cropRight))
? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation)
: (_screenWidth / _pixelDecimation);
width = (_width > (_cropLeft + _cropRight))
? ((_width - _cropLeft - _cropRight) / _pixelDecimation)
: (_width / _pixelDecimation);
height = (_screenHeight > unsigned(_cropTop + _cropBottom))
? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation)
: (_screenHeight / _pixelDecimation);
height = (_height > (_cropTop + _cropBottom))
? ((_height - _cropTop - _cropBottom) / _pixelDecimation)
: (_height / _pixelDecimation);
// calculate final image dimensions and adjust top/left cropping in 3D modes
switch (_videoMode)
{
case VideoMode::VIDEO_3DSBS:
_width = width /2;
_height = height;
_calculatedWidth = width /2;
_calculatedHeight = height;
_src_x = _cropLeft / 2;
_src_y = _cropTop;
_src_x_max = (_screenWidth / 2) - _cropRight;
_src_y_max = _screenHeight - _cropBottom;
_src_x_max = (_width / 2) - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop;
break;
case VideoMode::VIDEO_3DTAB:
_width = width;
_height = height / 2;
_calculatedWidth = width;
_calculatedHeight = height / 2;
_src_x = _cropLeft;
_src_y = _cropTop / 2;
_src_x_max = _screenWidth - _cropRight;
_src_y_max = (_screenHeight / 2) - _cropBottom;
_src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = (_height / 2) - _cropBottom - _cropTop;
break;
case VideoMode::VIDEO_2D:
default:
_width = width;
_height = height;
_calculatedWidth = width;
_calculatedHeight = height;
_src_x = _cropLeft;
_src_y = _cropTop;
_src_x_max = _screenWidth - _cropRight;
_src_y_max = _screenHeight - _cropBottom;
_src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop;
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;
}
@ -184,22 +270,129 @@ void QtGrabber::setVideoMode(VideoMode mode)
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);
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);
setupDisplay();
if (index <= _numberOfSDisplays)
{
_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>
QtWrapper::QtWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, unsigned updateRate_Hz)
: GrabberWrapper("Qt", &_grabber, 0, 0, updateRate_Hz)
, _grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation, display)
{}
QtWrapper::QtWrapper( int updateRate_Hz,
int 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()
{

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