2018-12-27 23:11:32 +01:00
# pragma once
2019-02-03 14:36:57 +01:00
# include <sstream>
2018-12-27 23:11:32 +01:00
# include <hyperion/ColorAdjustment.h>
# include <hyperion/MultiColorAdjustment.h>
# include <hyperion/LedString.h>
2021-11-16 18:12:56 +01:00
# include <QRegularExpression>
2018-12-27 23:11:32 +01:00
// fg effect
# include <hyperion/Hyperion.h>
2021-03-19 22:52:41 +01:00
# include <hyperion/PriorityMuxer.h>
2022-02-11 20:36:15 +01:00
# if defined(ENABLE_EFFECTENGINE)
Media Foundation/V4L2 grabber ... (#1119)
* - New Media Foundation grabber
- JsonAPI available grabber fix
- commented json config removed
* Added libjpeg-turbo to dependencies
* Fix OSX build
Removed Azure Pipelines from build scripts
* Remove Platform from Dashboard
* Correct Grabber Namings
* Grabber UI improvements, generic JSONEditor Selection Update
* Active grabber fix
* Stop Framebuffer grabber on failure
* - Image format NV12 and I420 added
- Flip mode
- Scaling factor for MJPEG
- VSCode (compile before run)
- CI (push) dependency libjpeg-turbo added
* Refactor MediaFoundation (Part 1)
* Remove QDebug output
* Added image flipping ability to MF Grabber
* fix issue 1160
* -Reload MF Grabber only once per WebUI update
- Cleanup
* Improvements
* - Set 'Software Frame Decimation' begin to 0
- Removed grabber specific device name from Log
- Keep pixel format when switching resolution
- Display 'Flip mode' correct in Log
- BGR24 images always flipped
* Refactor MediaFoundation (Part 2)
* Refactor V4L2 grabber (part 1) (#62)
* Media Foundation grabber adapted to V4L2 change
* Enable Media Foundation grabber on windows
* Have fps as int, fix height typo
* Added video standards to JsonAPI output
* Error handling in source reader improved
* Fix "Frame to small" error
* Discovery VideoSources and Dynamically Update Editor
* Hide all element when no video grabber discovered, upate naming
* Do not show unsupported grabbers
* Copy Log to Clipboard
* Update Grabber schema and Defaults
* Update access levels and validate crop ranges
* Height and width in Qt grabber corrected
* Correct formatting
* Untabify
* Global component states across instances
* Components divided on the dashboard
* refactor
* Fix Merge-issues
* Database migration aligning with updated grabber model
* Align Grabber.js with new utility functions
* Allow editor-validation for enum-lists
* Handle "Show Explainations scenario" correctly
* Grabber - Ensure save is only possible on valid content
* Dashboard update + fix GlobalSignal connection
* Ensure default database is populated with current release
* Correct grabber4L2 access level
* Display Signal detection area in preview
* Write Hyperion version into default config on compiling.
* Create defaultconfig.json dynamically
* WebUI changes
* Correct grabber config look-ups
* Refactor i18n language loading
* Fix en.json
* Split global capture from instance capture config
* Update grabber default values
* Standalone grabber: Add --debug switch
* Enhance showInputOptionsForKey for multiple keys
* Add grabber instance link to system grabber config
* Only show signal detection area, if grabber is enabled
* Always show Active element on grabber page
* Remote control - Only display gabber status, if global grabber is enabled
* WebUI optimization (thx to @mkcologne)
Start Grabber only when global settings are enabled
Fixed an issue in the WebUI preview
* V4L2/MF changes
* Jsoneditor, Correct translation for default values
* Refactor LED-Device handling in UI and make element naming consistent
* MF Discovery extended
* Fix LGTM finding
* Support Grabber Bri, Hue, Sat and Con in UI, plus their defaults
* Concider Access level for item filtering
* Concider Access level for item filtering
* Revert "Concider Access level for item filtering"
This reverts commit 5b0ce3c0f2de67e0c43788190cfff45614706129.
* Disable fpsSoftwareDecimation for framegrabber, as not supported yet
* JSON-Editor- Add updated schema for validation on dynamic elements
* added V4L2 color IDs
* LGTM findings fix
* destroy SR callback only on exit
* Grabber.js - Hide elements not supported by platform
* Fixed freezing start effect
* Grabber UI - Hardware controls - Show current values and allow to reset to defaults
* Grabber - Discovery - Add current values to properties
* Small things
* Clean-up Effects and have ENDLESS consistently defined
* Fix on/off/on priority during startup, by initializing _prevVisComp in line with background priority
* Add missing translation mappings
* DirectX Grabber reactivated/ QT Grabber size decimation fixed
* typo in push-master workflow
* Use PreciseTimer for Grabber to ensure stable FPS timing
* Set default Screencapture rate consistently
* Fix libjpeg-turbo download
* Remove Zero character from file
* docker-compile Add PLATFORM parameter, only copy output file after successful compile
* Framebuffer, Dispmanx, OSX, AML Grabber discovery, various clean-up and consistencies across grabbers
* Fix merge problem - on docker-compile Add PLATFORM parameter, only copy output file after successful compile
* Fix definition
* OSXFRameGrabber - Revert cast
* Clean-ups nach Feedback
* Disable certain libraries when building armlogic via standard stretch image as developer
* Add CEC availability to ServerInfo to have it platform independent
* Grabber UI - Fix problem that crop values are not populated when refining editor rage
* Preserve value when updating json-editor range
* LEDVisualisation - Clear image when source changes
* Fix - Preserve value when updating json-editor range
* LEDVisualisation - Clear image when no component is active
* Allow to have password handled by Password-Manager (#1263)
* Update default signal detection area to green assuming rainbow grabber
* LED Visualisation - Handle empty priority update
* Fix yuv420 in v4l2 grabber
* V4L2-Grabber discovery - Only report grabbers with valid video input information
* Grabber - Update static variables to have them working in release build
* LED Visualisation - ClearImage when no priorities
* LED Visualisation - Fix Logo resizing issue
* LED Visualisation - Have nearly black background and negative logo
Co-authored-by: LordGrey <lordgrey.emmel@gmail.com>
Co-authored-by: LordGrey <48840279+Lord-Grey@users.noreply.github.com>
2021-07-14 20:48:33 +02:00
# include <effectengine/Effect.h>
2022-02-11 20:36:15 +01:00
# endif
2018-12-27 23:11:32 +01:00
///
/// @brief Provide utility methods for Hyperion class
///
namespace hyperion {
void handleInitialEffect ( Hyperion * hyperion , const QJsonObject & FGEffectConfig )
{
# define FGCONFIG_ARRAY fgColorConfig.toArray()
// initial foreground effect/color
if ( FGEffectConfig [ " enable " ] . toBool ( true ) )
{
2022-02-11 20:36:15 +01:00
# if defined(ENABLE_EFFECTENGINE)
2018-12-27 23:11:32 +01:00
const QString fgTypeConfig = FGEffectConfig [ " type " ] . toString ( " effect " ) ;
const QString fgEffectConfig = FGEffectConfig [ " effect " ] . toString ( " Rainbow swirl fast " ) ;
2022-02-11 20:36:15 +01:00
# else
const QString fgTypeConfig = " color " ;
# endif
2018-12-27 23:11:32 +01:00
const QJsonValue fgColorConfig = FGEffectConfig [ " color " ] ;
int default_fg_duration_ms = 3000 ;
int fg_duration_ms = FGEffectConfig [ " duration_ms " ] . toInt ( default_fg_duration_ms ) ;
2022-02-11 20:36:15 +01:00
if ( fg_duration_ms < = PriorityMuxer : : ENDLESS )
2018-12-27 23:11:32 +01:00
{
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 ) ;
}
if ( fgTypeConfig . contains ( " color " ) )
{
2020-02-26 18:54:56 +01:00
std : : vector < ColorRgb > fg_color = {
ColorRgb {
2020-11-14 17:58:56 +01:00
static_cast < uint8_t > ( FGCONFIG_ARRAY . at ( 0 ) . toInt ( 0 ) ) ,
static_cast < uint8_t > ( FGCONFIG_ARRAY . at ( 1 ) . toInt ( 0 ) ) ,
static_cast < uint8_t > ( FGCONFIG_ARRAY . at ( 2 ) . toInt ( 0 ) )
2020-02-26 18:54:56 +01:00
}
2018-12-27 23:11:32 +01:00
} ;
2021-03-19 22:52:41 +01:00
hyperion - > setColor ( PriorityMuxer : : FG_PRIORITY , fg_color , fg_duration_ms ) ;
2022-01-22 17:48:03 +01:00
Info ( Logger : : getInstance ( " HYPERION " , " I " + QString : : number ( hyperion - > getInstanceIndex ( ) ) ) , " Initial foreground color set (%d %d %d) " , fg_color . at ( 0 ) . red , fg_color . at ( 0 ) . green , fg_color . at ( 0 ) . blue ) ;
2018-12-27 23:11:32 +01:00
}
2022-02-11 20:36:15 +01:00
# if defined(ENABLE_EFFECTENGINE)
2018-12-27 23:11:32 +01:00
else
{
2021-03-19 22:52:41 +01:00
int result = hyperion - > setEffect ( fgEffectConfig , PriorityMuxer : : FG_PRIORITY , fg_duration_ms ) ;
2022-01-22 17:48:03 +01:00
Info ( Logger : : getInstance ( " HYPERION " , " I " + QString : : number ( hyperion - > getInstanceIndex ( ) ) ) , " Initial foreground effect '%s' %s " , QSTRING_CSTR ( fgEffectConfig ) , ( ( result = = 0 ) ? " started " : " failed " ) ) ;
2018-12-27 23:11:32 +01:00
}
2022-02-11 20:36:15 +01:00
# endif
2018-12-27 23:11:32 +01:00
}
# undef FGCONFIG_ARRAY
}
ColorOrder createColorOrder ( const QJsonObject & deviceConfig )
{
return stringToColorOrder ( deviceConfig [ " colorOrder " ] . toString ( " rgb " ) ) ;
}
2020-06-28 23:12:22 +02:00
RgbTransform createRgbTransform ( const QJsonObject & colorConfig )
2018-12-27 23:11:32 +01:00
{
const double backlightThreshold = colorConfig [ " backlightThreshold " ] . toDouble ( 0.0 ) ;
const bool backlightColored = colorConfig [ " backlightColored " ] . toBool ( false ) ;
2020-11-14 17:58:56 +01:00
const int brightness = colorConfig [ " brightness " ] . toInt ( 100 ) ;
const int brightnessComp = colorConfig [ " brightnessCompensation " ] . toInt ( 100 ) ;
2020-06-28 23:12:22 +02:00
const double gammaR = colorConfig [ " gammaRed " ] . toDouble ( 1.0 ) ;
const double gammaG = colorConfig [ " gammaGreen " ] . toDouble ( 1.0 ) ;
const double gammaB = colorConfig [ " gammaBlue " ] . toDouble ( 1.0 ) ;
2018-12-27 23:11:32 +01:00
2020-11-14 17:58:56 +01:00
return RgbTransform ( gammaR , gammaG , gammaB , backlightThreshold , backlightColored , static_cast < uint8_t > ( brightness ) , static_cast < uint8_t > ( brightnessComp ) ) ;
2018-12-27 23:11:32 +01:00
}
2022-08-17 23:26:19 +02:00
OkhsvTransform createOkhsvTransform ( const QJsonObject & colorConfig )
{
const double saturationGain = colorConfig [ " saturationGain " ] . toDouble ( 1.0 ) ;
const double brightnessGain = colorConfig [ " brightnessGain " ] . toDouble ( 1.0 ) ;
return OkhsvTransform ( saturationGain , brightnessGain ) ;
}
2020-08-08 13:09:15 +02:00
RgbChannelAdjustment createRgbChannelAdjustment ( const QJsonObject & colorConfig , const QString & channelName , int defaultR , int defaultG , int defaultB )
2018-12-27 23:11:32 +01:00
{
const QJsonArray & channelConfig = colorConfig [ channelName ] . toArray ( ) ;
2020-06-28 23:12:22 +02:00
return RgbChannelAdjustment (
2020-11-14 17:58:56 +01:00
static_cast < uint8_t > ( channelConfig [ 0 ] . toInt ( defaultR ) ) ,
static_cast < uint8_t > ( channelConfig [ 1 ] . toInt ( defaultG ) ) ,
static_cast < uint8_t > ( channelConfig [ 2 ] . toInt ( defaultB ) ) ,
2020-06-28 23:12:22 +02:00
" ChannelAdjust_ " + channelName . toUpper ( )
2018-12-27 23:11:32 +01:00
) ;
}
2020-06-28 23:12:22 +02:00
ColorAdjustment * createColorAdjustment ( const QJsonObject & adjustmentConfig )
2018-12-27 23:11:32 +01:00
{
const QString id = adjustmentConfig [ " id " ] . toString ( " default " ) ;
ColorAdjustment * adjustment = new ColorAdjustment ( ) ;
adjustment - > _id = id ;
2020-06-28 23:12:22 +02:00
adjustment - > _rgbBlackAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " black " , 0 , 0 , 0 ) ;
adjustment - > _rgbWhiteAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " white " , 255 , 255 , 255 ) ;
adjustment - > _rgbRedAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " red " , 255 , 0 , 0 ) ;
adjustment - > _rgbGreenAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " green " , 0 , 255 , 0 ) ;
adjustment - > _rgbBlueAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " blue " , 0 , 0 , 255 ) ;
adjustment - > _rgbCyanAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " cyan " , 0 , 255 , 255 ) ;
adjustment - > _rgbMagentaAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " magenta " , 255 , 0 , 255 ) ;
adjustment - > _rgbYellowAdjustment = createRgbChannelAdjustment ( adjustmentConfig , " yellow " , 255 , 255 , 0 ) ;
adjustment - > _rgbTransform = createRgbTransform ( adjustmentConfig ) ;
2022-08-17 23:26:19 +02:00
adjustment - > _okhsvTransform = createOkhsvTransform ( adjustmentConfig ) ;
2018-12-27 23:11:32 +01:00
return adjustment ;
}
2020-11-14 17:58:56 +01:00
MultiColorAdjustment * createLedColorsAdjustment ( int ledCnt , const QJsonObject & colorConfig )
2018-12-27 23:11:32 +01:00
{
// Create the result, the transforms are added to this
MultiColorAdjustment * adjustment = new MultiColorAdjustment ( ledCnt ) ;
const QJsonValue adjustmentConfig = colorConfig [ " channelAdjustment " ] ;
2021-11-16 18:12:56 +01:00
const QRegularExpression overallExp ( " ([0-9]+( \\ -[0-9]+) ? ) ( , [ ] * ( [ 0 - 9 ] + ( \ \ - [ 0 - 9 ] + ) ? ) ) * " ) ;
2018-12-27 23:11:32 +01:00
const QJsonArray & adjustmentConfigArray = adjustmentConfig . toArray ( ) ;
for ( signed i = 0 ; i < adjustmentConfigArray . size ( ) ; + + i )
{
const QJsonObject & config = adjustmentConfigArray . at ( i ) . toObject ( ) ;
ColorAdjustment * colorAdjustment = createColorAdjustment ( config ) ;
adjustment - > addAdjustment ( colorAdjustment ) ;
const QString ledIndicesStr = config [ " leds " ] . toString ( " " ) . trimmed ( ) ;
if ( ledIndicesStr . compare ( " * " ) = = 0 )
{
// Special case for indices '*' => all leds
adjustment - > setAdjustmentForLed ( colorAdjustment - > _id , 0 , ledCnt - 1 ) ;
2020-02-10 15:21:58 +01:00
//Info(Logger::getInstance("HYPERION"), "ColorAdjustment '%s' => [0-%d]", QSTRING_CSTR(colorAdjustment->_id), ledCnt-1);
2018-12-27 23:11:32 +01:00
continue ;
}
2021-11-16 18:12:56 +01:00
if ( ! overallExp . match ( ledIndicesStr ) . hasMatch ( ) )
2018-12-27 23:11:32 +01:00
{
2020-02-10 15:21:58 +01:00
//Error(Logger::getInstance("HYPERION"), "Given led indices %d not correct format: %s", i, QSTRING_CSTR(ledIndicesStr));
2018-12-27 23:11:32 +01:00
continue ;
}
std : : stringstream ss ;
const QStringList ledIndexList = ledIndicesStr . split ( " , " ) ;
for ( int i = 0 ; i < ledIndexList . size ( ) ; + + i ) {
if ( i > 0 )
{
ss < < " , " ;
}
if ( ledIndexList [ i ] . contains ( " - " ) )
{
QStringList ledIndices = ledIndexList [ i ] . split ( " - " ) ;
int startInd = ledIndices [ 0 ] . toInt ( ) ;
int endInd = ledIndices [ 1 ] . toInt ( ) ;
adjustment - > setAdjustmentForLed ( colorAdjustment - > _id , startInd , endInd ) ;
ss < < startInd < < " - " < < endInd ;
}
else
{
int index = ledIndexList [ i ] . toInt ( ) ;
adjustment - > setAdjustmentForLed ( colorAdjustment - > _id , index , index ) ;
ss < < index ;
}
}
2020-02-10 15:21:58 +01:00
//Info(Logger::getInstance("HYPERION"), "ColorAdjustment '%s' => [%s]", QSTRING_CSTR(colorAdjustment->_id), ss.str().c_str());
2018-12-27 23:11:32 +01:00
}
return adjustment ;
}
/**
* Construct the ' led - string ' with the integration area definition per led and the color
* ordering of the RGB channels
* @ param ledsConfig The configuration of the led areas
* @ param deviceOrder The default RGB channel ordering
* @ return The constructed ledstring
*/
LedString createLedString ( const QJsonArray & ledConfigArray , const ColorOrder deviceOrder )
{
LedString ledString ;
const QString deviceOrderStr = colorOrderToString ( deviceOrder ) ;
for ( signed i = 0 ; i < ledConfigArray . size ( ) ; + + i )
{
2020-02-26 18:54:56 +01:00
const QJsonObject & ledConfig = ledConfigArray [ i ] . toObject ( ) ;
2018-12-27 23:11:32 +01:00
Led led ;
2020-02-26 18:54:56 +01:00
led . minX_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " hmin " ] . toDouble ( ) ) ) ;
led . maxX_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " hmax " ] . toDouble ( ) ) ) ;
led . minY_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " vmin " ] . toDouble ( ) ) ) ;
led . maxY_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " vmax " ] . toDouble ( ) ) ) ;
2019-08-25 16:32:19 +02:00
// Fix if the user swapped min and max
if ( led . minX_frac > led . maxX_frac )
2018-12-27 23:11:32 +01:00
{
2019-08-25 16:32:19 +02:00
std : : swap ( led . minX_frac , led . maxX_frac ) ;
2018-12-27 23:11:32 +01:00
}
2019-08-25 16:32:19 +02:00
if ( led . minY_frac > led . maxY_frac )
2018-12-27 23:11:32 +01:00
{
2019-08-25 16:32:19 +02:00
std : : swap ( led . minY_frac , led . maxY_frac ) ;
2018-12-27 23:11:32 +01:00
}
2019-08-25 16:32:19 +02:00
// Get the order of the rgb channels for this led (default is device order)
2020-02-26 18:54:56 +01:00
led . colorOrder = stringToColorOrder ( ledConfig [ " colorOrder " ] . toString ( deviceOrderStr ) ) ;
2019-08-25 16:32:19 +02:00
ledString . leds ( ) . push_back ( led ) ;
2018-12-27 23:11:32 +01:00
}
return ledString ;
}
QSize getLedLayoutGridSize ( const QJsonArray & ledConfigArray )
{
std : : vector < int > midPointsX ;
std : : vector < int > midPointsY ;
for ( signed i = 0 ; i < ledConfigArray . size ( ) ; + + i )
{
2020-02-26 18:54:56 +01:00
const QJsonObject & ledConfig = ledConfigArray [ i ] . toObject ( ) ;
double minX_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " hmin " ] . toDouble ( ) ) ) ;
double maxX_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " hmax " ] . toDouble ( ) ) ) ;
double minY_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " vmin " ] . toDouble ( ) ) ) ;
double maxY_frac = qMax ( 0.0 , qMin ( 1.0 , ledConfig [ " vmax " ] . toDouble ( ) ) ) ;
2019-08-25 16:32:19 +02:00
// Fix if the user swapped min and max
if ( minX_frac > maxX_frac )
2018-12-27 23:11:32 +01:00
{
2019-08-25 16:32:19 +02:00
std : : swap ( minX_frac , maxX_frac ) ;
2018-12-27 23:11:32 +01:00
}
2019-08-25 16:32:19 +02:00
if ( minY_frac > maxY_frac )
{
std : : swap ( minY_frac , maxY_frac ) ;
}
// calculate mid point and make grid calculation
midPointsX . push_back ( int ( 1000.0 * ( minX_frac + maxX_frac ) / 2.0 ) ) ;
midPointsY . push_back ( int ( 1000.0 * ( minY_frac + maxY_frac ) / 2.0 ) ) ;
2018-12-27 23:11:32 +01:00
}
// remove duplicates
std : : sort ( midPointsX . begin ( ) , midPointsX . end ( ) ) ;
midPointsX . erase ( std : : unique ( midPointsX . begin ( ) , midPointsX . end ( ) ) , midPointsX . end ( ) ) ;
std : : sort ( midPointsY . begin ( ) , midPointsY . end ( ) ) ;
midPointsY . erase ( std : : unique ( midPointsY . begin ( ) , midPointsY . end ( ) ) , midPointsY . end ( ) ) ;
2020-11-14 17:58:56 +01:00
QSize gridSize ( static_cast < int > ( midPointsX . size ( ) ) , static_cast < int > ( midPointsY . size ( ) ) ) ;
2020-03-27 16:08:42 +01:00
// Correct the grid in case it is malformed in width vs height
// Expected is at least 50% of width <-> height
if ( ( gridSize . width ( ) / gridSize . height ( ) ) > 2 )
gridSize . setHeight ( qMax ( 1 , gridSize . width ( ) / 2 ) ) ;
else if ( ( gridSize . width ( ) / gridSize . height ( ) ) < 0.5 )
gridSize . setWidth ( qMax ( 1 , gridSize . height ( ) / 2 ) ) ;
2020-10-18 19:08:27 +02:00
// Limit to 80px for performance reasons
const int pl = 80 ;
if ( gridSize . width ( ) > pl | | gridSize . height ( ) > pl )
{
gridSize . scale ( pl , pl , Qt : : KeepAspectRatio ) ;
}
2018-12-27 23:11:32 +01:00
return gridSize ;
}
} ;