Merge pull request #4 from tvdzwan/master

Updating

Former-commit-id: f2babbeab3f8dd80e92b37be5fd0c25c1b45a76b
This commit is contained in:
penfold42 2016-03-09 21:06:05 +11:00
commit 9d01c84494
79 changed files with 1709 additions and 469 deletions

View File

@ -91,9 +91,6 @@ include_directories(${CMAKE_SOURCE_DIR}/include)
# Prefer static linking over dynamic # Prefer static linking over dynamic
#set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") #set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so")
set(CMAKE_BUILD_TYPE "Debug")
#set(CMAKE_BUILD_TYPE "Release")
# enable C++11 # enable C++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11 -Wall") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11 -Wall")

View File

@ -24,11 +24,11 @@ mkdir "$HYPERION_DIR/build"
cd "$HYPERION_DIR/build" cd "$HYPERION_DIR/build"
# run cmake to generate make files on the raspberry pi # run cmake to generate make files on the raspberry pi
cmake .. cmake -DCMAKE_BUILD_TYPE=Release -Wno-dev ..
# or if you are not compiling on the raspberry pi and need to disable the Dispmanx grabber and support for spi devices # or if you are not compiling on the raspberry pi and need to disable the Dispmanx grabber and support for spi devices
cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_X11=ON .. cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_X11=ON -DCMAKE_BUILD_TYPE=Release -Wno-dev ..
# as an alternative for the dispmanx grabber on non-rpi devices (e.g. cubox-i) you could try the framebuffer grabber # as an alternative for the dispmanx grabber on non-rpi devices (e.g. cubox-i) you could try the framebuffer grabber
cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_FB=ON .. cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_FB=ON -DCMAKE_BUILD_TYPE=Release -Wno-dev ..
# for OSX build you need XCode, qt4 libraries and cmake. You can use macport (homebrew might work too) to install them # for OSX build you need XCode, qt4 libraries and cmake. You can use macport (homebrew might work too) to install them
sudo port install qt4-mac sudo port install qt4-mac
sudo port install cmake sudo port install cmake
@ -36,6 +36,12 @@ cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_V4L2=OFF -DENABLE_OSX=O
# run make to build Hyperion # run make to build Hyperion
make make
# or if you have a system with more then 1 cpu core
make -j 4
# "4" is the number of cpu cores (e.g. 4 on RPi2), this makes compile faster
#after compile, to remove any stuff not needed for a release version.
strip bin/*
# The binaries are build in "$HYPERION_DIR/build/bin". You could copy those to /usr/bin # The binaries are build in "$HYPERION_DIR/build/bin". You could copy those to /usr/bin
sudo cp ./bin/hyperion-remote /usr/bin/ sudo cp ./bin/hyperion-remote /usr/bin/

View File

@ -24,6 +24,8 @@ tar --create --verbose --gzip --absolute-names --show-transformed-names --ignore
--transform "s:$repodir/effects/:hyperion/effects/:" \ --transform "s:$repodir/effects/:hyperion/effects/:" \
--transform "s:$repodir/config/:hyperion/config/:" \ --transform "s:$repodir/config/:hyperion/config/:" \
--transform "s:$repodir/bin/hyperion.init.sh:hyperion/init.d/hyperion.init.sh:" \ --transform "s:$repodir/bin/hyperion.init.sh:hyperion/init.d/hyperion.init.sh:" \
--transform "s:$repodir/bin/hyperion.systemd.sh:hyperion/init.d/hyperion.systemd.sh:" \
--transform "s:$repodir/bin/hyperion.initctl.sh:hyperion/init.d/hyperion.initctl.sh:" \
--transform "s://:/:g" \ --transform "s://:/:g" \
"$builddir/bin/hyperiond" \ "$builddir/bin/hyperiond" \
"$builddir/bin/hyperion-remote" \ "$builddir/bin/hyperion-remote" \
@ -32,5 +34,7 @@ tar --create --verbose --gzip --absolute-names --show-transformed-names --ignore
"$builddir/bin/dispmanx2png" \ "$builddir/bin/dispmanx2png" \
"$repodir/effects/"* \ "$repodir/effects/"* \
"$repodir/bin/hyperion.init.sh" \ "$repodir/bin/hyperion.init.sh" \
"$repodir/bin/hyperion.systemd.sh" \
"$repodir/bin/hyperion.initctl.sh" \
"$repodir/config/hyperion.config.json" "$repodir/config/hyperion.config.json"

View File

@ -1,7 +1,16 @@
#!/bin/bash #!/bin/bash
# Hyperion daemon # Hyperion daemon service
# description: Hyperion daemon # description: Hyperion daemon
# processname: hyperiond # processname: hyperiond
### BEGIN INIT INFO
# Provides: Hyperion
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Hyperion Ambilight init.d Service.
# Description: Hyperion Ambilight init.d Service.
### END INIT INFO
DAEMON=hyperiond DAEMON=hyperiond
DAEMONOPTS="/etc/hyperion.config.json" DAEMONOPTS="/etc/hyperion.config.json"

17
bin/hyperion.initctl.sh Normal file
View File

@ -0,0 +1,17 @@
## Hyperion daemon initctl script
description "hyperion"
author "poljvd & tvdzwan"
start on (runlevel [2345])
stop on (runlevel [!2345])
respawn
pre-start script
#comment out the following 2 lines for x32/64
modprobe spidev
/usr/bin/gpio2spi
end script
exec /usr/bin/hyperiond /etc/hyperion.config.json

15
bin/hyperion.systemd.sh Normal file
View File

@ -0,0 +1,15 @@
[Unit]
Description=Hyperion Systemd service
[Service]
Type=simple
User=root
Group=root
UMask=007
ExecStart=/opt/hyperion/bin/hyperiond /etc/hyperion.config.json
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
TimeoutStopSec=10
[Install]
WantedBy=multi-user.target

105
bin/remove_hyperion.sh Normal file
View File

@ -0,0 +1,105 @@
#!/bin/sh
# Script to remove Hyperion and all services
# Make sure /sbin is on the path (for service to find sub scripts)
PATH="/sbin:$PATH"
#Check if HyperCon is logged in as root
if [ $(id -u) != 0 ] && [ "$1" = "HyperConRemove" ]; then
echo '---> Critical Error: Please connect as user "root" through HyperCon'
echo '---> We need admin privileges to remove your Hyperion! -> abort'
exit 1
fi
#Check, if script is running as root
if [ $(id -u) != 0 ]; then
echo '---> Critical Error: Please run the script as root (sudo sh ./remove_hyperion.sh)'
exit 1
fi
#Welcome message
echo '*******************************************************************************'
echo 'This script will remove Hyperion and it´s services'
echo '-----> Please BACKUP your hyperion.config.json if necessary <-----'
echo '*******************************************************************************'
#Skip the prompt if HyperCon Remove
if [ "$1" = "" ]; then
#Prompt for confirmation to proceed
while true
do
echo -n "---> Do you really want to remove Hyperion and it´s services? (y or n) :"
read CONFIRM
case $CONFIRM in
y|Y|YES|yes|Yes) break ;;
n|N|no|NO|No)
echo "---> Aborting - you entered \"$CONFIRM\""
exit
;;
*) echo "-> Please enter only y or n"
esac
done
echo "---> You entered \"$CONFIRM\". Remove Hyperion!"
fi
# Find out if we are on OpenElec
OS_OPENELEC=`grep -m1 -c OpenELEC /etc/issue`
# check which init script we should use
USE_SYSTEMD=`grep -m1 -c systemd /proc/1/comm`
USE_INITCTL=`which /sbin/initctl | wc -l`
USE_SERVICE=`which /usr/sbin/service | wc -l`
# Stop hyperion daemon if it is running
echo '---> Stop Hyperion, if necessary'
if [ $OS_OPENELEC -eq 1 ]; then
killall hyperiond 2>/dev/null
elif [ $USE_INITCTL -eq 1 ]; then
/sbin/initctl stop hyperion 2>/dev/null
elif [ $USE_SERVICE -eq 1 ]; then
/usr/sbin/service hyperion stop 2>/dev/null
elif [ $USE_SYSTEMD -eq 1 ]; then
service hyperion stop 2>/dev/null
fi
#Disabling and delete service files
if [ $USE_INITCTL -eq 1 ]; then
echo '---> Delete and disable Hyperion initctl script'
rm -v /etc/init/hyperion 2>/dev/null
initctl reload-configuration
elif [ $OS_OPENELEC -eq 1 ]; then
# Remove Hyperion from OpenELEC autostart.sh
echo "---> Remove Hyperion from OpenELEC autostart.sh"
sed -i "/hyperiond/d" /storage/.config/autostart.sh 2>/dev/null
elif [ $USE_SYSTEMD -eq 1 ]; then
# Delete and disable Hyperion systemd script
echo '---> Delete and disable Hyperion systemd script'
systemctl disable hyperion.service
rm -v /etc/systemd/system/hyperion.service 2>/dev/null
elif [ $USE_SERVICE -eq 1 ]; then
# Delete and disable Hyperion init.d script
echo '---> Delete and disable Hyperion init.d script'
update-rc.d -f hyperion remove
rm /etc/init.d/hyperion 2>/dev/null
fi
# Delete Hyperion binaries
if [ $OS_OPENELEC -eq 1 ]; then
# Remove OpenELEC Hyperion binaries and configs
echo '---> Remove the OpenELEC Hyperion binaries and hyperion.config.json'
rm -rv /storage/hyperion 2>/dev/null
rm -v /storage/.config/hyperion.config.json 2>/dev/null
else
#Remove binaries on all distributions/systems (not OpenELEC)
echo "---> Remove links to the binaries"
rm -v /usr/bin/hyperiond 2>/dev/null
rm -v /usr/bin/hyperion-remote 2>/dev/null
rm -v /usr/bin/hyperion-v4l2 2>/dev/null
rm -v /etc/hyperion.config.json 2>/dev/null
echo "---> Remove binaries"
rm -rv /opt/hyperion 2>/dev/null
fi
echo '*******************************************************************************'
echo 'Hyperion successful removed!'
echo '*******************************************************************************'
exit 0

View File

@ -4,22 +4,16 @@
{ {
/// Device configuration contains the following fields: /// Device configuration contains the following fields:
/// * 'name' : The user friendly name of the device (only used for display purposes) /// * 'name' : The user friendly name of the device (only used for display purposes)
/// * 'type' : The type of the device or leds (known types for now are 'ws2801', 'ldp8806', /// * 'type' : The type of the device or leds (known types for now are
/// 'lpd6803', 'sedu', 'adalight', 'lightpack', 'philipshue', 'test' and 'none') /// APA102, Adalight, AmbiLed, Atmo, Hyperion-USBASP-WS2801, Hyperion-USBASP-WS2812, Lightberry, Lightpack, LPD6803, LPD8806, Multi-Lightpack, P9813, Paintpack, PhilipsHUE, PiBlaster, SEDU, Test, ThinkerForge, TPM2, WS2801, WS2812b, None)
/// * 'output' : The output specification depends on selected device. This can for example be the /// * [device type specific configuration]
/// device specifier, device serial number, or the output file name
/// * 'rate' : The baudrate of the output to the device
/// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.). /// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.).
/// Specific of Philips Hue:
/// * 'username' : The name of user registred on the Philips Hue Bridge
/// * 'switchOffOnBlack' : Define if Hue light switch off when black is detected
/// * 'transitiontime' : Set the time of transition between color of Hue light
"device" : "device" :
{ {
"name" : "MyPi", "name" : "MyPi",
"type" : "ws2801", "type" : "ws2801",
"output" : "/dev/spidev0.0", "output" : "/dev/spidev0.0",
"rate" : 250000, "rate" : 1000000,
"colorOrder" : "rgb" "colorOrder" : "rgb"
}, },
@ -45,6 +39,7 @@
/// - 'type' The type of smoothing algorithm ('linear' or 'none') /// - 'type' The type of smoothing algorithm ('linear' or 'none')
/// - 'time_ms' The time constant for smoothing algorithm in milliseconds /// - 'time_ms' The time constant for smoothing algorithm in milliseconds
/// - 'updateFrequency' The update frequency of the leds in Hz /// - 'updateFrequency' The update frequency of the leds in Hz
/// - 'updateDelay' The delay of the output to leds (in periods of smoothing)
"color" : "color" :
{ {
"transform" : "transform" :
@ -82,13 +77,156 @@
], ],
"smoothing" : "smoothing" :
{ {
"type" : "none", "type" : "linear",
"time_ms" : 200, "time_ms" : 200,
"updateFrequency" : 20.0000, "updateFrequency" : 20.0000,
"updateDelay" : 0 "updateDelay" : 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.0 and 1.0)
/// * 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)
"blackborderdetector" :
{
"enable" : true,
"threshold" : 0.0,
"unknownFrameCnt" : 600,
"borderFrameCnt" : 50,
"maxInconsistentCnt" : 10,
"blurRemoveCnt" : 1,
"mode" : "default"
},
/// The configuration of the effect engine, contains the following items:
/// * paths : An array with absolute location(s) of directories with effects
/// * color : Set static color after boot -> set effect to "" (empty) and input the values [R,G,B] and set duration_ms NOT to 0 (use 1) instead
/// * effect : The effect selected as 'boot sequence'
/// * duration_ms : The duration of the selected effect (0=endless)
/// * priority : The priority of the selected effect/static color (default=0)
"effects" :
{
"paths" :
[
"/opt/hyperion/effects"
]
},
"bootsequence" :
{
"color" : [0,0,0],
"effect" : "Rainbow swirl fast",
"duration_ms" : 3000,
"priority" : 990
},
/// 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
/// * proto : Proto server adress and port of your target. Syntax:[IP:PORT] -> ["127.0.0.1:19447"] or more instances to forward ["127.0.0.1:19447","192.168.0.24:19449"]
/// * 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! (HyperCon -> External -> Json Server/Proto Server)
// "forwarder" :
// {
// "proto" : ["127.0.0.1:19447"],
// "json" : ["127.0.0.1:19446"]
// },
/// The configuration for the frame-grabber, contains the following items:
/// * 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]
"framegrabber" :
{
"width" : 80,
"height" : 45,
"frequency_Hz" : 10.0
},
/// The configuration of the Kodi connection used to enable and disable the frame-grabber. Contains the following fields:
/// * xbmcAddress : The IP address of the Kodi-host
/// * xbmcTcpPort : The TCP-port of the Kodi-server
/// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback
/// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show
/// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback
/// * grabMenu : Flag indicating that the frame-grabber is on(true) at the Kodi menu
/// * grabScreensaver : Flag indicating that the frame-grabber is on(true) when Kodi is on screensaver
/// * enable3DDetection : Flag indicating that the frame-grabber should switch to a 3D compatible modus if a 3D video is playing
// "xbmcVideoChecker" :
// {
// "xbmcAddress" : "127.0.0.1",
// "xbmcTcpPort" : 9090,
// "grabVideo" : true,
// "grabPictures" : true,
// "grabAudio" : true,
// "grabMenu" : false,
// "grabScreensaver" : true,
// "enable3DDetection" : true
// },
/// 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 Proto server which enables the protobuffer remote interface
/// * port : Port at which the protobuffer server is started
"protoServer" :
{
"port" : 19445
},
/// The configuration of the boblight server which enables the boblight remote interface
/// * port : Port at which the boblight server is started
// "boblightServer" :
// {
// "port" : 19333
// },
/// Configuration for the embedded V4L2 grabber
/// * device : V4L2 Device to use [default="/dev/video0"]
/// * input : V4L2 input to use [default=0]
/// * standard : Video standard (no-change/PAL/NTSC) [default="no-change"]
/// * width : V4L2 width to set [default=-1]
/// * height : V4L2 height to set [default=-1]
/// * frameDecimation : Frame decimation factor [default=2]
/// * sizeDecimation : Size decimation factor [default=8]
/// * priority : Hyperion priority channel [default=800]
/// * mode : 3D mode to use 2D/3DSBS/3DTAB (note: no autodetection) [default="2D"]
/// * 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]
/// * redSignalThreshold : Signal threshold for the red channel between 0.0 and 1.0 [default=0.0]
/// * greenSignalThreshold : Signal threshold for the green channel between 0.0 and 1.0 [default=0.0]
/// * blueSignalThreshold : Signal threshold for the blue channel between 0.0 and 1.0 [default=0.0]
// "grabber-v4l2" :
// {
// "device" : "/dev/video0",
// "input" : 0,
// "standard" : "no-change",
// "width" : -1,
// "height" : -1,
// "frameDecimation" : 2,
// "sizeDecimation" : 8,
// "priority" : 800,
// "mode" : "2D",
// "cropLeft" : 0,
// "cropRight" : 0,
// "cropTop" : 0,
// "cropBottom" : 0,
// "redSignalThreshold" : 0.0,
// "greenSignalThreshold" : 0.0,
// "blueSignalThreshold" : 0.0
// },
/// The configuration for each individual led. This contains the specification of the area /// 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 /// averaged of an input image for each led to determine its color. Each item in the list
/// contains the following fields: /// contains the following fields:
@ -102,47 +240,47 @@
[ [
{ {
"index" : 0, "index" : 0,
"hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 1, "index" : 1,
"hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 2, "index" : 2,
"hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 3, "index" : 3,
"hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 4, "index" : 4,
"hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 5, "index" : 5,
"hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 6, "index" : 6,
"hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 7, "index" : 7,
"hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 8, "index" : 8,
"hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
@ -182,271 +320,155 @@
}, },
{ {
"index" : 16, "index" : 16,
"hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
},
{
"index" : 17,
"hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 18, "index" : 17,
"hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 19, "index" : 18,
"hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 20, "index" : 19,
"hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 21, "index" : 20,
"hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 22, "index" : 21,
"hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 23, "index" : 22,
"hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 24, "index" : 23,
"hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 25, "index" : 24,
"hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{
"index" : 25,
"hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
},
{ {
"index" : 26, "index" : 26,
"hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 27, "index" : 27,
"hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 28, "index" : 28,
"hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 29, "index" : 29,
"hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 30, "index" : 30,
"hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 31, "index" : 31,
"hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
}, },
{ {
"index" : 32, "index" : 32,
"hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
},
{
"index" : 33,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 }
},
{
"index" : 34,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 }
}, },
{ {
"index" : 35, "index" : 33,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 }
}, },
{ {
"index" : 36, "index" : 34,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 }
}, },
{ {
"index" : 37, "index" : 35,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 }
}, },
{ {
"index" : 38, "index" : 36,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 }
}, },
{ {
"index" : 39, "index" : 37,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 }
}, },
{ {
"index" : 40, "index" : 38,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 }
}, },
{ {
"index" : 41, "index" : 39,
"hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
},
{
"index" : 42,
"hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 43, "index" : 40,
"hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 44, "index" : 41,
"hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 45, "index" : 42,
"hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 46, "index" : 43,
"hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 47, "index" : 44,
"hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}, },
{ {
"index" : 48, "index" : 45,
"hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
},
{
"index" : 49,
"hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
} }
], ],
/// 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.0 and 1.0)
/// * unknownFrameCnt : Number of frames without any detection before the border is set to 0 (default 600) - optional
/// * borderFrameCnt : Number of frames before a consistent detected border gets set (default 50) - optional
/// * maxInconsistentCnt : Number of inconsistent frames that are ignored before a new border gets a chance to proof consistency - optional
/// * blurRemoveCnt : Number of pixels that get removed from the detected border to cut away blur (default 1) - optional
/// * mode : Border detection mode (values "default","classic","osd") - optional
"blackborderdetector" :
{
"enable" : true,
"threshold" : 0.01,
"unknownFrameCnt": 600,
"borderFrameCnt" : 50,
"maxInconsistentCnt" : 10,
"blurRemoveCnt": 1,
"mode" : "default"
},
/// The configuration of the effect engine, contains the following items:
/// * paths : An array with absolute location(s) of directories with effects
/// * bootsequence : The effect selected as 'boot sequence'
/// * effect : name of the effect you want to start. Set to empty if no effect wanted
/// * color : switch to static color after effect is done
/// * duration_ms : duration of boot effect in ms. 0 means effect stays forever
/// * priority : priority of boot effect and static color
"effects" :
{
"paths" :
[
"/opt/hyperion/effects"
]
},
"bootsequence" :
{
"color" : [0,0,0],
"effect" : "Rainbow swirl fast",
"duration_ms" : 3000,
"priority" : 0
},
/// The configuration for the frame-grabber, contains the following items:
/// * 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]
"framegrabber" :
{
"width" : 64,
"height" : 64,
"frequency_Hz" : 10.0
},
/// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields:
/// * xbmcAddress : The IP address of the XBMC-host
/// * xbmcTcpPort : The TCP-port of the XBMC-server
/// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback
/// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show
/// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback
/// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu
/// * grabScreensaver : Flag indicating that the frame-grabber is on(true) when XBMC is on screensaver
/// * enable3DDetection : Flag indicating that the frame-grabber should switch to a 3D compatible modus if a 3D video is playing
"xbmcVideoChecker" :
{
"xbmcAddress" : "127.0.0.1",
"xbmcTcpPort" : 9090,
"grabVideo" : true,
"grabPictures" : true,
"grabAudio" : true,
"grabMenu" : false,
"grabScreensaver" : true,
"enable3DDetection" : true
},
/// 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 Proto server which enables the protobuffer remote interface
/// * port : Port at which the protobuffer server is started
"protoServer" :
{
"port" : 19445
},
/// The configuration of the boblight server which enables the boblight remote interface
/// * port : Port at which the boblight server is started
// "boblightServer" :
// {
// "port" : 19333
// },
"endOfJson" : "endOfJson" "endOfJson" : "endOfJson"
} }

View File

@ -19,6 +19,21 @@
"colorOrder" : "rgb" "colorOrder" : "rgb"
}, },
/// Configuration for message forwarding to other hyperions
/// protobuffer and json remote interface are forwarded to configured hosts
/// 'proto' is mostly used for video streams and 'json' for effects
///
/// ** pay attention which port you use. use correct ports for protols **
///
/// * 'proto' : list of host in form of <ip>:<port>
/// * 'json' : list of host in form of <ip>:<port>
/// "forwarder" :
/// {
/// "proto" : [ "127.0.0.1:19445","192.168.178.88:19445" ],
/// "json" : [ "127.0.0.1:19444","192.168.178.88:19444" ]
/// },
/// Color manipulation configuration used to tune the output colors to specific surroundings. /// Color manipulation configuration used to tune the output colors to specific surroundings.
/// The configuration contains a list of color-transforms. Each transform contains the /// The configuration contains a list of color-transforms. Each transform contains the
/// following fields: /// following fields:

View File

@ -0,0 +1,10 @@
{
"name" : "Cinema brighten lights",
"script" : "fade.py",
"args" :
{
"fade-time" : 5.0,
"color-start" : [ 136, 97, 7 ],
"color-end" : [ 238, 173, 47 ]
}
}

View File

@ -0,0 +1,10 @@
{
"name" : "Cinema dim lights",
"script" : "fade.py",
"args" :
{
"fade-time" : 5.0,
"color-start" : [ 238, 173, 47 ],
"color-end" : [ 136, 97, 7 ]
}
}

27
effects/fade.py Normal file
View File

@ -0,0 +1,27 @@
import hyperion, time
# Get the parameters
fadeTime = float(hyperion.args.get('fade-time', 5.0))
colorStart = hyperion.args.get('color-start', (255,174,11))
colorEnd = hyperion.args.get('color-end', (100,100,100))
color_step = (
(colorEnd[0] - colorStart[0]) / 256.0,
(colorEnd[1] - colorStart[1]) / 256.0,
(colorEnd[2] - colorStart[2]) / 256.0
)
# fade color
calcChannel = lambda i: min(max(int(colorStart[i] + color_step[i]*step),0),255)
for step in range(256):
if hyperion.abort():
break
hyperion.setColor( calcChannel(0),calcChannel(1),calcChannel(2) )
time.sleep( fadeTime / 256 )
# maintain color until effect end
hyperion.setColor(colorEnd[0],colorEnd[1],colorEnd[2])
while not hyperion.abort():
time.sleep(1)

9
effects/random.json Normal file
View File

@ -0,0 +1,9 @@
{
"name" : "Random",
"script" : "random.py",
"args" :
{
"speed" : 1.0,
"saturation" : 1.0
}
}

21
effects/random.py Normal file
View File

@ -0,0 +1,21 @@
import hyperion, time, colorsys, random
# get args
sleepTime = float(hyperion.args.get('speed', 1.0))
saturation = float(hyperion.args.get('saturation', 1.0))
ledData = bytearray()
# Initialize the led data
for i in range(hyperion.ledCount):
ledData += bytearray((0,0,0))
# Start the write data loop
while not hyperion.abort():
hyperion.setColor(ledData)
for i in range(hyperion.ledCount):
if random.randrange(10) == 1:
rgb = colorsys.hsv_to_rgb(random.random(), saturation, random.random())
ledData[i*3 ] = int(255*rgb[0])
ledData[i*3+1] = int(255*rgb[1])
ledData[i*3+2] = int(255*rgb[2])
time.sleep(sleepTime)

10
effects/running_dots.json Normal file
View File

@ -0,0 +1,10 @@
{
"name" : "Running dots",
"script" : "running_dots.py",
"args" :
{
"speed" : 1.5,
"whiteLevel" : 100,
"colorLevel" : 230
}
}

43
effects/running_dots.py Normal file
View File

@ -0,0 +1,43 @@
import hyperion, time, colorsys, random
# get options from args
sleepTime = float(hyperion.args.get('speed', 1.5)) * 0.005
whiteLevel = int(hyperion.args.get('whiteLevel', 0))
lvl = int(hyperion.args.get('colorLevel', 220))
# check value
whiteLevel = min( whiteLevel, 254 )
lvl = min( lvl, 255 )
if whiteLevel >= lvl:
lvl = 255
# Initialize the led data
ledData = bytearray()
for i in range(hyperion.ledCount):
ledData += bytearray((0,0,0))
runners = [
{ "pos":0, "step": 4, "lvl":lvl},
{ "pos":1, "step": 5, "lvl":lvl},
{ "pos":2, "step": 6, "lvl":lvl},
{ "pos":0, "step": 7, "lvl":lvl},
{ "pos":1, "step": 8, "lvl":lvl},
{ "pos":2, "step": 9, "lvl":lvl},
#{ "pos":0, "step":10, "lvl":lvl},
#{ "pos":1, "step":11, "lvl":lvl},
#{ "pos":2, "step":12, "lvl":lvl},
]
# Start the write data loop
counter = 0
while not hyperion.abort():
counter += 1
for r in runners:
if counter % r["step"] == 0:
ledData[r["pos"]] = whiteLevel
r["pos"] = (r["pos"]+3) % (hyperion.ledCount*3)
ledData[r["pos"]] = r["lvl"]
hyperion.setColor(ledData)
time.sleep(sleepTime)

11
effects/shutdown.json Normal file
View File

@ -0,0 +1,11 @@
{
"name" : "System Shutdown",
"script" : "shutdown.py",
"args" :
{
"speed" : 1.2,
"alarm-color" : [255,0,0],
"post-color" : [255,174,11],
"shutdown-enabled" : false
}
}

49
effects/shutdown.py Normal file
View File

@ -0,0 +1,49 @@
import hyperion, time, subprocess
def setPixel(x,y,rgb):
global imageData, width
offset = y*width*3 + x*3
if offset+2 < len(imageData):
imageData[offset] = rgb[0]
imageData[offset+1] = rgb[1]
imageData[offset+2] = rgb[2]
# Initialize the led data and args
sleepTime = float(hyperion.args.get('speed', 1.0))*0.5
alarmColor = hyperion.args.get('alarm-color', (255,0,0))
postColor = hyperion.args.get('post-color', (255,174,11))
off = bool(hyperion.args.get('shutdown-enabled', False))
width = 12
height = 10
imageData = bytearray(height * width * (0,0,0))
# Start the write data loop
for i in range(6):
if hyperion.abort():
off = False
break
if i % 2:
hyperion.setColor(alarmColor[0], alarmColor[1], alarmColor[2])
else:
hyperion.setColor(0, 0, 0)
time.sleep(sleepTime)
for y in range(height,0,-1):
if hyperion.abort():
off = False
break
for x in range(width):
setPixel(x, y-1, alarmColor)
hyperion.setImage(width, height, imageData)
time.sleep(sleepTime)
time.sleep(1)
for y in range(height):
for x in range(width):
setPixel(x, y, postColor)
hyperion.setImage(width, height, imageData)
time.sleep(2)
if off and not hyperion.abort():
subprocess.call("halt")

View File

@ -3,8 +3,8 @@
"script" : "snake.py", "script" : "snake.py",
"args" : "args" :
{ {
"rotation-time" : 10.0, "rotation-time" : 12.0,
"color" : [255, 0, 0], "color" : [255, 0, 0],
"percentage" : 25 "percentage" : 10
} }
} }

View File

@ -23,7 +23,7 @@ for i in range(hyperion.ledCount-snakeLeds):
ledData += bytearray((0, 0, 0)) ledData += bytearray((0, 0, 0))
for i in range(1,snakeLeds+1): for i in range(1,snakeLeds+1):
rgb = colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2]/i) rgb = colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2]*(snakeLeds-i)/snakeLeds)
ledData += bytearray((int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))) ledData += bytearray((int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255)))
# Calculate the sleep time and rotation increment # Calculate the sleep time and rotation increment

14
effects/sparks-color.json Normal file
View File

@ -0,0 +1,14 @@
{
"name" : "Sparks Color",
"script" : "sparks.py",
"args" :
{
"rotation-time" : 3.0,
"sleep-time" : 0.05,
"brightness" : 1.0,
"saturation" : 1.0,
"reverse" : false,
"color" : [255,255,255],
"random-color" : true
}
}

14
effects/sparks.json Normal file
View File

@ -0,0 +1,14 @@
{
"name" : "Sparks",
"script" : "sparks.py",
"args" :
{
"rotation-time" : 3.0,
"sleep-time" : 0.05,
"brightness" : 1.0,
"saturation" : 1.0,
"reverse" : false,
"color" : [255,255,255],
"random-color" : false
}
}

37
effects/sparks.py Normal file
View File

@ -0,0 +1,37 @@
import hyperion, time, colorsys, random
# Get the parameters
rotationTime = float(hyperion.args.get('rotation-time', 3.0))
sleepTime = float(hyperion.args.get('sleep-time', 0.05))
brightness = float(hyperion.args.get('brightness', 1.0))
saturation = float(hyperion.args.get('saturation', 1.0))
reverse = bool(hyperion.args.get('reverse', False))
color = list(hyperion.args.get('color', (255,255,255)))
randomColor = bool(hyperion.args.get('random-color', False))
# Check parameters
rotationTime = max(0.1, rotationTime)
brightness = max(0.0, min(brightness, 1.0))
saturation = max(0.0, min(saturation, 1.0))
# Initialize the led data
ledData = bytearray()
for i in range(hyperion.ledCount):
ledData += bytearray((0, 0, 0))
# Start the write data loop
while not hyperion.abort():
ledData[:] = bytearray(3*hyperion.ledCount)
for i in range(hyperion.ledCount):
if random.random() < 0.005:
if randomColor:
rgb = colorsys.hsv_to_rgb(random.random(), 1, 1)
for n in range(3):
color[n] = int(rgb[n]*255)
for n in range(3):
ledData[i*3+n] = color[n]
hyperion.setColor(ledData)
time.sleep(sleepTime)

View File

@ -1,6 +1,4 @@
import hyperion import hyperion, time
import time
import colorsys
# Get the rotation time # Get the rotation time
color = hyperion.args.get('color', (255,255,255)) color = hyperion.args.get('color', (255,255,255))
@ -12,13 +10,9 @@ frequency = min(100.0, frequency)
# Compute the strobe interval # Compute the strobe interval
sleepTime = 1.0 / frequency sleepTime = 1.0 / frequency
# Initialize the led data
blackLedsData = bytearray(hyperion.ledCount * ( 0, 0, 0))
whiteLedsData = bytearray(hyperion.ledCount * color)
# Start the write data loop # Start the write data loop
while not hyperion.abort(): while not hyperion.abort():
hyperion.setColor(blackLedsData) hyperion.setColor(0, 0, 0)
time.sleep(sleepTime) time.sleep(sleepTime)
hyperion.setColor(whiteLedsData) hyperion.setColor(color[0], color[1], color[2])
time.sleep(sleepTime) time.sleep(sleepTime)

8
effects/traces.json Normal file
View File

@ -0,0 +1,8 @@
{
"name" : "Color traces",
"script" : "traces.py",
"args" :
{
"speed" : 1.0
}
}

34
effects/traces.py Normal file
View File

@ -0,0 +1,34 @@
import hyperion
import time
import colorsys
import random
# Initialize the led data
ledData = bytearray()
for i in range(hyperion.ledCount):
ledData += bytearray((0,0,0))
sleepTime = float(hyperion.args.get('speed', 1.0)) * 0.004
runners = [
{ "i":0, "pos":0, "c":0, "step":9 , "lvl":255},
{ "i":1, "pos":0, "c":0, "step":8 , "lvl":255},
{ "i":2, "pos":0, "c":0, "step":7 , "lvl":255},
{ "i":0, "pos":0, "c":0, "step":6 , "lvl":100},
{ "i":1, "pos":0, "c":0, "step":5 , "lvl":100},
{ "i":2, "pos":0, "c":0, "step":4, "lvl":100},
]
# Start the write data loop
while not hyperion.abort():
for r in runners:
if r["c"] == 0:
#ledData[r["pos"]*3+r["i"]] = 0
r["c"] = r["step"]
r["pos"] = (r["pos"]+1)%hyperion.ledCount
ledData[r["pos"]*3+r["i"]] = int(r["lvl"]*(0.2+0.8*random.random()))
else:
r["c"] -= 1
hyperion.setColor(ledData)
time.sleep(sleepTime)

View File

@ -68,6 +68,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;
@ -91,4 +94,7 @@ private:
/// Pointer to Hyperion for writing led values /// Pointer to Hyperion for writing led values
Hyperion * _hyperion; Hyperion * _hyperion;
// forwarding enabled
bool _forward;
}; };

View File

@ -68,6 +68,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;
@ -91,4 +94,7 @@ private:
/// Pointer to Hyperion for writing led values /// Pointer to Hyperion for writing led values
Hyperion * _hyperion; Hyperion * _hyperion;
// forwarding enabled
bool _forward;
}; };

View File

@ -68,6 +68,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;

View File

@ -69,6 +69,9 @@ public slots:
/// ///
void setVideoMode(const VideoMode videoMode); void setVideoMode(const VideoMode videoMode);
signals:
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private: private:
/// The update rate [Hz] /// The update rate [Hz]
const int _updateInterval_ms; const int _updateInterval_ms;

View File

@ -44,6 +44,7 @@ public slots:
signals: signals:
void emitColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms); void emitColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms);
void emitImage(int priority, const Image<ColorRgb> & image, const int timeout_ms);
private slots: private slots:
void newFrame(const Image<ColorRgb> & image); void newFrame(const Image<ColorRgb> & image);

View File

@ -13,6 +13,7 @@
// Hyperion includes // Hyperion includes
#include <hyperion/LedString.h> #include <hyperion/LedString.h>
#include <hyperion/PriorityMuxer.h> #include <hyperion/PriorityMuxer.h>
#include <hyperion/MessageForwarder.h>
// Effect engine includes // Effect engine includes
#include <effectengine/EffectDefinition.h> #include <effectengine/EffectDefinition.h>
@ -125,6 +126,10 @@ public slots:
/// Tell Hyperion that the transforms have changed and the leds need to be updated /// Tell Hyperion that the transforms have changed and the leds need to be updated
void transformsUpdated(); void transformsUpdated();
/// Returns MessageForwarder Object
/// @return instance of message forwarder object
MessageForwarder * getForwarder();
/// ///
/// Clears the given priority channel. This will switch the led-colors to the colors of the next /// Clears the given priority channel. This will switch the led-colors to the colors of the next
/// lower priority channel (or off if no more channels are set) /// lower priority channel (or off if no more channels are set)
@ -168,6 +173,7 @@ public:
static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig); static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig);
static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice); static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice);
static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig);
signals: signals:
/// Signal which is emitted when a priority channel is actively cleared /// Signal which is emitted when a priority channel is actively cleared
@ -201,6 +207,9 @@ private:
/// Effect engine /// Effect engine
EffectEngine * _effectEngine; EffectEngine * _effectEngine;
// proto and json Message forwarder
MessageForwarder * _messageForwarder;
/// The timer for handling priority channel timeouts /// The timer for handling priority channel timeouts
QTimer _timer; QTimer _timer;
}; };

View File

@ -0,0 +1,38 @@
#pragma once
// STL includes
#include <vector>
#include <map>
#include <cstdint>
#include <limits>
// QT includes
#include <QList>
#include <QStringList>
#include <QHostAddress>
// Utils includes
#include <utils/ColorRgb.h>
class MessageForwarder
{
public:
struct JsonSlaveAddress {
QHostAddress addr;
quint16 port;
};
MessageForwarder();
~MessageForwarder();
void addJsonSlave(std::string slave);
void addProtoSlave(std::string slave);
bool protoForwardingEnabled();
QStringList getProtoSlaves();
QList<MessageForwarder::JsonSlaveAddress> getJsonSlaves();
private:
QStringList _protoSlaves;
QList<MessageForwarder::JsonSlaveAddress> _jsonSlaves;
};

View File

@ -7,6 +7,7 @@
#include <QColor> #include <QColor>
#include <QImage> #include <QImage>
#include <QTcpSocket> #include <QTcpSocket>
#include <QTimer>
#include <QMap> #include <QMap>
// hyperion util // hyperion util
@ -19,8 +20,11 @@
/// ///
/// Connection class to setup an connection to the hyperion server and execute commands /// Connection class to setup an connection to the hyperion server and execute commands
/// ///
class ProtoConnection class ProtoConnection : public QObject
{ {
Q_OBJECT
public: public:
/// ///
/// Constructor /// Constructor
@ -67,10 +71,6 @@ public:
/// ///
void clearAll(); void clearAll();
private:
/// Try to connect to the Hyperion host
void connectToHost();
/// ///
/// Send a command message and receive its reply /// Send a command message and receive its reply
/// ///
@ -78,6 +78,13 @@ private:
/// ///
void sendMessage(const proto::HyperionRequest & message); void sendMessage(const proto::HyperionRequest & message);
private slots:
/// Try to connect to the Hyperion host
void connectToHost();
private:
/// ///
/// Parse a reply message /// Parse a reply message
/// ///
@ -99,4 +106,7 @@ private:
/// Skip receiving reply messages from Hyperion if set /// Skip receiving reply messages from Hyperion if set
bool _skipReply; bool _skipReply;
QTimer _timer;
QAbstractSocket::SocketState _prevSocketState;
}; };

View File

@ -6,11 +6,23 @@
// Qt includes // Qt includes
#include <QTcpServer> #include <QTcpServer>
#include <QSet> #include <QSet>
#include <QList>
#include <QStringList>
// Hyperion includes // Hyperion includes
#include <hyperion/Hyperion.h> #include <hyperion/Hyperion.h>
// hyperion includes
#include <utils/Image.h>
#include <utils/ColorRgb.h>
// forward decl
class ProtoClientConnection; class ProtoClientConnection;
class ProtoConnection;
namespace proto {
class HyperionRequest;
}
/// ///
/// This class creates a TCP server which accepts connections wich can then send /// This class creates a TCP server which accepts connections wich can then send
@ -35,6 +47,9 @@ public:
/// ///
uint16_t getPort() const; uint16_t getPort() const;
public slots:
void sendImageToProtoSlaves(int priority, const Image<ColorRgb> & image, int duration_ms);
private slots: private slots:
/// ///
/// Slot which is called when a client tries to create a new connection /// Slot which is called when a client tries to create a new connection
@ -47,6 +62,8 @@ private slots:
/// ///
void closedConnection(ProtoClientConnection * connection); void closedConnection(ProtoClientConnection * connection);
void newMessage(const proto::HyperionRequest * message);
private: private:
/// Hyperion instance /// Hyperion instance
Hyperion * _hyperion; Hyperion * _hyperion;
@ -56,4 +73,9 @@ private:
/// List with open connections /// List with open connections
QSet<ProtoClientConnection *> _openConnections; QSet<ProtoClientConnection *> _openConnections;
QStringList _forwardClients;
/// Hyperion proto connection object for forwarding
QList<ProtoConnection*> _proxy_connections;
}; };

View File

@ -5,6 +5,8 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <utils/ColorRgb.h>
template <typename Pixel_T> template <typename Pixel_T>
class Image class Image
@ -183,6 +185,25 @@ public:
{ {
return _pixels; return _pixels;
} }
///
/// Convert image of any color order to a RGB image.
///
/// @param[out] image The image that buffers the output
///
void toRgb(Image<ColorRgb>& image)
{
image.resize(_width, _height);
const unsigned imageSize = _width * _height;
for (unsigned idx=0; idx<imageSize; idx++)
{
const Pixel_T color = memptr()[idx];
image.memptr()[idx] = ColorRgb{color.red, color.green, color.blue};
}
}
private: private:
/// ///

View File

@ -1,5 +1,7 @@
find_package(PythonLibs REQUIRED) find_package(PythonLibs REQUIRED)
#OpenElec uses 2.7, if you want to compile for OpenElec require 2.7
#find_package(PythonLibs 2.7 REQUIRED)
# Include the python directory. Also include the parent (which is for example /usr/include) # Include the python directory. Also include the parent (which is for example /usr/include)
# which may be required when it is not includes by the (cross-) compiler by default. # which may be required when it is not includes by the (cross-) compiler by default.

View File

@ -13,7 +13,7 @@
#include <sys/types.h> #include <sys/types.h>
// Local includes // Local includes
#include "AmlogicGrabber.h" #include <grabber/AmlogicGrabber.h>
// Flags copied from 'include/linux/amlogic/amports/amvideocap.h' at https://github.com/codesnake/linux-amlogic // Flags copied from 'include/linux/amlogic/amports/amvideocap.h' at https://github.com/codesnake/linux-amlogic
#define AMVIDEOCAP_IOC_MAGIC 'V' #define AMVIDEOCAP_IOC_MAGIC 'V'

View File

@ -9,7 +9,7 @@
// Amlogic grabber includes // Amlogic grabber includes
#include <grabber/AmlogicWrapper.h> #include <grabber/AmlogicWrapper.h>
#include "AmlogicGrabber.h" #include <grabber/AmlogicGrabber.h>
AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) : AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) :
@ -26,6 +26,7 @@ AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeig
// Configure the timer to generate events every n milliseconds // Configure the timer to generate events every n milliseconds
_timer.setInterval(_updateInterval_ms); _timer.setInterval(_updateInterval_ms);
_timer.setSingleShot(false); _timer.setSingleShot(false);
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
_processor->setSize(grabWidth, grabHeight); _processor->setSize(grabWidth, grabHeight);
@ -55,8 +56,14 @@ void AmlogicWrapper::action()
return; return;
} }
_processor->process(_image, _ledColors); if ( _forward )
{
Image<ColorRgb> image_rgb;
_image.toRgb(image_rgb);
emit emitImage(_priority, image_rgb, _timeout_ms);
}
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }

View File

@ -7,7 +7,7 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic)
SET(AmlogicQT_HEADERS ${CURRENT_HEADER_DIR}/AmlogicWrapper.h) SET(AmlogicQT_HEADERS ${CURRENT_HEADER_DIR}/AmlogicWrapper.h)
SET(AmlogicHEADERS SET(AmlogicHEADERS
${CURRENT_SOURCE_DIR}/AmlogicGrabber.h ${CURRENT_HEADER_DIR}/AmlogicGrabber.h
) )
SET(AmlogicSOURCES SET(AmlogicSOURCES

View File

@ -13,7 +13,7 @@ SET(DispmanxGrabberQT_HEADERS
) )
SET(DispmanxGrabberHEADERS SET(DispmanxGrabberHEADERS
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.h ${CURRENT_HEADER_DIR}/DispmanxFrameGrabber.h
) )
SET(DispmanxGrabberSOURCES SET(DispmanxGrabberSOURCES
@ -22,9 +22,9 @@ SET(DispmanxGrabberSOURCES
) )
if(ENABLE_QT5) if(ENABLE_QT5)
QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
else(ENABLE_QT5) else(ENABLE_QT5)
QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
endif(ENABLE_QT5) endif(ENABLE_QT5)
add_library(dispmanx-grabber add_library(dispmanx-grabber
@ -37,4 +37,5 @@ add_library(dispmanx-grabber
target_link_libraries(dispmanx-grabber target_link_libraries(dispmanx-grabber
hyperion hyperion
${QT_LIBRARIES} ${QT_LIBRARIES}
${BCM_LIBRARIES}) ${BCM_LIBRARIES}
)

View File

@ -4,7 +4,7 @@
#include <iostream> #include <iostream>
// Local includes // Local includes
#include "DispmanxFrameGrabber.h" #include "grabber/DispmanxFrameGrabber.h"
DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height) : DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height) :
_vc_display(0), _vc_display(0),

View File

@ -9,7 +9,7 @@
// Dispmanx grabber includes // Dispmanx grabber includes
#include <grabber/DispmanxWrapper.h> #include <grabber/DispmanxWrapper.h>
#include "DispmanxFrameGrabber.h" #include <grabber/DispmanxFrameGrabber.h>
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) : DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) :
@ -28,6 +28,7 @@ DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHe
_timer.setSingleShot(false); _timer.setSingleShot(false);
_processor->setSize(grabWidth, grabHeight); _processor->setSize(grabWidth, grabHeight);
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
// Connect the QTimer to this // Connect the QTimer to this
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(action())); QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(action()));
@ -51,10 +52,17 @@ void DispmanxWrapper::action()
// Grab frame into the allocated image // Grab frame into the allocated image
_frameGrabber->grabFrame(_image); _frameGrabber->grabFrame(_image);
_processor->process(_image, _ledColors); if ( _forward )
{
Image<ColorRgb> image_rgb;
_image.toRgb(image_rgb);
emit emitImage(_priority, image_rgb, _timeout_ms);
}
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }
void DispmanxWrapper::stop() void DispmanxWrapper::stop()
{ {
// Stop the timer, effectivly stopping the process // Stop the timer, effectivly stopping the process

View File

@ -46,8 +46,9 @@ void FramebufferWrapper::action()
// Grab frame into the allocated image // Grab frame into the allocated image
_frameGrabber->grabFrame(_image); _frameGrabber->grabFrame(_image);
_processor->process(_image, _ledColors); emit emitImage(_priority, _image, _timeout_ms);
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }
void FramebufferWrapper::stop() void FramebufferWrapper::stop()

View File

@ -46,8 +46,9 @@ void OsxWrapper::action()
// Grab frame into the allocated image // Grab frame into the allocated image
_frameGrabber->grabFrame(_image); _frameGrabber->grabFrame(_image);
_processor->process(_image, _ledColors); emit emitImage(_priority, _image, _timeout_ms);
_processor->process(_image, _ledColors);
_hyperion->setColors(_priority, _ledColors, _timeout_ms); _hyperion->setColors(_priority, _ledColors, _timeout_ms);
} }
void OsxWrapper::stop() void OsxWrapper::stop()

View File

@ -94,6 +94,9 @@ void V4L2Wrapper::newFrame(const Image<ColorRgb> &image)
// process the new image // process the new image
_processor->process(image, _ledColors); _processor->process(image, _ledColors);
// forward to other hyperions
emit emitImage(_priority, image, _timeout_ms);
// send colors to Hyperion // send colors to Hyperion
emit emitColors(_priority, _ledColors, _timeout_ms); emit emitColors(_priority, _ledColors, _timeout_ms);
} }

View File

@ -18,6 +18,7 @@ SET(Hyperion_HEADERS
${CURRENT_HEADER_DIR}/PriorityMuxer.h ${CURRENT_HEADER_DIR}/PriorityMuxer.h
${CURRENT_SOURCE_DIR}/MultiColorTransform.h ${CURRENT_SOURCE_DIR}/MultiColorTransform.h
${CURRENT_HEADER_DIR}/MessageForwarder.h
) )
SET(Hyperion_SOURCES SET(Hyperion_SOURCES
@ -30,6 +31,7 @@ SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp ${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp
${CURRENT_SOURCE_DIR}/MessageForwarder.cpp
) )
set(Hyperion_RESOURCES set(Hyperion_RESOURCES

View File

@ -267,12 +267,45 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig,
} }
MessageForwarder * Hyperion::createMessageForwarder(const Json::Value & forwarderConfig)
{
MessageForwarder * forwarder = new MessageForwarder();
if ( ! forwarderConfig.isNull() )
{
if ( ! forwarderConfig["json"].isNull() && forwarderConfig["json"].isArray() )
{
for (const Json::Value& addr : forwarderConfig["json"])
{
std::cout << "Json forward to " << addr.asString() << std::endl;
forwarder->addJsonSlave(addr.asString());
}
}
if ( ! forwarderConfig["proto"].isNull() && forwarderConfig["proto"].isArray() )
{
for (const Json::Value& addr : forwarderConfig["proto"])
{
std::cout << "Proto forward to " << addr.asString() << std::endl;
forwarder->addProtoSlave(addr.asString());
}
}
}
return forwarder;
}
MessageForwarder * Hyperion::getForwarder()
{
return _messageForwarder;
}
Hyperion::Hyperion(const Json::Value &jsonConfig) : Hyperion::Hyperion(const Json::Value &jsonConfig) :
_ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))), _ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))),
_muxer(_ledString.leds().size()), _muxer(_ledString.leds().size()),
_raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])), _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])),
_device(LedDeviceFactory::construct(jsonConfig["device"])), _device(LedDeviceFactory::construct(jsonConfig["device"])),
_effectEngine(nullptr), _effectEngine(nullptr),
_messageForwarder(createMessageForwarder(jsonConfig["forwarder"])),
_timer() _timer()
{ {
if (!_raw2ledTransform->verifyTransforms()) if (!_raw2ledTransform->verifyTransforms())
@ -314,6 +347,9 @@ Hyperion::~Hyperion()
// delete the color transform // delete the color transform
delete _raw2ledTransform; delete _raw2ledTransform;
// delete the message forwarder
delete _messageForwarder;
} }
unsigned Hyperion::getLedCount() const unsigned Hyperion::getLedCount() const

View File

@ -0,0 +1,51 @@
// STL includes
#include <stdexcept>
#include <hyperion/MessageForwarder.h>
MessageForwarder::MessageForwarder()
{
}
MessageForwarder::~MessageForwarder()
{
}
void MessageForwarder::addJsonSlave(std::string slave)
{
QStringList parts = QString(slave.c_str()).split(":");
if (parts.size() != 2)
throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(slave.c_str()).toStdString());
bool ok;
quint16 port = parts[1].toUShort(&ok);
if (!ok)
throw std::runtime_error(QString("Wrong address: Unable to parse the port number (%1)").arg(parts[1]).toStdString());
JsonSlaveAddress c;
c.addr = QHostAddress(parts[0]);
c.port = port;
_jsonSlaves << c;
}
void MessageForwarder::addProtoSlave(std::string slave)
{
_protoSlaves << QString(slave.c_str());
}
QStringList MessageForwarder::getProtoSlaves()
{
return _protoSlaves;
}
QList<MessageForwarder::JsonSlaveAddress> MessageForwarder::getJsonSlaves()
{
return _jsonSlaves;
}
bool MessageForwarder::protoForwardingEnabled()
{
return ! _protoSlaves.empty();
}

View File

@ -16,6 +16,7 @@
// hyperion util includes // hyperion util includes
#include <hyperion/ImageProcessorFactory.h> #include <hyperion/ImageProcessorFactory.h>
#include <hyperion/ImageProcessor.h> #include <hyperion/ImageProcessor.h>
#include <hyperion/MessageForwarder.h>
#include <hyperion/ColorTransform.h> #include <hyperion/ColorTransform.h>
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
@ -250,8 +251,27 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
handleNotImplemented(); handleNotImplemented();
} }
void JsonClientConnection::forwardJsonMessage(const Json::Value & message)
{
QTcpSocket client;
QList<MessageForwarder::JsonSlaveAddress> list = _hyperion->getForwarder()->getJsonSlaves();
for ( int i=0; i<list.size(); i++ )
{
client.connectToHost(list.at(i).addr, list.at(i).port);
if ( client.waitForConnected(500) )
{
sendMessage(message,&client);
client.close();
}
}
}
void JsonClientConnection::handleColorCommand(const Json::Value &message) void JsonClientConnection::handleColorCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt(); int duration = message.get("duration", -1).asInt();
@ -289,6 +309,8 @@ void JsonClientConnection::handleColorCommand(const Json::Value &message)
void JsonClientConnection::handleImageCommand(const Json::Value &message) void JsonClientConnection::handleImageCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt(); int duration = message.get("duration", -1).asInt();
@ -320,6 +342,8 @@ void JsonClientConnection::handleImageCommand(const Json::Value &message)
void JsonClientConnection::handleEffectCommand(const Json::Value &message) void JsonClientConnection::handleEffectCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt(); int duration = message.get("duration", -1).asInt();
@ -418,6 +442,8 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
void JsonClientConnection::handleClearCommand(const Json::Value &message) void JsonClientConnection::handleClearCommand(const Json::Value &message)
{ {
forwardJsonMessage(message);
// extract parameters // extract parameters
int priority = message["priority"].asInt(); int priority = message["priority"].asInt();
@ -428,8 +454,10 @@ void JsonClientConnection::handleClearCommand(const Json::Value &message)
sendSuccessReply(); sendSuccessReply();
} }
void JsonClientConnection::handleClearallCommand(const Json::Value &) void JsonClientConnection::handleClearallCommand(const Json::Value & message)
{ {
forwardJsonMessage(message);
// clear priority // clear priority
_hyperion->clearall(); _hyperion->clearall();
@ -534,6 +562,47 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
} }
} }
void JsonClientConnection::sendMessage(const Json::Value & message, QTcpSocket * socket)
{
// serialize message (FastWriter already appends a newline)
std::string serializedMessage = Json::FastWriter().write(message);
// write message
socket->write(serializedMessage.c_str());
if (!socket->waitForBytesWritten())
{
//std::cout << "Error while writing data to host" << std::endl;
return;
}
// read reply data
QByteArray serializedReply;
while (!serializedReply.contains('\n'))
{
// receive reply
if (!socket->waitForReadyRead())
{
//std::cout << "Error while reading data from host" << std::endl;
return;
}
serializedReply += socket->readAll();
}
int bytes = serializedReply.indexOf('\n') + 1; // Find the end of message
// parse reply data
Json::Reader jsonReader;
Json::Value reply;
if (!jsonReader.parse(serializedReply.constData(), serializedReply.constData() + bytes, reply))
{
//std::cout << "Error while parsing reply: invalid json" << std::endl;
return;
}
}
void JsonClientConnection::sendSuccessReply() void JsonClientConnection::sendSuccessReply()
{ {
// create reply // create reply

View File

@ -124,6 +124,7 @@ private:
/// @param message The JSON message to send /// @param message The JSON message to send
/// ///
void sendMessage(const Json::Value & message); void sendMessage(const Json::Value & message);
void sendMessage(const Json::Value & message, QTcpSocket * socket);
/// ///
/// Send a standard reply indicating success /// Send a standard reply indicating success
@ -147,6 +148,11 @@ private:
/// ///
void handleWebSocketFrame(); void handleWebSocketFrame();
///
/// forward json message
///
void forwardJsonMessage(const Json::Value & message);
private: private:
/// ///
/// Check if a JSON messag is valid according to a given JSON schema /// Check if a JSON messag is valid according to a given JSON schema

View File

@ -16,6 +16,14 @@ JsonServer::JsonServer(Hyperion *hyperion, uint16_t port) :
throw std::runtime_error("Json server could not bind to port"); throw std::runtime_error("Json server could not bind to port");
} }
QList<MessageForwarder::JsonSlaveAddress> list = _hyperion->getForwarder()->getJsonSlaves();
for ( int i=0; i<list.size(); i++ )
{
if ( list.at(i).addr == QHostAddress::LocalHost && list.at(i).port == port ) {
throw std::runtime_error("Loop between proto server and forwarder detected. Fix your config!");
}
}
// Set trigger for incoming connections // Set trigger for incoming connections
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection())); connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));

View File

@ -1,8 +1,8 @@
#include "LedDeviceFadeCandy.h" #include "LedDeviceFadeCandy.h"
static const ssize_t MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds static const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds
static const ssize_t OPC_SET_PIXELS = 0; // OPC command codes static const unsigned OPC_SET_PIXELS = 0; // OPC command codes
static const ssize_t OPC_HEADER_SIZE = 4; // OPC header size static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) : LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) :

View File

@ -81,6 +81,9 @@ void ProtoClientConnection::socketClosed()
void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message) void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message)
{ {
// forward messages
emit newMessage(&message);
switch (message.command()) switch (message.command())
{ {
case proto::HyperionRequest::COLOR: case proto::HyperionRequest::COLOR:

View File

@ -6,12 +6,14 @@
// Qt includes // Qt includes
#include <QByteArray> #include <QByteArray>
#include <QTcpSocket> #include <QTcpSocket>
#include <QStringList>
// Hyperion includes // Hyperion includes
#include <hyperion/Hyperion.h> #include <hyperion/Hyperion.h>
// proto includes // proto includes
#include "message.pb.h" #include "message.pb.h"
#include "protoserver/ProtoConnection.h"
class ImageProcessor; class ImageProcessor;
@ -41,6 +43,7 @@ signals:
/// @param connection This connection object /// @param connection This connection object
/// ///
void connectionClosed(ProtoClientConnection * connection); void connectionClosed(ProtoClientConnection * connection);
void newMessage(const proto::HyperionRequest * message);
private slots: private slots:
/// ///

View File

@ -9,7 +9,8 @@
ProtoConnection::ProtoConnection(const std::string & a) : ProtoConnection::ProtoConnection(const std::string & a) :
_socket(), _socket(),
_skipReply(false) _skipReply(false),
_prevSocketState(QAbstractSocket::UnconnectedState)
{ {
QString address(a.c_str()); QString address(a.c_str());
QStringList parts = address.split(":"); QStringList parts = address.split(":");
@ -29,10 +30,18 @@ ProtoConnection::ProtoConnection(const std::string & a) :
// try to connect to host // try to connect to host
std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl; std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
connectToHost(); connectToHost();
// start the connection timer
_timer.setInterval(5000);
_timer.setSingleShot(false);
connect(&_timer,SIGNAL(timeout()), this, SLOT(connectToHost()) );
_timer.start();
} }
ProtoConnection::~ProtoConnection() ProtoConnection::~ProtoConnection()
{ {
_timer.stop();
_socket.close(); _socket.close();
} }
@ -91,19 +100,36 @@ void ProtoConnection::clearAll()
void ProtoConnection::connectToHost() void ProtoConnection::connectToHost()
{ {
// try connection only when
if (_socket.state() == QAbstractSocket::UnconnectedState)
{
_socket.connectToHost(_host, _port); _socket.connectToHost(_host, _port);
if (_socket.waitForConnected()) { //_socket.waitForConnected(1000);
std::cout << "Connected to Hyperion host" << std::endl;
} }
} }
void ProtoConnection::sendMessage(const proto::HyperionRequest &message) void ProtoConnection::sendMessage(const proto::HyperionRequest &message)
{ {
if (_socket.state() == QAbstractSocket::UnconnectedState) // print out connection message only when state is changed
if (_socket.state() != _prevSocketState )
{ {
std::cout << "Currently disconnected: trying to connect to host" << std::endl; switch (_socket.state() )
connectToHost(); {
case QAbstractSocket::UnconnectedState:
std::cout << "No connection to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
break;
case QAbstractSocket::ConnectedState:
std::cout << "Connected to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
break;
default:
//std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
break;
} }
_prevSocketState = _socket.state();
}
if (_socket.state() != QAbstractSocket::ConnectedState) if (_socket.state() != QAbstractSocket::ConnectedState)
{ {

View File

@ -2,7 +2,9 @@
#include <stdexcept> #include <stdexcept>
// project includes // project includes
#include <hyperion/MessageForwarder.h>
#include <protoserver/ProtoServer.h> #include <protoserver/ProtoServer.h>
#include "protoserver/ProtoConnection.h"
#include "ProtoClientConnection.h" #include "ProtoClientConnection.h"
ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) : ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
@ -11,6 +13,20 @@ ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
_server(), _server(),
_openConnections() _openConnections()
{ {
MessageForwarder * forwarder = hyperion->getForwarder();
QStringList slaves = forwarder->getProtoSlaves();
for (int i = 0; i < slaves.size(); ++i) {
if ( QString("127.0.0.1:%1").arg(port) == slaves.at(i) ) {
throw std::runtime_error("Loop between proto server and forwarder detected. Fix your config!");
}
ProtoConnection* p = new ProtoConnection(slaves.at(i).toLocal8Bit().constData());
p->setSkipReply(true);
_proxy_connections << p;
}
if (!_server.listen(QHostAddress::Any, port)) if (!_server.listen(QHostAddress::Any, port))
{ {
throw std::runtime_error("Proto server could not bind to port"); throw std::runtime_error("Proto server could not bind to port");
@ -25,6 +41,9 @@ ProtoServer::~ProtoServer()
foreach (ProtoClientConnection * connection, _openConnections) { foreach (ProtoClientConnection * connection, _openConnections) {
delete connection; delete connection;
} }
while (!_proxy_connections.isEmpty())
delete _proxy_connections.takeFirst();
} }
uint16_t ProtoServer::getPort() const uint16_t ProtoServer::getPort() const
@ -44,9 +63,23 @@ void ProtoServer::newConnection()
// register slot for cleaning up after the connection closed // register slot for cleaning up after the connection closed
connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*))); connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*)));
connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*)));
} }
} }
void ProtoServer::newMessage(const proto::HyperionRequest * message)
{
for (int i = 0; i < _proxy_connections.size(); ++i)
_proxy_connections.at(i)->sendMessage(*message);
}
void ProtoServer::sendImageToProtoSlaves(int priority, const Image<ColorRgb> & image, int duration_ms)
{
for (int i = 0; i < _proxy_connections.size(); ++i)
_proxy_connections.at(i)->setImage(image, priority, duration_ms);
}
void ProtoServer::closedConnection(ProtoClientConnection *connection) void ProtoServer::closedConnection(ProtoClientConnection *connection)
{ {
std::cout << "Proto connection closed" << std::endl; std::cout << "Proto connection closed" << std::endl;

View File

@ -15,4 +15,9 @@ if(ENABLE_PROTOBUF)
if(ENABLE_X11) if(ENABLE_X11)
add_subdirectory(hyperion-x11) add_subdirectory(hyperion-x11)
endif() endif()
if(ENABLE_DISPMANX)
add_subdirectory(hyperion-dispmanx)
endif()
endif() endif()

View File

@ -0,0 +1,38 @@
// Hyperion-AmLogic includes
#include "AmlogicWrapper.h"
AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz) :
_timer(this),
_grabber(grabWidth, grabHeight)
{
_timer.setSingleShot(false);
_timer.setInterval(updateRate_Hz);
// Connect capturing to the timeout signal of the timer
connect(&_timer, SIGNAL(timeout()), this, SLOT(capture()));
}
const Image<ColorRgb> & AmlogicWrapper::getScreenshot()
{
capture();
return _screenshot_rgb;
}
void AmlogicWrapper::start()
{
_timer.start();
}
void AmlogicWrapper::stop()
{
_timer.stop();
}
void AmlogicWrapper::capture()
{
_grabber.grabFrame(_screenshot);
_screenshot.toRgb(_screenshot_rgb);
emit sig_screenshot(_screenshot_rgb);
}

View File

@ -0,0 +1,43 @@
// QT includes
#include <QTimer>
// Hyperion-Dispmanx includes
#include <grabber/AmlogicGrabber.h>
class AmlogicWrapper : public QObject
{
Q_OBJECT
public:
AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz);
const Image<ColorRgb> & getScreenshot();
///
/// Starts the timed capturing of screenshots
///
void start();
void stop();
signals:
void sig_screenshot(const Image<ColorRgb> & screenshot);
private slots:
///
/// Performs a single screenshot capture and publishes the capture screenshot on the screenshot signal.
///
void capture();
private:
/// The QT timer to generate capture-publish events
QTimer _timer;
/// The grabber for creating screenshots
AmlogicGrabber _grabber;
// image buffers
Image<ColorRgb> _screenshot_rgb;
Image<ColorBgr> _screenshot;
};

View File

@ -14,6 +14,7 @@ include_directories(
) )
set(Hyperion_AML_QT_HEADERS set(Hyperion_AML_QT_HEADERS
AmlogicWrapper.h
) )
set(Hyperion_AML_HEADERS set(Hyperion_AML_HEADERS
@ -21,12 +22,13 @@ set(Hyperion_AML_HEADERS
set(Hyperion_AML_SOURCES set(Hyperion_AML_SOURCES
hyperion-aml.cpp hyperion-aml.cpp
AmlogicWrapper.cpp
) )
if(ENABLE_QT5) if(ENABLE_QT5)
QT5_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS}) QT5_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS})
else(ENABLE_QT5) else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS}) QT4_WRAP_CPP(Hyperion_AML_HEADERS_MOC ${Hyperion_AML_QT_HEADERS})
endif(ENABLE_QT5) endif(ENABLE_QT5)
add_executable(hyperion-amlogic add_executable(hyperion-amlogic

View File

@ -7,16 +7,16 @@
// getoptPlusPLus includes // getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h> #include <getoptPlusPlus/getoptpp.h>
#include "../../libsrc/grabber/amlogic/AmlogicGrabber.h" #include <protoserver/ProtoConnectionWrapper.h>
#include "AmlogicWrapper.h"
using namespace vlofgren; using namespace vlofgren;
// save the image as screenshot // save the image as screenshot
void saveScreenshot(const char * filename, const Image<ColorBgr> & image) void saveScreenshot(const char * filename, const Image<ColorRgb> & image)
{ {
// store as PNG // store as PNG
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
pngImage = pngImage.rgbSwapped();
pngImage.save(filename); pngImage.save(filename);
} }
@ -30,16 +30,17 @@ int main(int argc, char ** argv)
OptionsParser optionParser("X11 capture application for Hyperion"); OptionsParser optionParser("X11 capture application for Hyperion");
ParameterSet & parameters = optionParser.getParameters(); ParameterSet & parameters = optionParser.getParameters();
//IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Capture frame rate [default=10]"); IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Capture frame rate [default=10]");
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Width of the captured image [default=128]"); IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Width of the captured image [default=128]");
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Height of the captured image [default=128]"); IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Height of the captured image [default=128]");
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]");
//SwitchParameter<> & argSkipReply = parameters.add<SwitchParameter<>> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); SwitchParameter<> & argSkipReply = parameters.add<SwitchParameter<>> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion");
SwitchParameter<> & argHelp = parameters.add<SwitchParameter<>> ('h', "help", "Show this help message and exit"); SwitchParameter<> & argHelp = parameters.add<SwitchParameter<>> ('h', "help", "Show this help message and exit");
// set defaults // set defaults
argFps.setDefault(10);
argWidth.setDefault(160); argWidth.setDefault(160);
argHeight.setDefault(160); argHeight.setDefault(160);
argAddress.setDefault("127.0.0.1:19445"); argAddress.setDefault("127.0.0.1:19445");
@ -57,29 +58,36 @@ int main(int argc, char ** argv)
int width = argWidth.getValue(); int width = argWidth.getValue();
int height = argHeight.getValue(); int height = argHeight.getValue();
if (width < 160 || height < 60) if (width < 160 || height < 160)
{ {
std::cout << "Minimum width and height is 160" << std::endl; std::cout << "Minimum width and height is 160" << std::endl;
width = std::max(160, width); width = std::max(160, width);
height = std::max(160, height); height = std::max(160, height);
} }
int grabInterval = 1000 / argFps.getValue();
AmlogicWrapper amlWrapper(argWidth.getValue(),argHeight.getValue(),grabInterval);
if (argScreenshot.isSet()) if (argScreenshot.isSet())
{ {
// Create the grabber
AmlogicGrabber amlGrabber(width, height);
// Capture a single screenshot and finish // Capture a single screenshot and finish
Image<ColorBgr> screenshot; const Image<ColorRgb> & screenshot = amlWrapper.getScreenshot();
amlGrabber.grabFrame(screenshot);
saveScreenshot("screenshot.png", screenshot); saveScreenshot("screenshot.png", screenshot);
} }
else else
{ {
// TODO[TvdZ]: Implement the proto-client mechanisme // Create the Proto-connection with hyperiond
std::cerr << "The PROTO-interface has not been implemented yet" << std::endl; ProtoConnectionWrapper protoWrapper(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet());
}
// Connect the screen capturing to the proto processing
QObject::connect(&amlWrapper, SIGNAL(sig_screenshot(const Image<ColorRgb> &)), &protoWrapper, SLOT(receiveImage(Image<ColorRgb>)));
// Start the capturing
amlWrapper.start();
// Start the application
app.exec();
}
} }
catch (const std::runtime_error & e) catch (const std::runtime_error & e)
{ {

View File

@ -0,0 +1,65 @@
# Configure minimum CMAKE version
cmake_minimum_required(VERSION 2.8)
# Set the project name
project(hyperion-dispmanx)
if(ENABLE_QT5)
find_package(Qt5Widgets REQUIRED)
else(ENABLE_QT5)
# find Qt4
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork )
endif(ENABLE_QT5)
# Find the BCM-package (VC control)
find_package(BCM REQUIRED)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver
${QT_INCLUDES}
${BCM_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIRS}
)
set(Hyperion_Dispmanx_QT_HEADERS
DispmanxWrapper.h)
set(Hyperion_Dispmanx_HEADERS
)
set(Hyperion_Dispmanx_SOURCES
hyperion-dispmanx.cpp
DispmanxWrapper.cpp
)
if(ENABLE_QT5)
QT5_WRAP_CPP(Hyperion_Dispmanx_HEADERS_MOC ${Hyperion_Dispmanx_QT_HEADERS})
else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_Dispmanx_HEADERS_MOC ${Hyperion_Dispmanx_QT_HEADERS})
endif(ENABLE_QT5)
add_executable( ${PROJECT_NAME}
${Hyperion_Dispmanx_HEADERS}
${Hyperion_Dispmanx_SOURCES}
${Hyperion_Dispmanx_HEADERS_MOC}
)
target_link_libraries( ${PROJECT_NAME}
getoptPlusPlus
blackborder
hyperion-utils
protoserver
dispmanx-grabber
${Dispmanx_LIBRARIES}
pthread
)
if(ENABLE_QT5)
qt5_use_modules(hyperion-dispmanx Widgets Core Gui Network)
else(ENABLE_QT5)
qt4_use_modules(hyperion-dispmanx
Core
Gui
Network
)
endif(ENABLE_QT5)

View File

@ -0,0 +1,38 @@
// Hyperion-Dispmanx includes
#include "DispmanxWrapper.h"
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz) :
_timer(this),
_grabber(grabWidth, grabHeight)
{
_timer.setSingleShot(false);
_timer.setInterval(updateRate_Hz);
// Connect capturing to the timeout signal of the timer
connect(&_timer, SIGNAL(timeout()), this, SLOT(capture()));
}
const Image<ColorRgb> & DispmanxWrapper::getScreenshot()
{
capture();
return _screenshot_rgb;
}
void DispmanxWrapper::start()
{
_timer.start();
}
void DispmanxWrapper::stop()
{
_timer.stop();
}
void DispmanxWrapper::capture()
{
_grabber.grabFrame(_screenshot);
_screenshot.toRgb(_screenshot_rgb);
emit sig_screenshot(_screenshot_rgb);
}

View File

@ -0,0 +1,42 @@
// QT includes
#include <QTimer>
// Hyperion-Dispmanx includes
#include <grabber/DispmanxFrameGrabber.h>
class DispmanxWrapper : public QObject
{
Q_OBJECT
public:
DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz);
const Image<ColorRgb> & getScreenshot();
///
/// Starts the timed capturing of screenshots
///
void start();
void stop();
signals:
void sig_screenshot(const Image<ColorRgb> & screenshot);
private slots:
///
/// Performs a single screenshot capture and publishes the capture screenshot on the screenshot
/// signal.
///
void capture();
private:
/// The QT timer to generate capture-publish events
QTimer _timer;
/// The grabber for creating screenshots
DispmanxFrameGrabber _grabber;
Image<ColorRgb> _screenshot_rgb;
Image<ColorRgba> _screenshot;
};

View File

@ -0,0 +1,92 @@
// QT includes
#include <QCoreApplication>
#include <QImage>
// getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h>
#include <protoserver/ProtoConnectionWrapper.h>
#include "DispmanxWrapper.h"
using namespace vlofgren;
// save the image as screenshot
void saveScreenshot(const char * filename, const Image<ColorRgb> & image)
{
// store as PNG
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
pngImage.save(filename);
}
int main(int argc, char ** argv)
{
QCoreApplication app(argc, argv);
try
{
// create the option parser and initialize all parameters
OptionsParser optionParser("Dispmanx capture application for Hyperion");
ParameterSet & parameters = optionParser.getParameters();
IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Capture frame rate [default=10]");
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "The width of the grabbed frames [pixels]");
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "The height of the grabbed frames");
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]");
SwitchParameter<> & argSkipReply = parameters.add<SwitchParameter<>> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion");
SwitchParameter<> & argHelp = parameters.add<SwitchParameter<>> ('h', "help", "Show this help message and exit");
// set defaults
argFps.setDefault(10);
argWidth.setDefault(64);
argHeight.setDefault(64);
argAddress.setDefault("127.0.0.1:19445");
argPriority.setDefault(800);
// parse all options
optionParser.parse(argc, const_cast<const char **>(argv));
// check if we need to display the usage. exit if we do.
if (argHelp.isSet())
{
optionParser.usage();
return 0;
}
// Create the dispmanx grabbing stuff
int grabInterval = 1000 / argFps.getValue();
DispmanxWrapper dispmanxWrapper(argWidth.getValue(),argHeight.getValue(),grabInterval);
if (argScreenshot.isSet())
{
// Capture a single screenshot and finish
const Image<ColorRgb> & screenshot = dispmanxWrapper.getScreenshot();
saveScreenshot("screenshot.png", screenshot);
}
else
{
// Create the Proto-connection with hyperiond
ProtoConnectionWrapper protoWrapper(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet());
// Connect the screen capturing to the proto processing
QObject::connect(&dispmanxWrapper, SIGNAL(sig_screenshot(const Image<ColorRgb> &)), &protoWrapper, SLOT(receiveImage(Image<ColorRgb>)));
// Start the capturing
dispmanxWrapper.start();
// Start the application
app.exec();
}
}
catch (const std::runtime_error & e)
{
// An error occured. Display error and quit
std::cerr << e.what() << std::endl;
return -1;
}
return 0;
}

View File

@ -1,6 +1,7 @@
// C++ includes // C++ includes
#include <cassert> #include <cassert>
#include <csignal> #include <csignal>
#include <vector>
// QT includes // QT includes
#include <QCoreApplication> #include <QCoreApplication>
@ -188,6 +189,39 @@ int main(int argc, char** argv)
std::cout << "XBMC video checker created and started" << std::endl; std::cout << "XBMC video checker created and started" << std::endl;
} }
// ---- network services -----
// Create Json server if configuration is present
JsonServer * jsonServer = nullptr;
if (config.isMember("jsonServer"))
{
const Json::Value & jsonServerConfig = config["jsonServer"];
jsonServer = new JsonServer(&hyperion, jsonServerConfig["port"].asUInt());
std::cout << "Json server created and started on port " << jsonServer->getPort() << std::endl;
}
#ifdef ENABLE_PROTOBUF
// Create Proto server if configuration is present
ProtoServer * protoServer = nullptr;
if (config.isMember("protoServer"))
{
const Json::Value & protoServerConfig = config["protoServer"];
protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt() );
std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl;
}
#endif
// Create Boblight server if configuration is present
BoblightServer * boblightServer = nullptr;
if (config.isMember("boblightServer"))
{
const Json::Value & boblightServerConfig = config["boblightServer"];
boblightServer = new BoblightServer(&hyperion, boblightServerConfig["port"].asUInt());
std::cout << "Boblight server created and started on port " << boblightServer->getPort() << std::endl;
}
// ---- grabber -----
#ifdef ENABLE_DISPMANX #ifdef ENABLE_DISPMANX
// Construct and start the frame-grabber if the configuration is present // Construct and start the frame-grabber if the configuration is present
DispmanxWrapper * dispmanx = nullptr; DispmanxWrapper * dispmanx = nullptr;
@ -206,6 +240,10 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), dispmanx, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), dispmanx, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(dispmanx, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
dispmanx->start(); dispmanx->start();
std::cout << "Frame grabber created and started" << std::endl; std::cout << "Frame grabber created and started" << std::endl;
} }
@ -213,7 +251,7 @@ int main(int argc, char** argv)
#if !defined(ENABLE_OSX) && !defined(ENABLE_FB) #if !defined(ENABLE_OSX) && !defined(ENABLE_FB)
if (config.isMember("framegrabber")) if (config.isMember("framegrabber"))
{ {
std::cerr << "The dispmanx framegrabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The dispmanx framegrabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#endif #endif
@ -245,14 +283,19 @@ int main(int argc, char** argv)
grabberConfig.get("cropTop", 0).asInt(), grabberConfig.get("cropTop", 0).asInt(),
grabberConfig.get("cropBottom", 0).asInt()); grabberConfig.get("cropBottom", 0).asInt());
#ifdef ENABLE_PROTOBUF
QObject::connect(v4l2Grabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
v4l2Grabber->start(); v4l2Grabber->start();
std::cout << "V4l2 grabber created and started" << std::endl; std::cout << "V4l2 grabber created and started" << std::endl;
} }
#else #else
if (config.isMember("grabber-v4l2")) if (config.isMember("grabber-v4l2"))
{ {
std::cerr << "The v4l2 grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The v4l2 grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#ifdef ENABLE_AMLOGIC #ifdef ENABLE_AMLOGIC
@ -273,6 +316,10 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), amlGrabber, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), amlGrabber, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(amlGrabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
amlGrabber->start(); amlGrabber->start();
std::cout << "AMLOGIC grabber created and started" << std::endl; std::cout << "AMLOGIC grabber created and started" << std::endl;
} }
@ -302,18 +349,22 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), fbGrabber, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), fbGrabber, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(fbGrabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
fbGrabber->start(); fbGrabber->start();
std::cout << "Framebuffer grabber created and started" << std::endl; std::cout << "Framebuffer grabber created and started" << std::endl;
} }
#else #else
if (config.isMember("framebuffergrabber")) if (config.isMember("framebuffergrabber"))
{ {
std::cerr << "The framebuffer grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The framebuffer grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) #if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX)
else if (config.isMember("framegrabber")) else if (config.isMember("framegrabber"))
{ {
std::cerr << "The framebuffer grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The framebuffer grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#endif #endif
@ -337,50 +388,26 @@ int main(int argc, char** argv)
QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), osxGrabber, SLOT(setVideoMode(VideoMode))); QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), osxGrabber, SLOT(setVideoMode(VideoMode)));
} }
#ifdef ENABLE_PROTOBUF
QObject::connect(osxGrabber, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), protoServer, SLOT(sendImageToProtoSlaves(int, const Image<ColorRgb>&, const int)) );
#endif
osxGrabber->start(); osxGrabber->start();
std::cout << "OSX grabber created and started" << std::endl; std::cout << "OSX grabber created and started" << std::endl;
} }
#else #else
if (config.isMember("osxgrabber")) if (config.isMember("osxgrabber"))
{ {
std::cerr << "The osx grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The osx grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_FB) #if !defined(ENABLE_DISPMANX) && !defined(ENABLE_FB)
else if (config.isMember("framegrabber")) else if (config.isMember("framegrabber"))
{ {
std::cerr << "The osx grabber can not be instantiated, becuse it has been left out from the build" << std::endl; std::cerr << "The osx grabber can not be instantiated, because it has been left out from the build" << std::endl;
} }
#endif #endif
#endif #endif
// Create Json server if configuration is present
JsonServer * jsonServer = nullptr;
if (config.isMember("jsonServer"))
{
const Json::Value & jsonServerConfig = config["jsonServer"];
jsonServer = new JsonServer(&hyperion, jsonServerConfig["port"].asUInt());
std::cout << "Json server created and started on port " << jsonServer->getPort() << std::endl;
}
#ifdef ENABLE_PROTOBUF
// Create Proto server if configuration is present
ProtoServer * protoServer = nullptr;
if (config.isMember("protoServer"))
{
const Json::Value & protoServerConfig = config["protoServer"];
protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt());
std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl;
}
#endif
// Create Boblight server if configuration is present
BoblightServer * boblightServer = nullptr;
if (config.isMember("boblightServer"))
{
const Json::Value & boblightServerConfig = config["boblightServer"];
boblightServer = new BoblightServer(&hyperion, boblightServerConfig["port"].asUInt());
std::cout << "Boblight server created and started on port " << boblightServer->getPort() << std::endl;
}
// run the application // run the application
int rc = app.exec(); int rc = app.exec();

View File

@ -4,22 +4,42 @@
// Utils includes // Utils includes
#include <utils/Image.h> #include <utils/Image.h>
#include <utils/ColorRgba.h>
#include <utils/ColorRgb.h> #include <utils/ColorRgb.h>
#include <utils/ColorBgr.h>
#include <hyperion/ImageProcessor.h>
int main() int main()
{ {
std::cout << "Constructing image" << std::endl; std::cout << "Constructing image" << std::endl;
Image<ColorRgb> image(64, 64, ColorRgb::BLACK); int width = 64;
int height = 64;
Image<ColorRgb> image_rgb(width, height, ColorRgb::BLACK);
Image<ColorBgr> image_bgr(image_rgb.width(), image_rgb.height(), ColorBgr::BLACK);
std::cout << "Writing image" << std::endl; std::cout << "Writing image" << std::endl;
for (unsigned y=0; y<64; ++y) unsigned l = width * height;
// BGR
for (unsigned i=0; i<l; ++i)
image_bgr.memptr()[i] = ColorBgr{0,128,255};
// to RGB
image_bgr.toRgb(image_rgb);
// test
for (unsigned i=0; i<l; ++i)
{ {
for (unsigned x=0; x<64; ++x) const ColorRgb rgb = image_rgb.memptr()[i];
{ if ( rgb.red != 255 || rgb.green != 128 || rgb.blue != 0 )
image(x,y) = ColorRgb::RED; std::cout << "RGB error idx " << i << " " << rgb << std::endl;
}
} }
std::cout << "Finished (destruction will be performed)" << std::endl; std::cout << "Finished (destruction will be performed)" << std::endl;
return 0; return 0;

View File

@ -10,7 +10,7 @@
#include <getoptPlusPlus/getoptpp.h> #include <getoptPlusPlus/getoptpp.h>
// Dispmanx grabber includes // Dispmanx grabber includes
#include <grabber/dispmanx/DispmanxFrameGrabber.h> #include <grabber/DispmanxFrameGrabber.h>
using namespace vlofgren; using namespace vlofgren;