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 )
@ -122,6 +129,11 @@ elseif ( "${PLATFORM}" MATCHES "rpi" )
SET ( DEFAULT_WS281XPWM ON )
elseif ( "${PLATFORM}" STREQUAL "amlogic" )
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 )
elseif ( "${PLATFORM}" MATCHES "x11" )
@ -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

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

View File

@ -37,10 +37,10 @@
<div class="panel-body">
<div 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>

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;
@ -76,19 +96,21 @@ table.input-group{width:100%}
/*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,6 +44,7 @@
"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_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",
@ -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",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -3,8 +3,7 @@
<head>
<script>
if (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent))
{
if (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent)) {
window.location.pathname = '/content/ie_not_supported.html';
}
</script>
@ -52,6 +51,8 @@
<script src="js/lib/jquery.i18n/jquery.i18n.language.js"></script>
<script src="js/lib/jquery.i18n/CLDRPluralRuleParser.js"></script>
<script src="js/languages.js"></script>
<!-- Bootstrap Core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
@ -83,21 +84,24 @@
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![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>
<style type="text/css">
#loading_overlay, #wrapper {
display: none
}
</style>
</noscript>
<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">
<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>
@ -105,7 +109,7 @@
<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>
<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 -->
@ -166,7 +170,7 @@
<li id="btn_setlang">
<a>
<div>
<i class="fa fa-globe"></i>
<i class="fa fa-globe fa-fw"></i>
<select id="language-select" class="selectpicker" data-width="fit" data-style="btn-transparent"> </select>
</div>
</a>
@ -207,22 +211,23 @@
<li> <a class="inactive mnava" href="#dashboard"><i class="fa fa-dashboard fa-fw"></i><span data-i18n="main_menu_dashboard_token">Dashboard</span></a> </li>
<li> <a class="inactive mnava" href="#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>
<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" href="#conf_leds"><i class="mdi mdi-lightbulb-on fa-fw"></i><span data-i18n="main_menu_leds_conf_token">LED Hardware</span></a> </li>
<li> <a class="inactive mnava" id="MenuItemLeds" href="#conf_leds"><i class="mdi mdi-lightbulb-on fa-fw"></i><span data-i18n="main_menu_leds_conf_token">LED Hardware</span></a> </li>
<li> <a class="inactive mnava" id="MenuItemInstCapture" href="#conf_instcapture"><i class="fa fa-camera fa-fw"></i><span data-i18n="main_menu_instcapture_conf_token">Sources</span></a> </li>
<li> <a class="inactive mnava" href="#conf_effect"><i class="fa fa-spinner fa-fw"></i><span data-i18n="main_menu_effect_conf_token">Effects</span></a> </li>
<li> <a class="inactive mnava" href="#conf_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" 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" href="#conf_webconfig" id="load_webconfig"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_webconfig_token">Webconfiguration</span></a> </li>
<li> <a class="inactive mnava" id="MenuItemWeb" href="#conf_webconfig" id="load_webconfig"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_webconfig_token">Webconfiguration</span></a> </li>
<li> <a class="inactive mnava" id="MenuItemLogging" href="#conf_logging"><i class="fa fa-reorder fa-fw"></i><span data-i18n="main_menu_logging_token">Log</span></a> </li>
<li> <a class="inactive mnava" 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>
@ -320,6 +325,7 @@
<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>

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,12 +52,18 @@ $(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") {
if ((components[idx].name === "FORWARDER" && window.currentHyperionInstance != 0) ||
(components[idx].name === "GRABBER" && !window.serverConfig.framegrabber.enable) ||
(components[idx].name === "V4L" && !window.serverConfig.grabberV4L2.enable))
continue;
var comp_enabled = components[idx].enabled ? "checked" : "";
const general_comp = "general_comp_" + components[idx].name;
var componentBtn = '<input ' +
componentBtn = '<input ' +
'id="' + general_comp + '" ' + comp_enabled +
' type="checkbox" ' +
'data-toggle="toggle" ' +
@ -64,14 +72,15 @@ $(document).ready(function () {
'data-on="' + $.i18n('general_btn_on') + '" ' +
'data-off="' + $.i18n('general_btn_off') + '">';
tab_components += '<tr><td></td><td>' + $.i18n('general_comp_' + components[idx].name) + '</td><td style="text-align:right">' + componentBtn + '</td></tr>';
instance_components += '<tr><td></td><td>' + $.i18n('general_comp_' + components[idx].name) + '</td><td style="text-align:right">' + componentBtn + '</td></tr>';
}
}
instances_html += '<tbody>' + tab_components + '</tbody></table>';
instances_html += '<tbody>' + instance_components + '</tbody></table>';
instances_html += '</div></div></div>';
$('.instances').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

@ -6,8 +6,7 @@ $(document).ready( function() {
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)
{
if (window.showOptHelp) {
$('#conf_cont').append(createHelpTable(window.schema.general.properties, $.i18n("edt_conf_gen_heading_title")));
}
else
@ -22,12 +21,12 @@ $(document).ready( function() {
});
$('#btn_submit').off().on('click', function () {
window.showOptHelp = conf_editor.getEditor("root.general.showOptHelp").getValue();
requestWriteConfig(conf_editor.getValue());
});
// Instance handling
function handleInstanceRename(e)
{
function handleInstanceRename(e) {
conf_editor.on('change', function () {
window.readOnlyMode ? $('#btn_cl_save').attr('disabled', true) : $('#btn_submit').attr('disabled', false);
@ -46,8 +45,7 @@ $(document).ready( function() {
});
}
function handleInstanceDelete(e)
{
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 () {
@ -55,18 +53,15 @@ $(document).ready( function() {
});
}
function buildInstanceList()
{
function buildInstanceList() {
var inst = serverInfo.instance
$('.itbody').html("");
for(var key in inst)
{
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)
{
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') + '">';
@ -109,40 +104,32 @@ $(document).ready( function() {
});
//import
function dis_imp_btn(state)
{
function dis_imp_btn(state) {
state || window.readOnlyMode ? $('#btn_import_conf').attr('disabled', true) : $('#btn_import_conf').attr('disabled', false);
}
function readFile(evt)
{
function readFile(evt) {
var f = evt.target.files[0];
if (f)
{
if (f) {
var r = new FileReader();
r.onload = function(e)
{
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)
{
if (check.length != 0) {
showInfoDialog('error', "", $.i18n('infoDialog_import_jsonerror_text', f.name, JSON.stringify(check)));
dis_imp_btn(true);
}
else
{
else {
content = JSON.parse(content);
//check for hyperion json
if(typeof content.leds === 'undefined' || typeof content.general === 'undefined')
{
if (typeof content.leds === 'undefined' || typeof content.general === 'undefined') {
showInfoDialog('error', "", $.i18n('infoDialog_import_hyperror_text', f.name));
dis_imp_btn(true);
}
else
{
else {
dis_imp_btn(false);
importedConf = content;
confName = f.name;
@ -185,8 +172,7 @@ $(document).ready( function() {
});
//create introduction
if(window.showOptHelp)
{
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");

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

File diff suppressed because it is too large Load Diff

View File

@ -313,6 +313,11 @@ $(document).ready(function () {
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 (!conf_editor.validate().length) {
if (canIdentify) {
$("#btn_test_controller").removeClass('hidden');
$("#btn_test_controller").show();
$('#btn_test_controller').attr('disabled', false);
}
else {
} else {
$('#btn_test_controller').hide();
$('#btn_test_controller').attr('disabled', true);
}
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
if (hardwareLedCount < 1) {
} else {
canSave = false;
}
@ -886,9 +887,9 @@ $(document).ready(function () {
conf_editor.getEditor(specOptPath + "host").setValue(val);
break;
}
}
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

@ -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');

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,6 +33,8 @@ window.comps = [];
window.defaultPasswordIsSet = null;
tokenList = {};
const ENDLESS = -1;
function initRestart()
{
$(window.hyperion).off();
@ -470,8 +472,14 @@ async function requestLedDeviceProperties(type, params)
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));
}
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

@ -4,13 +4,21 @@ $(document).ready(function() {
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, toggleLedsNum = false;
var toggleLeds = false;
var toggleLedsNum = false;
var toggleSigDetectArea = false;
var activeComponent = "";
const image = new Image()
image.src = "img/hyperion/logo_negativ.png",
/// add prototype for simple canvas clear() method
CanvasRenderingContext2D.prototype.clear = function () {
@ -19,8 +27,7 @@ $(document).ready(function() {
function create2dPaths() {
twoDPaths = [];
for(var idx=0; idx<leds.length; idx++)
{
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));
}
@ -69,16 +76,15 @@ $(document).ready(function() {
$(window.hyperion).one("ready", function () {
leds = window.serverConfig.leds;
grabberConfig = window.serverConfig.grabberV4L2;
if(window.showOptHelp)
{
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")
}
if(getStorage('ledsim_width') != null)
{
if (getStorage('ledsim_width') != null) {
ledsim_width = getStorage('ledsim_width');
ledsim_height = getStorage('ledsim_height');
}
@ -98,8 +104,7 @@ $(document).ready(function() {
updateLedLayout();
},
opened: function (e) {
if(!lC)
{
if (!lC) {
updateLedLayout();
lC = true;
}
@ -112,6 +117,7 @@ $(document).ready(function() {
},
closed: function (e) {
modalOpened = false;
lC = false;
},
resizeStop: function (e) {
setStorage("ledsim_width", $("#ledsim_dialog").outerWidth());
@ -121,12 +127,23 @@ $(document).ready(function() {
// apply new serverinfos
$(window.hyperion).on("cmd-config-getconfig", function (event) {
leds = event.response.info.leds;
grabberConfig = event.response.info.grabberV4L2;
updateLedLayout();
});
});
function printLedsToCanvas(colors)
{
function printLedsToCanvas(colors) {
if (grabberConfig.enable && grabberConfig.signalDetection && toggleSigDetectArea && storedAccess === 'expert') {
sigDetectAreaCanvasNodeCtx.setLineDash([5, 5]);
sigDetectAreaCanvasNodeCtx.stroke(build2DPath(grabberConfig.sDHOffsetMin * canvas_width,
grabberConfig.sDVOffsetMin * canvas_height,
(grabberConfig.sDHOffsetMax - grabberConfig.sDHOffsetMin) * canvas_width,
(grabberConfig.sDVOffsetMax - grabberConfig.sDVOffsetMin) * canvas_height,
5));
}
// toggle leds, do not print
if (toggleLeds)
return;
@ -141,8 +158,7 @@ $(document).ready(function() {
if (colors && colors.length / 3 < leds.length)
return;
for(var idx=0; idx<leds.length; idx++)
{
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])
@ -150,10 +166,10 @@ $(document).ready(function() {
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]);
if(toggleLedsNum)
{
if (toggleLedsNum) {
//ledsCanvasNodeCtx.shadowOffsetX = 1;
//ledsCanvasNodeCtx.shadowOffsetY = 1;
//ledsCanvasNodeCtx.shadowColor = "black";
@ -168,8 +184,13 @@ $(document).ready(function() {
}
}
function updateLedLayout()
{
function updateLedLayout() {
if (grabberConfig.enable && grabberConfig.signalDetection && storedAccess === 'expert') {
$("#sigDetectArea_toggle").show();
} else {
$("#sigDetectArea_toggle").hide();
}
//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;
@ -177,14 +198,17 @@ $(document).ready(function() {
$('#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>';
$('#leds_canvas').html(leds_html);
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();
}
// ------------------------------------------------------------------
@ -198,45 +222,52 @@ $(document).ready(function() {
toggleLeds = !toggleLeds
ledsCanvasNodeCtx.clear();
toggleClass('#leds_toggle', "btn-success", "btn-danger");
if (!toggleLeds) {
$("#leds_toggle_num").show();
} else {
$("#leds_toggle_num").hide();
}
});
// ------------------------------------------------------------------
$('#leds_toggle_live_video').off().on("click", function () {
setClassByBool('#leds_toggle_live_video', window.imageStreamActive, "btn-success", "btn-danger");
if ( window.imageStreamActive )
{
if (window.imageStreamActive) {
requestLedImageStop();
resetImage();
}
else
{
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)
{
if (!modalOpened) {
requestLedColorsStop();
}
else
{
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 (!modalOpened) {
if ($('#leds_prev_toggle_live_video').length > 0)
return;
requestLedImageStop();
}
else
{
else {
var imageData = (event.response.result.image);
var image = new Image();
@ -255,25 +286,44 @@ $(document).ready(function() {
$(window.hyperion).on("cmd-settings-update", function (event) {
var obj = event.response.data
if ( obj.leds) {
console.log("ledsim: cmd-settings-update", 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
leds = window.serverConfig.leds;
grabberConfig = window.serverConfig.grabberV4L2;
updateLedLayout();
}
});
function resetImage(){
if (getStorage("darkMode", false) == "on") {
imageCanvasNodeCtx.clear();
} else {
imageCanvasNodeCtx.fillStyle = "rgb(225,225,225)"
imageCanvasNodeCtx.fillRect(0, 0, canvas_width, canvas_height);
$(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();
}
var image = document.getElementById('navbar_brand_logo');
});
function resetImage() {
if (typeof imageCanvasNodeCtx !== "undefined") {
imageCanvasNodeCtx.fillStyle = "#1c1c1c"; //90% gray
imageCanvasNodeCtx.fillRect(0, 0, canvas_width, canvas_height);
imageCanvasNodeCtx.drawImage(image, canvas_width / 2 - image.width / 2, canvas_height / 2 - image.height / 2, image.width, image.height);
}
}
});

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,10 +1,5 @@
var storedAccess;
var storedLang;
var availLang = ['cs', 'de', 'en', 'es', 'fr', 'it', 'nl', 'nb', 'pl', 'pt', 'ro', 'sv', 'vi', 'ru', 'tr', 'zh-CN'];
var availLangText = ['Čeština', 'Deutsch', 'English', 'Español', 'Français', 'Italiano', 'Nederlands', 'Norsk Bokmål', 'Polski', 'Português', 'Română', 'Svenska', 'Tiếng Việt', 'русский', 'Türkçe', '汉语'];
var availAccess = ['default', 'advanced', 'expert'];
//$.i18n.debug = true;
var storedAccess;
//Change Password
function changePassword(){
@ -28,46 +23,9 @@ function changePassword(){
$(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())
{
storedLang = getStorage("langcode");
if (storedLang == null)
{
setStorage("langcode", 'auto');
storedLang = 'auto';
initTrans(storedLang);
}
else
{
initTrans(storedLang);
}
}
else
{
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");
initTrans('auto');
storedLang = 'auto';
storedAccess = "default";
$('#btn_setlang').attr("disabled", true);
$('#language-select').attr("disabled", true);
$('#btn_setaccess').attr("disabled", true);
}
@ -75,10 +33,14 @@ $(document).ready( function() {
//access
storedAccess = getStorage("accesslevel");
if (storedAccess == null)
{
setStorage("accesslevel", "default");
if (storedAccess == null) {
storedAccess = "default";
setStorage("accesslevel", storedAccess);
}
if (!storageComp()) {
showInfoDialog('warning', $.i18n('InfoDialog_nostorage_title'), $.i18n('InfoDialog_nostorage_text'));
$('#btn_setlang').attr("disabled", true);
}
$('#btn_setaccess').off().on('click',function() {

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,6 +3,21 @@
# TurboJPEG_INCLUDE_DIRS
# TurboJPEG_LIBRARY
if (ENABLE_MF)
find_path(TurboJPEG_INCLUDE_DIRS
NAMES turbojpeg.h
PATHS
"C:/libjpeg-turbo64"
PATH_SUFFIXES include
)
find_library(TurboJPEG_LIBRARY
NAMES turbojpeg turbojpeg-static
PATHS
"C:/libjpeg-turbo64"
PATH_SUFFIXES bin lib
)
else()
find_path(TurboJPEG_INCLUDE_DIRS
NAMES turbojpeg.h
PATH_SUFFIXES include
@ -12,6 +27,7 @@ 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

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

@ -5,21 +5,79 @@
* 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);
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);
@ -31,4 +89,5 @@ 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>
// decoder thread includes
#include <grabber/EncoderThread.h>
// Determine the cmake options
#include <HyperionConfig.h>
#if defined(ENABLE_CEC)
#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>
#endif
///
/// Capture class for V4L2 devices
///
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
class V4L2Grabber : public Grabber
{
Q_OBJECT
@ -46,117 +40,66 @@ public:
struct DeviceProperties
{
QString name = QString();
QMultiMap<QString, int> inputs = QMultiMap<QString, int>();
QStringList resolutions = QStringList();
QStringList framerates = QStringList();
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,108 +52,80 @@ 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;
@ -157,6 +133,15 @@ protected:
/// the selected 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

@ -21,6 +21,7 @@
<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-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,6 +79,9 @@
using namespace hyperion;
// Constants
namespace { const bool verbose = false; }
JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject *parent, bool noListener)
: API(log, localConnection, parent)
{
@ -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")
@ -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 )
{
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();
@ -1441,6 +1456,146 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString&
}
}
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);

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,62 +11,92 @@
#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
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("/dev/fb0",width,height)
, _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))
if (!openDevice(_videoDev, DEFAULT_VIDEO_DEVICE))
{
Error(_log, "Failed to open video device(%s): %d - %s", VIDEO_DEVICE, errno, strerror(errno));
return false;
Error(_log, "Failed to open video device(%s): %d - %s", DEFAULT_VIDEO_DEVICE, errno, strerror(errno));
}
else
{
@ -75,93 +104,230 @@ bool AmlogicGrabber::isVideoPlaying()
if(ioctl(_videoDev, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) < 0)
{
Error(_log, "Failed to retrieve video state from device: %d - %s", errno, strerror(errno));
closeDev(_videoDev);
return false;
closeDevice(_videoDev);
}
else
{
if ( videoDisabled == 0 )
{
rc = true;
}
}
}
return videoDisabled == 0;
}
return rc;
}
int AmlogicGrabber::grabFrame(Image<ColorRgb> & image)
{
if (!_enabled) return 0;
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
// Make sure video is playing, else there is nothing to grab
if (isVideoPlaying())
{
if (_grabbingModeNotification!=1)
{
Info(_log, "VPU mode");
Info(_log, "Switch to VPU capture mode");
_grabbingModeNotification = 1;
_lastError = 0;
}
if (grabFrame_amvideocap(image) < 0)
closeDev(_captureDev);
if (grabFrame_amvideocap(image) < 0) {
closeDevice(_captureDev);
rc = -1;
}
}
else
{
if (_grabbingModeNotification!=2)
{
Info( _log, "FB mode");
Info( _log, "Switch to Framebuffer capture mode");
_grabbingModeNotification = 2;
_lastError = 0;
}
_fbGrabber.grabFrame(image);
rc = _fbGrabber.grabFrame(image);
usleep(50 * 1000);
//usleep(50 * 1000);
}
return 0;
}
return rc;
}
int AmlogicGrabber::grabFrame_amvideocap(Image<ColorRgb> & image)
{
int 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 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)
{
ErrorIf(_lastError != 2,_log,"Failed to configure capture device (%d - %s)", errno, strerror(errno));
_lastError = 2;
return -1;
}
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)
{
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;
return -1;
}
else if (_bytesToRead != bytesRead)
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;
return -1;
rc = -1;
}
_useImageResampler = true;
_imageResampler.processImage((const uint8_t*)_image_ptr, _width, _height, (_width << 1) + _width, PixelFormat::BGR24, image);
else {
_imageResampler.processImage(static_cast<uint8_t*>(_image_ptr),
_width,
_height,
linelen,
PixelFormat::BGR24, image);
_lastError = 0;
return 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;
}
}
if (inputsDiscovered.isEmpty())
{
DebugIf(verbose, _log, "No displays found to capture from!");
}
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
}
void AmlogicGrabber::setVideoMode(VideoMode mode)
{
Grabber::setVideoMode(mode);
_fbGrabber.setVideoMode(mode);
}
bool AmlogicGrabber::setPixelDecimation(int pixelDecimation)
{
return ( Grabber::setPixelDecimation( pixelDecimation) &&
_fbGrabber.setPixelDecimation( pixelDecimation));
}
void AmlogicGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
{
Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom);
_fbGrabber.setCropping(cropLeft, cropRight, cropTop, cropBottom);
}
bool AmlogicGrabber::setWidthHeight(int width, int height)
{
bool rc (false);
if ( Grabber::setWidthHeight(width, height) )
{
_image_bgr.resize(static_cast<unsigned>(width), static_cast<unsigned>(height));
_width = width;
_height = height;
_bytesToRead = _image_bgr.size();
_image_ptr = _image_bgr.memptr();
rc = _fbGrabber.setWidthHeight(width, height);
}
return rc;
}
bool AmlogicGrabber::setFramerate(int fps)
{
return (Grabber::setFramerate(fps) &&
_fbGrabber.setFramerate(fps));
}

View File

@ -1,9 +1,11 @@
#include <grabber/AmlogicWrapper.h>
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 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)
DispmanxFrameGrabber::DispmanxFrameGrabber()
: Grabber("DISPMANXGRABBER")
, _vc_display(0)
, _vc_resource(0)
, _vc_flags(0)
, _vc_flags(DISPMANX_TRANSFORM_T(0))
, _captureBuffer(new ColorRgba[0])
, _captureBufferSize(0)
, _image_rgba(width, height)
, _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,11 +72,14 @@ 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,
@ -77,55 +88,42 @@ bool DispmanxFrameGrabber::setWidthHeight(int width, int height)
&vc_nativeImageHandle);
assert(_vc_resource);
// Define the capture rectangle with the same size
if (_vc_resource != 0)
{
Debug(_log,"Define the capture rectangle with the same size");
vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height);
return true;
rc = true;
}
return false;
}
return rc;
}
void DispmanxFrameGrabber::setFlags(int vc_flags)
void DispmanxFrameGrabber::setFlags(DISPMANX_TRANSFORM_T vc_flags)
{
_vc_flags = vc_flags;
}
int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
{
if (!_enabled) return 0;
int ret;
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
// 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;
int cropLeft = _cropLeft;
int cropRight = _cropRight;
int cropTop = _cropTop;
int cropBottom = _cropBottom;
if (_vc_flags & DISPMANX_SNAPSHOT_FILL)
{
// 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;
}
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;
}
unsigned imageWidth = static_cast<unsigned>(_width - cropLeft - cropRight);
unsigned imageHeight = static_cast<unsigned>(_height - cropTop - cropBottom);
// resize the given image if needed
if (image.width() != imageWidth || image.height() != imageHeight)
@ -139,22 +137,23 @@ int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
}
// Open the connection to the display
_vc_display = vc_dispmanx_display_open(0);
_vc_display = vc_dispmanx_display_open(DEFAULT_DEVICE);
if (_vc_display < 0)
{
Error(_log, "Cannot open display: %d", _vc_display);
return -1;
Error(_log, "Cannot open display: %d", DEFAULT_DEVICE);
rc = -1;
}
else {
// Create the snapshot (incl down-scaling)
ret = vc_dispmanx_snapshot(_vc_display, _vc_resource, (DISPMANX_TRANSFORM_T) _vc_flags);
int ret = vc_dispmanx_snapshot(_vc_display, _vc_resource, _vc_flags);
if (ret < 0)
{
Error(_log, "Snapshot failed: %d", ret);
vc_dispmanx_display_close(_vc_display);
return ret;
rc = ret;
}
else
{
// Read the snapshot into the memory
void* imagePtr = _image_rgba.memptr();
void* capturePtr = imagePtr;
@ -166,11 +165,11 @@ int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
// grab to temp buffer if image pitch isn't valid or if we are cropping
if (imagePitch != capturePitch
|| (unsigned)_rectangle.width != imageWidth
|| (unsigned)_rectangle.height != imageHeight)
|| static_cast<unsigned>(_rectangle.width) != imageWidth
|| static_cast<unsigned>(_rectangle.height) != imageHeight)
{
// check if we need to resize the capture buffer
unsigned captureSize = capturePitch * _rectangle.height / sizeof(ColorRgba);
unsigned captureSize = capturePitch * static_cast<unsigned>(_rectangle.height) / sizeof(ColorRgba);
if (_captureBufferSize != captureSize)
{
delete[] _captureBuffer;
@ -185,31 +184,107 @@ int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
if (ret < 0)
{
Error(_log, "vc_dispmanx_resource_read_data failed: %d", ret);
vc_dispmanx_display_close(_vc_display);
return ret;
rc = ret;
}
// copy capture data to image if we captured to temp buffer
if (imagePtr != capturePtr)
else
{
// 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++)
{
memcpy((uint8_t*)imagePtr + y * imagePitch,
src_ptr + y * capturePitch,
imagePitch);
_imageResampler.processImage(static_cast<uint8_t*>(capturePtr),
_width,
_height,
static_cast<int>(capturePitch),
PixelFormat::RGB32,
image);
}
}
// 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;
if (_isEnabled && !_isDeviceInError)
{
if ( getScreenInfo() )
{
/* map the device to memory */
uint8_t * fbp = static_cast<uint8_t*>(mmap(nullptr, _fixInfo.smem_len, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, _fbfd, 0));
if (fbp == MAP_FAILED) {
QString errorReason = QString ("Error mapping %1, [%2] %3").arg(_fbDevice).arg(errno).arg(std::strerror(errno));
this->setInError ( errorReason );
closeDevice();
rc = -1;
}
else
{
_imageResampler.processImage(fbp,
static_cast<int>(_varInfo.xres),
static_cast<int>(_varInfo.yres),
static_cast<int>(_fixInfo.line_length),
_pixelFormat,
image);
munmap(fbp, _fixInfo.smem_len);
}
}
closeDevice();
}
return rc;
}
bool FramebufferFrameGrabber::openDevice()
{
bool rc = true;
/* Open the framebuffer device */
int fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (fbfd == -1)
_fbfd = ::open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (_fbfd < 0)
{
Error(_log, "Error opening %s, %s : ", QSTRING_CSTR(_fbDevice), std::strerror(errno));
return -1;
QString errorReason = QString ("Error opening %1, [%2] %3").arg(_fbDevice).arg(errno).arg(std::strerror(errno));
this->setInError ( errorReason );
rc = false;
}
return rc;
}
/* 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)
bool FramebufferFrameGrabber::closeDevice()
{
case 16: pixelFormat = PixelFormat::BGR16; break;
case 24: pixelFormat = PixelFormat::BGR24; break;
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;
case 32: _pixelFormat = PixelFormat::PIXELFORMAT_RGB32;
break;
#else
case 32: pixelFormat = PixelFormat::BGR32; break;
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;
rc= false;
QString errorReason = QString ("Unknown pixel format: %1 bits per pixel").arg(static_cast<int>(_varInfo.bits_per_pixel));
this->setInError ( errorReason );
closeDevice();
}
}
}
return rc;
}
/* 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;
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);
}
_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;
if (!video_inputs.isEmpty())
{
inputsDiscovered["device"] = "framebuffer";
inputsDiscovered["device_name"] = "Framebuffer";
inputsDiscovered["type"] = "screen";
inputsDiscovered["video_inputs"] = video_inputs;
}
}
void FramebufferFrameGrabber::setDevicePath(const QString& path)
if (inputsDiscovered.isEmpty())
{
if(_fbDevice != path)
{
_fbDevice = path;
int result;
struct fb_var_screeninfo vinfo;
DebugIf(verbose, _log, "No displays found to capture from!");
}
// Check if the framebuffer device can be opened and display the current resolution
int fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY);
if (fbfd == -1)
{
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));
}
else
{
Info(_log, "Display opened with resolution: %dx%d@%dbit", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
}
close(fbfd);
}
}
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
}

View File

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

View File

@ -5,15 +5,33 @@ unsigned __osx_frame_counter = 0;
const int __screenWidth = 800;
const int __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)
// 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,10 +43,38 @@ 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()
{
bool result = false;
if ( ! open() )
{
if ( _isWayland )
{
Error(_log, "Grabber does not work under Wayland!");
}
}
else
{
// cleanup last screen
freeResources();
_numberOfSDisplays = 0;
QScreen* primary = QGuiApplication::primaryScreen();
QList<QScreen *> screens = QGuiApplication::screens();
@ -49,38 +84,70 @@ bool QtGrabber::setupDisplay()
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!");
return false;
result = false;
}
else
{
_numberOfSDisplays = screens.size();
Info(_log,"Available Displays:");
int index = 0;
for(auto screen : screens)
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++;
++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 > unsigned(screens.size()-1))
if (_display > _numberOfSDisplays - 1 )
{
if ((screens.at(0)->size() != screens.at(0)->virtualSize()) && (_display == _numberOfSDisplays))
{
_isVirtual = true;
_display = 0;
}
else
{
Info(_log, "The requested display index '%d' is not available, falling back to display 0", _display);
_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);
return true;
}
result = 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;
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
if(_screen == nullptr)
{
// reinit, this will disable capture on failure
setEnabled(setupDisplay());
return -1;
bool result = setupDisplay();
setEnabled(result);
}
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
QPixmap resizedPixmap = originalPixmap.scaled(_width,_height);
QImage imageFrame = resizedPixmap.toImage().convertToFormat( QImage::Format_RGB888);
image.resize(imageFrame.width(), imageFrame.height());
for (int y=0; y<imageFrame.height(); ++y)
for (int x=0; x<imageFrame.width(); ++x)
if (_isEnabled)
{
QColor inPixel(imageFrame.pixel(x,y));
ColorRgb & outPixel = image(x,y);
outPixel.red = inPixel.red();
outPixel.green = inPixel.green();
outPixel.blue = inPixel.blue();
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));
return 0;
for (int y = 0; y < imageFrame.height(); y++)
{
memcpy((unsigned char*)image.memptr() + y * image.width() * 3, static_cast<unsigned char*>(imageFrame.scanLine(y)), imageFrame.width() * 3);
}
}
}
}
return rc;
}
int QtGrabber::updateScreenDimensions(bool force)
{
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()
{

View File

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

File diff suppressed because it is too large Load Diff

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