mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Merge branch 'master' of https://github.com/tvdzwan/hyperion.git
Former-commit-id: e21a530324abb6af92f4b34abdfbf23313775f4a
This commit is contained in:
commit
b4c49c9ec4
@ -13,6 +13,9 @@ option (ENABLE_SPIDEV "Enable the SPIDEV device" ON)
|
|||||||
message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX})
|
message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX})
|
||||||
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
|
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
|
||||||
|
|
||||||
|
option (ENABLE_V4L2 "Enable the V4L2 grabber" ON)
|
||||||
|
message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
|
||||||
|
|
||||||
# Createt the configuration file
|
# Createt the configuration file
|
||||||
# configure a header file to pass some of the CMake settings
|
# configure a header file to pass some of the CMake settings
|
||||||
# to the source code
|
# to the source code
|
||||||
|
@ -3,5 +3,8 @@
|
|||||||
// Define to enable the dispmanx grabber
|
// Define to enable the dispmanx grabber
|
||||||
#cmakedefine ENABLE_DISPMANX
|
#cmakedefine ENABLE_DISPMANX
|
||||||
|
|
||||||
|
// Define to enable the v4l2 grabber
|
||||||
|
#cmakedefine ENABLE_V4L2
|
||||||
|
|
||||||
// Define to enable the spi-device
|
// Define to enable the spi-device
|
||||||
#cmakedefine ENABLE_SPIDEV
|
#cmakedefine ENABLE_SPIDEV
|
||||||
|
@ -22,6 +22,7 @@ tar --create --verbose --gzip --absolute-names --show-transformed-names \
|
|||||||
--transform "s://:/:g" \
|
--transform "s://:/:g" \
|
||||||
"$builddir/bin/hyperiond" \
|
"$builddir/bin/hyperiond" \
|
||||||
"$builddir/bin/hyperion-remote" \
|
"$builddir/bin/hyperion-remote" \
|
||||||
|
"$builddir/bin/hyperion-v4l2" \
|
||||||
"$builddir/bin/gpio2spi" \
|
"$builddir/bin/gpio2spi" \
|
||||||
"$builddir/bin/dispmanx2png" \
|
"$builddir/bin/dispmanx2png" \
|
||||||
"$repodir/effects/"* \
|
"$repodir/effects/"* \
|
||||||
|
@ -47,6 +47,7 @@ fi
|
|||||||
if [ $IS_OPENELEC -ne 1 ]; then
|
if [ $IS_OPENELEC -ne 1 ]; then
|
||||||
ln -fs /opt/hyperion/bin/hyperiond /usr/bin/hyperiond
|
ln -fs /opt/hyperion/bin/hyperiond /usr/bin/hyperiond
|
||||||
ln -fs /opt/hyperion/bin/hyperion-remote /usr/bin/hyperion-remote
|
ln -fs /opt/hyperion/bin/hyperion-remote /usr/bin/hyperion-remote
|
||||||
|
ln -fs /opt/hyperion/bin/hyperion-v4l2 /usr/bin/hyperion-v4l2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# create link to the gpio changer (gpio->spi)
|
# create link to the gpio changer (gpio->spi)
|
||||||
@ -63,7 +64,6 @@ else
|
|||||||
ln -s /opt/hyperion/config/hyperion.config.json /etc/hyperion.config.json
|
ln -s /opt/hyperion/config/hyperion.config.json /etc/hyperion.config.json
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Copy the service control configuration to /etc/int
|
# Copy the service control configuration to /etc/int
|
||||||
if [ $USE_INITCTL -eq 1 ]; then
|
if [ $USE_INITCTL -eq 1 ]; then
|
||||||
echo 'Installing initctl script'
|
echo 'Installing initctl script'
|
||||||
|
@ -56,21 +56,21 @@
|
|||||||
"red" :
|
"red" :
|
||||||
{
|
{
|
||||||
"threshold" : 0.0000,
|
"threshold" : 0.0000,
|
||||||
"gamma" : 2.0000,
|
"gamma" : 1.0000,
|
||||||
"blacklevel" : 0.0000,
|
"blacklevel" : 0.0000,
|
||||||
"whitelevel" : 1.0000
|
"whitelevel" : 1.0000
|
||||||
},
|
},
|
||||||
"green" :
|
"green" :
|
||||||
{
|
{
|
||||||
"threshold" : 0.0000,
|
"threshold" : 0.0000,
|
||||||
"gamma" : 2.0000,
|
"gamma" : 1.0000,
|
||||||
"blacklevel" : 0.0000,
|
"blacklevel" : 0.0000,
|
||||||
"whitelevel" : 1.0000
|
"whitelevel" : 1.0000
|
||||||
},
|
},
|
||||||
"blue" :
|
"blue" :
|
||||||
{
|
{
|
||||||
"threshold" : 0.0000,
|
"threshold" : 0.0000,
|
||||||
"gamma" : 2.0000,
|
"gamma" : 1.0000,
|
||||||
"blacklevel" : 0.0000,
|
"blacklevel" : 0.0000,
|
||||||
"whitelevel" : 1.0000
|
"whitelevel" : 1.0000
|
||||||
}
|
}
|
||||||
@ -348,10 +348,12 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
/// The black border configuration, contains the following items:
|
/// The black border configuration, contains the following items:
|
||||||
/// * enable : true if the detector should be activated
|
/// * 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)
|
||||||
"blackborderdetector" :
|
"blackborderdetector" :
|
||||||
{
|
{
|
||||||
"enable" : true
|
"enable" : true,
|
||||||
|
"threshold" : 0.01
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The configuration of the effect engine, contains the following items:
|
/// The configuration of the effect engine, contains the following items:
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
"device" :
|
"device" :
|
||||||
{
|
{
|
||||||
"name" : "MyPi",
|
"name" : "MyPi",
|
||||||
"type" : "test",
|
"type" : "adalight",
|
||||||
"output" : "~/hyperion.test.out",
|
"output" : "/dev/ttyUSB0",
|
||||||
"rate" : 250000,
|
"rate" : 115200,
|
||||||
"colorOrder" : "rgb"
|
"colorOrder" : "rgb"
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -348,10 +348,12 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
/// The black border configuration, contains the following items:
|
/// The black border configuration, contains the following items:
|
||||||
/// * enable : true if the detector should be activated
|
/// * 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)
|
||||||
"blackborderdetector" :
|
"blackborderdetector" :
|
||||||
{
|
{
|
||||||
"enable" : true
|
"enable" : true,
|
||||||
|
"threshold" : 0.01
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The configuration of the effect engine, contains the following items:
|
/// The configuration of the effect engine, contains the following items:
|
||||||
@ -371,6 +373,38 @@
|
|||||||
"duration_ms" : 3000
|
"duration_ms" : 3000
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// 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
|
/// The configuration of the Json server which enables the json remote interface
|
||||||
/// * port : Port at which the json server is started
|
/// * port : Port at which the json server is started
|
||||||
"jsonServer" :
|
"jsonServer" :
|
||||||
|
@ -120,7 +120,7 @@ void OptionsParser::usage() const {
|
|||||||
for(i = parameters.parameters.begin();
|
for(i = parameters.parameters.begin();
|
||||||
i != parameters.parameters.end(); i++)
|
i != parameters.parameters.end(); i++)
|
||||||
{
|
{
|
||||||
cerr.width(30);
|
cerr.width(33);
|
||||||
cerr << std::left << " " + (*i)->usageLine();
|
cerr << std::left << " " + (*i)->usageLine();
|
||||||
|
|
||||||
cerr.width(40);
|
cerr.width(40);
|
||||||
|
@ -1 +1 @@
|
|||||||
4d50c38a61c9f32a15b29ef3b3953c2835fa9cac
|
5e8ca7ba33eb38d828b50971ec94b045025caa78
|
@ -1 +1 @@
|
|||||||
96b8f6c7e0241930c944a7358af473dabecf1dae
|
08d42deff1de4c4296e4c6e22c783a0096ed3396
|
@ -48,8 +48,9 @@ namespace hyperion
|
|||||||
public:
|
public:
|
||||||
///
|
///
|
||||||
/// Constructs a black-border detector
|
/// Constructs a black-border detector
|
||||||
|
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use
|
||||||
///
|
///
|
||||||
BlackBorderDetector();
|
BlackBorderDetector(uint8_t blackborderThreshold);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Performs the actual black-border detection on the given image
|
/// Performs the actual black-border detection on the given image
|
||||||
@ -125,7 +126,11 @@ namespace hyperion
|
|||||||
inline bool isBlack(const Pixel_T & color)
|
inline bool isBlack(const Pixel_T & color)
|
||||||
{
|
{
|
||||||
// Return the simple compare of the color against black
|
// Return the simple compare of the color against black
|
||||||
return color.red < 3 && color.green < 3 && color.green < 3;
|
return color.red < _blackborderThreshold && color.green < _blackborderThreshold && color.green < _blackborderThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Threshold for the blackborder detector [0 .. 255]
|
||||||
|
const uint8_t _blackborderThreshold;
|
||||||
};
|
};
|
||||||
} // end namespace hyperion
|
} // end namespace hyperion
|
@ -21,11 +21,13 @@ namespace hyperion
|
|||||||
/// horizontal border becomes the current border
|
/// horizontal border becomes the current border
|
||||||
/// @param blurRemoveCnt The size to add to a horizontal or vertical border (because the
|
/// @param blurRemoveCnt The size to add to a horizontal or vertical border (because the
|
||||||
/// outer pixels is blurred (black and color combined due to image scaling))
|
/// outer pixels is blurred (black and color combined due to image scaling))
|
||||||
|
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use
|
||||||
///
|
///
|
||||||
BlackBorderProcessor(
|
BlackBorderProcessor(
|
||||||
const unsigned unknownFrameCnt,
|
const unsigned unknownFrameCnt,
|
||||||
const unsigned borderFrameCnt,
|
const unsigned borderFrameCnt,
|
||||||
const unsigned blurRemoveCnt);
|
const unsigned blurRemoveCnt,
|
||||||
|
uint8_t blackborderThreshold);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Return the current (detected) border
|
/// Return the current (detected) border
|
@ -8,7 +8,9 @@
|
|||||||
#include <hyperion/ImageProcessorFactory.h>
|
#include <hyperion/ImageProcessorFactory.h>
|
||||||
#include <hyperion/LedString.h>
|
#include <hyperion/LedString.h>
|
||||||
#include <hyperion/ImageToLedsMap.h>
|
#include <hyperion/ImageToLedsMap.h>
|
||||||
#include <hyperion/BlackBorderProcessor.h>
|
|
||||||
|
// Black border includes
|
||||||
|
#include <blackborder/BlackBorderProcessor.h>
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The ImageProcessor translates an RGB-image to RGB-values for the leds. The processing is
|
/// The ImageProcessor translates an RGB-image to RGB-values for the leds. The processing is
|
||||||
@ -53,7 +55,7 @@ public:
|
|||||||
verifyBorder(image);
|
verifyBorder(image);
|
||||||
|
|
||||||
// Create a result vector and call the 'in place' functionl
|
// Create a result vector and call the 'in place' functionl
|
||||||
std::vector<ColorRgb> colors = mImageToLeds->getMeanLedColor(image);
|
std::vector<ColorRgb> colors = _imageToLeds->getMeanLedColor(image);
|
||||||
|
|
||||||
// return the computed colors
|
// return the computed colors
|
||||||
return colors;
|
return colors;
|
||||||
@ -75,7 +77,7 @@ public:
|
|||||||
verifyBorder(image);
|
verifyBorder(image);
|
||||||
|
|
||||||
// Determine the mean-colors of each led (using the existing mapping)
|
// Determine the mean-colors of each led (using the existing mapping)
|
||||||
mImageToLeds->getMeanLedColor(image, ledColors);
|
_imageToLeds->getMeanLedColor(image, ledColors);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -98,8 +100,10 @@ private:
|
|||||||
/// given led-string specification
|
/// given led-string specification
|
||||||
///
|
///
|
||||||
/// @param[in] ledString The led-string specification
|
/// @param[in] ledString The led-string specification
|
||||||
|
/// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled
|
||||||
|
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use
|
||||||
///
|
///
|
||||||
ImageProcessor(const LedString &ledString, bool enableBlackBorderDetector);
|
ImageProcessor(const LedString &ledString, bool enableBlackBorderDetector, uint8_t blackborderThreshold);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Performs black-border detection (if enabled) on the given image
|
/// Performs black-border detection (if enabled) on the given image
|
||||||
@ -116,17 +120,17 @@ private:
|
|||||||
const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder();
|
const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder();
|
||||||
|
|
||||||
// Clean up the old mapping
|
// Clean up the old mapping
|
||||||
delete mImageToLeds;
|
delete _imageToLeds;
|
||||||
|
|
||||||
if (border.unknown)
|
if (border.unknown)
|
||||||
{
|
{
|
||||||
// Construct a new buffer and mapping
|
// Construct a new buffer and mapping
|
||||||
mImageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, mLedString.leds());
|
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Construct a new buffer and mapping
|
// Construct a new buffer and mapping
|
||||||
mImageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), border.horizontalSize, border.verticalSize, mLedString.leds());
|
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), border.horizontalSize, border.verticalSize, _ledString.leds());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "CURRENT BORDER TYPE: unknown=" << border.unknown << " hor.size=" << border.horizontalSize << " vert.size=" << border.verticalSize << std::endl;
|
std::cout << "CURRENT BORDER TYPE: unknown=" << border.unknown << " hor.size=" << border.horizontalSize << " vert.size=" << border.verticalSize << std::endl;
|
||||||
@ -135,14 +139,14 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// The Led-string specification
|
/// The Led-string specification
|
||||||
const LedString mLedString;
|
const LedString _ledString;
|
||||||
|
|
||||||
/// Flag the enables(true)/disabled(false) blackborder detector
|
/// Flag the enables(true)/disabled(false) blackborder detector
|
||||||
bool _enableBlackBorderRemoval;
|
const bool _enableBlackBorderRemoval;
|
||||||
|
|
||||||
/// The processor for black border detection
|
/// The processor for black border detection
|
||||||
hyperion::BlackBorderProcessor * _borderProcessor;
|
hyperion::BlackBorderProcessor * _borderProcessor;
|
||||||
|
|
||||||
/// The mapping of image-pixels to leds
|
/// The mapping of image-pixels to leds
|
||||||
hyperion::ImageToLedsMap* mImageToLeds;
|
hyperion::ImageToLedsMap* _imageToLeds;
|
||||||
};
|
};
|
||||||
|
@ -30,8 +30,10 @@ public:
|
|||||||
/// Initialises this factory with the given led-configuration
|
/// Initialises this factory with the given led-configuration
|
||||||
///
|
///
|
||||||
/// @param[in] ledString The led configuration
|
/// @param[in] ledString The led configuration
|
||||||
|
/// @param[in] enableBlackBorderDetector Flag indicating if the blacborder detector should be enabled
|
||||||
|
/// @param[in] blackborderThreshold The threshold which the blackborder detector should use
|
||||||
///
|
///
|
||||||
void init(const LedString& ledString, bool enableBlackBorderDetector);
|
void init(const LedString& ledString, bool enableBlackBorderDetector, double blackborderThreshold);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Creates a new ImageProcessor. The onwership of the processor is transferred to the caller.
|
/// Creates a new ImageProcessor. The onwership of the processor is transferred to the caller.
|
||||||
@ -46,4 +48,7 @@ private:
|
|||||||
|
|
||||||
/// Flag indicating if the black border detector should be used
|
/// Flag indicating if the black border detector should be used
|
||||||
bool _enableBlackBorderDetector;
|
bool _enableBlackBorderDetector;
|
||||||
|
|
||||||
|
/// Threshold for the blackborder detector [0 .. 255]
|
||||||
|
uint8_t _blackborderThreshold;
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include)
|
|||||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc)
|
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc)
|
||||||
|
|
||||||
add_subdirectory(hyperion)
|
add_subdirectory(hyperion)
|
||||||
|
add_subdirectory(blackborder)
|
||||||
add_subdirectory(jsonserver)
|
add_subdirectory(jsonserver)
|
||||||
add_subdirectory(protoserver)
|
add_subdirectory(protoserver)
|
||||||
add_subdirectory(boblightserver)
|
add_subdirectory(boblightserver)
|
||||||
|
11
libsrc/blackborder/BlackBorderDetector.cpp
Normal file
11
libsrc/blackborder/BlackBorderDetector.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
// BlackBorders includes
|
||||||
|
#include <blackborder/BlackBorderDetector.h>
|
||||||
|
|
||||||
|
using namespace hyperion;
|
||||||
|
|
||||||
|
BlackBorderDetector::BlackBorderDetector(uint8_t blackborderThreshold) :
|
||||||
|
_blackborderThreshold(blackborderThreshold)
|
||||||
|
{
|
||||||
|
// empty
|
||||||
|
}
|
@ -1,17 +1,17 @@
|
|||||||
|
|
||||||
// Local-Hyperion includes
|
// Blackborder includes
|
||||||
#include <hyperion/BlackBorderProcessor.h>
|
#include <blackborder/BlackBorderProcessor.h>
|
||||||
|
|
||||||
using namespace hyperion;
|
using namespace hyperion;
|
||||||
|
|
||||||
BlackBorderProcessor::BlackBorderProcessor(
|
BlackBorderProcessor::BlackBorderProcessor(const unsigned unknownFrameCnt,
|
||||||
const unsigned unknownFrameCnt,
|
|
||||||
const unsigned borderFrameCnt,
|
const unsigned borderFrameCnt,
|
||||||
const unsigned blurRemoveCnt) :
|
const unsigned blurRemoveCnt,
|
||||||
|
uint8_t blackborderThreshold) :
|
||||||
_unknownSwitchCnt(unknownFrameCnt),
|
_unknownSwitchCnt(unknownFrameCnt),
|
||||||
_borderSwitchCnt(borderFrameCnt),
|
_borderSwitchCnt(borderFrameCnt),
|
||||||
_blurRemoveCnt(blurRemoveCnt),
|
_blurRemoveCnt(blurRemoveCnt),
|
||||||
_detector(),
|
_detector(blackborderThreshold),
|
||||||
_currentBorder({true, -1, -1}),
|
_currentBorder({true, -1, -1}),
|
||||||
_previousDetectedBorder({true, -1, -1}),
|
_previousDetectedBorder({true, -1, -1}),
|
||||||
_consistentCnt(0)
|
_consistentCnt(0)
|
23
libsrc/blackborder/CMakeLists.txt
Normal file
23
libsrc/blackborder/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
# Define the current source locations
|
||||||
|
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/blackborder)
|
||||||
|
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/blackborder)
|
||||||
|
|
||||||
|
SET(Blackborder_HEADERS
|
||||||
|
${CURRENT_HEADER_DIR}/BlackBorderDetector.h
|
||||||
|
${CURRENT_HEADER_DIR}/BlackBorderProcessor.h
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(Blackborder_SOURCES
|
||||||
|
${CURRENT_SOURCE_DIR}/BlackBorderDetector.cpp
|
||||||
|
${CURRENT_SOURCE_DIR}/BlackBorderProcessor.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(blackborder
|
||||||
|
${Blackborder_HEADERS}
|
||||||
|
${Blackborder_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(blackborder
|
||||||
|
hyperion-utils
|
||||||
|
)
|
@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
// Local-Hyperion includes
|
|
||||||
#include <hyperion/BlackBorderDetector.h>
|
|
||||||
|
|
||||||
using namespace hyperion;
|
|
||||||
|
|
||||||
BlackBorderDetector::BlackBorderDetector()
|
|
||||||
{
|
|
||||||
// empty
|
|
||||||
}
|
|
@ -17,9 +17,6 @@ SET(Hyperion_HEADERS
|
|||||||
${CURRENT_HEADER_DIR}/LedString.h
|
${CURRENT_HEADER_DIR}/LedString.h
|
||||||
${CURRENT_HEADER_DIR}/PriorityMuxer.h
|
${CURRENT_HEADER_DIR}/PriorityMuxer.h
|
||||||
|
|
||||||
${CURRENT_HEADER_DIR}/BlackBorderDetector.h
|
|
||||||
${CURRENT_HEADER_DIR}/BlackBorderProcessor.h
|
|
||||||
|
|
||||||
${CURRENT_SOURCE_DIR}/MultiColorTransform.h
|
${CURRENT_SOURCE_DIR}/MultiColorTransform.h
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,8 +27,6 @@ SET(Hyperion_SOURCES
|
|||||||
${CURRENT_SOURCE_DIR}/LedString.cpp
|
${CURRENT_SOURCE_DIR}/LedString.cpp
|
||||||
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp
|
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp
|
||||||
|
|
||||||
${CURRENT_SOURCE_DIR}/BlackBorderDetector.cpp
|
|
||||||
${CURRENT_SOURCE_DIR}/BlackBorderProcessor.cpp
|
|
||||||
${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
|
||||||
@ -54,6 +49,7 @@ add_library(hyperion
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(hyperion
|
target_link_libraries(hyperion
|
||||||
|
blackborder
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
leddevice
|
leddevice
|
||||||
effectengine
|
effectengine
|
||||||
|
@ -270,7 +270,10 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) :
|
|||||||
throw std::runtime_error("Color transformation incorrectly set");
|
throw std::runtime_error("Color transformation incorrectly set");
|
||||||
}
|
}
|
||||||
// initialize the image processor factory
|
// initialize the image processor factory
|
||||||
ImageProcessorFactory::getInstance().init(_ledString, jsonConfig["blackborderdetector"].get("enable", true).asBool());
|
ImageProcessorFactory::getInstance().init(
|
||||||
|
_ledString,
|
||||||
|
jsonConfig["blackborderdetector"].get("enable", true).asBool(),
|
||||||
|
jsonConfig["blackborderdetector"].get("threshold", 0.01).asDouble());
|
||||||
|
|
||||||
// initialize the color smoothing filter
|
// initialize the color smoothing filter
|
||||||
_device = createColorSmoothing(jsonConfig["color"]["smoothing"], _device);
|
_device = createColorSmoothing(jsonConfig["color"]["smoothing"], _device);
|
||||||
|
@ -2,50 +2,52 @@
|
|||||||
// Hyperion includes
|
// Hyperion includes
|
||||||
#include <hyperion/ImageProcessor.h>
|
#include <hyperion/ImageProcessor.h>
|
||||||
#include <hyperion/ImageToLedsMap.h>
|
#include <hyperion/ImageToLedsMap.h>
|
||||||
#include <hyperion/BlackBorderProcessor.h>
|
|
||||||
|
// Blacborder includes
|
||||||
|
#include <blackborder/BlackBorderProcessor.h>
|
||||||
|
|
||||||
using namespace hyperion;
|
using namespace hyperion;
|
||||||
|
|
||||||
ImageProcessor::ImageProcessor(const LedString& ledString, bool enableBlackBorderDetector) :
|
ImageProcessor::ImageProcessor(const LedString& ledString, bool enableBlackBorderDetector, uint8_t blackborderThreshold) :
|
||||||
mLedString(ledString),
|
_ledString(ledString),
|
||||||
_enableBlackBorderRemoval(enableBlackBorderDetector),
|
_enableBlackBorderRemoval(enableBlackBorderDetector),
|
||||||
_borderProcessor(new BlackBorderProcessor(600, 50, 1)),
|
_borderProcessor(new BlackBorderProcessor(600, 50, 1, blackborderThreshold)),
|
||||||
mImageToLeds(nullptr)
|
_imageToLeds(nullptr)
|
||||||
{
|
{
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageProcessor::~ImageProcessor()
|
ImageProcessor::~ImageProcessor()
|
||||||
{
|
{
|
||||||
delete mImageToLeds;
|
delete _imageToLeds;
|
||||||
delete _borderProcessor;
|
delete _borderProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ImageProcessor::getLedCount() const
|
unsigned ImageProcessor::getLedCount() const
|
||||||
{
|
{
|
||||||
return mLedString.leds().size();
|
return _ledString.leds().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageProcessor::setSize(const unsigned width, const unsigned height)
|
void ImageProcessor::setSize(const unsigned width, const unsigned height)
|
||||||
{
|
{
|
||||||
// Check if the existing buffer-image is already the correct dimensions
|
// Check if the existing buffer-image is already the correct dimensions
|
||||||
if (mImageToLeds && mImageToLeds->width() == width && mImageToLeds->height() == height)
|
if (_imageToLeds && _imageToLeds->width() == width && _imageToLeds->height() == height)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the old buffer and mapping
|
// Clean up the old buffer and mapping
|
||||||
delete mImageToLeds;
|
delete _imageToLeds;
|
||||||
|
|
||||||
// Construct a new buffer and mapping
|
// Construct a new buffer and mapping
|
||||||
mImageToLeds = new ImageToLedsMap(width, height, 0, 0, mLedString.leds());
|
_imageToLeds = new ImageToLedsMap(width, height, 0, 0, _ledString.leds());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &hscanEnd, double &vscanBegin, double &vscanEnd) const
|
bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &hscanEnd, double &vscanBegin, double &vscanEnd) const
|
||||||
{
|
{
|
||||||
if (led < mLedString.leds().size())
|
if (led < _ledString.leds().size())
|
||||||
{
|
{
|
||||||
const Led & l = mLedString.leds()[led];
|
const Led & l = _ledString.leds()[led];
|
||||||
hscanBegin = l.minX_frac;
|
hscanBegin = l.minX_frac;
|
||||||
hscanEnd = l.maxX_frac;
|
hscanEnd = l.maxX_frac;
|
||||||
vscanBegin = l.minY_frac;
|
vscanBegin = l.minY_frac;
|
||||||
|
@ -10,13 +10,25 @@ ImageProcessorFactory& ImageProcessorFactory::getInstance()
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageProcessorFactory::init(const LedString& ledString, bool enableBlackBorderDetector)
|
void ImageProcessorFactory::init(const LedString& ledString, bool enableBlackBorderDetector, double blackborderThreshold)
|
||||||
{
|
{
|
||||||
_ledString = ledString;
|
_ledString = ledString;
|
||||||
_enableBlackBorderDetector = enableBlackBorderDetector;
|
_enableBlackBorderDetector = enableBlackBorderDetector;
|
||||||
|
|
||||||
|
int threshold = int(std::ceil(blackborderThreshold * 255));
|
||||||
|
if (threshold < 0)
|
||||||
|
threshold = 0;
|
||||||
|
else if (threshold > 255)
|
||||||
|
threshold = 255;
|
||||||
|
_blackborderThreshold = uint8_t(threshold);
|
||||||
|
|
||||||
|
if (_enableBlackBorderDetector)
|
||||||
|
{
|
||||||
|
std::cout << "Black border threshold set to " << blackborderThreshold << " (" << int(_blackborderThreshold) << ")" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageProcessor* ImageProcessorFactory::newImageProcessor() const
|
ImageProcessor* ImageProcessorFactory::newImageProcessor() const
|
||||||
{
|
{
|
||||||
return new ImageProcessor(_ledString, _enableBlackBorderDetector);
|
return new ImageProcessor(_ledString, _enableBlackBorderDetector, _blackborderThreshold);
|
||||||
}
|
}
|
||||||
|
@ -223,6 +223,12 @@
|
|||||||
"enable" : {
|
"enable" : {
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"required" : true
|
"required" : true
|
||||||
|
},
|
||||||
|
"threshold" : {
|
||||||
|
"type" : "number",
|
||||||
|
"required" : false,
|
||||||
|
"minimum" : 0.0,
|
||||||
|
"maximum" : 1.0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties" : false
|
"additionalProperties" : false
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver)
|
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver)
|
||||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver)
|
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver)
|
||||||
|
|
||||||
# add protocol buffers
|
|
||||||
find_package(Protobuf REQUIRED)
|
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
${PROTOBUF_INCLUDE_DIRS})
|
${PROTOBUF_INCLUDE_DIRS})
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
add_subdirectory(hyperiond)
|
add_subdirectory(hyperiond)
|
||||||
add_subdirectory(hyperion-remote)
|
add_subdirectory(hyperion-remote)
|
||||||
|
if (ENABLE_V4L2)
|
||||||
|
add_subdirectory(hyperion-v4l2)
|
||||||
|
endif (ENABLE_V4L2)
|
||||||
|
57
src/hyperion-v4l2/CMakeLists.txt
Normal file
57
src/hyperion-v4l2/CMakeLists.txt
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
project(hyperion-v4l2)
|
||||||
|
|
||||||
|
# add protocol buffers
|
||||||
|
find_package(Protobuf REQUIRED)
|
||||||
|
|
||||||
|
# find Qt4
|
||||||
|
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
|
${QT_INCLUDES}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Hyperion_V4L2_HEADERS
|
||||||
|
V4L2Grabber.h
|
||||||
|
ProtoConnection.h
|
||||||
|
ImageHandler.h
|
||||||
|
VideoStandardParameter.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Hyperion_V4L2_SOURCES
|
||||||
|
hyperion-v4l2.cpp
|
||||||
|
V4L2Grabber.cpp
|
||||||
|
ProtoConnection.cpp
|
||||||
|
ImageHandler.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Hyperion_V4L2_PROTOS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/protoserver/message.proto
|
||||||
|
)
|
||||||
|
|
||||||
|
protobuf_generate_cpp(Hyperion_V4L2_PROTO_SRCS Hyperion_V4L2_PROTO_HDRS
|
||||||
|
${Hyperion_V4L2_PROTOS}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(hyperion-v4l2
|
||||||
|
${Hyperion_V4L2_HEADERS}
|
||||||
|
${Hyperion_V4L2_SOURCES}
|
||||||
|
${Hyperion_V4L2_PROTO_SRCS}
|
||||||
|
${Hyperion_V4L2_PROTO_HDRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(hyperion-v4l2
|
||||||
|
getoptPlusPlus
|
||||||
|
blackborder
|
||||||
|
hyperion-utils
|
||||||
|
${PROTOBUF_LIBRARIES}
|
||||||
|
pthread
|
||||||
|
)
|
||||||
|
|
||||||
|
qt4_use_modules(hyperion-v4l2
|
||||||
|
Core
|
||||||
|
Gui
|
||||||
|
Network)
|
41
src/hyperion-v4l2/ImageHandler.cpp
Normal file
41
src/hyperion-v4l2/ImageHandler.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// hyperion-v4l2 includes
|
||||||
|
#include "ImageHandler.h"
|
||||||
|
|
||||||
|
ImageHandler::ImageHandler(const std::string &address, int priority, double signalThreshold, bool skipProtoReply) :
|
||||||
|
_priority(priority),
|
||||||
|
_connection(address),
|
||||||
|
_signalThreshold(signalThreshold),
|
||||||
|
_signalProcessor(100, 50, 0, uint8_t(std::min(255, std::max(0, int(255*signalThreshold)))))
|
||||||
|
{
|
||||||
|
_connection.setSkipReply(skipProtoReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageHandler::receiveImage(const Image<ColorRgb> &image)
|
||||||
|
{
|
||||||
|
// check if we should do signal detection
|
||||||
|
if (_signalThreshold < 0)
|
||||||
|
{
|
||||||
|
_connection.setImage(image, _priority, 200);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_signalProcessor.process(image))
|
||||||
|
{
|
||||||
|
std::cout << "Signal state = " << (_signalProcessor.getCurrentBorder().unknown ? "off" : "on") << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// consider an unknown border as no signal
|
||||||
|
// send the image to Hyperion if we have a signal
|
||||||
|
if (!_signalProcessor.getCurrentBorder().unknown)
|
||||||
|
{
|
||||||
|
_connection.setImage(image, _priority, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageHandler::imageCallback(void *arg, const Image<ColorRgb> &image)
|
||||||
|
{
|
||||||
|
ImageHandler * handler = static_cast<ImageHandler *>(arg);
|
||||||
|
handler->receiveImage(image);
|
||||||
|
}
|
||||||
|
|
34
src/hyperion-v4l2/ImageHandler.h
Normal file
34
src/hyperion-v4l2/ImageHandler.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// blackborder includes
|
||||||
|
#include <blackborder/BlackBorderProcessor.h>
|
||||||
|
|
||||||
|
// hyperion-v4l includes
|
||||||
|
#include "ProtoConnection.h"
|
||||||
|
|
||||||
|
/// This class handles callbacks from the V4L2 grabber
|
||||||
|
class ImageHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ImageHandler(const std::string & address, int priority, double signalThreshold, bool skipProtoReply);
|
||||||
|
|
||||||
|
/// Handle a single image
|
||||||
|
/// @param image The image to process
|
||||||
|
void receiveImage(const Image<ColorRgb> & image);
|
||||||
|
|
||||||
|
/// static function used to direct callbacks to a ImageHandler object
|
||||||
|
/// @param arg This should be an ImageHandler instance
|
||||||
|
/// @param image The image to process
|
||||||
|
static void imageCallback(void * arg, const Image<ColorRgb> & image);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Priority for calls to Hyperion
|
||||||
|
const int _priority;
|
||||||
|
|
||||||
|
/// Hyperion proto connection object
|
||||||
|
ProtoConnection _connection;
|
||||||
|
|
||||||
|
/// Threshold used for signal detection
|
||||||
|
double _signalThreshold;
|
||||||
|
|
||||||
|
/// Blackborder detector which is used as a signal detector (unknown border = no signal)
|
||||||
|
hyperion::BlackBorderProcessor _signalProcessor;
|
||||||
|
};
|
188
src/hyperion-v4l2/ProtoConnection.cpp
Normal file
188
src/hyperion-v4l2/ProtoConnection.cpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// stl includes
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QRgb>
|
||||||
|
|
||||||
|
// hyperion-v4l2 includes
|
||||||
|
#include "ProtoConnection.h"
|
||||||
|
|
||||||
|
ProtoConnection::ProtoConnection(const std::string & a) :
|
||||||
|
_socket(),
|
||||||
|
_skipReply(false)
|
||||||
|
{
|
||||||
|
QString address(a.c_str());
|
||||||
|
QStringList parts = address.split(":");
|
||||||
|
if (parts.size() != 2)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(address).toStdString());
|
||||||
|
}
|
||||||
|
_host = parts[0];
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
_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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to connect to host
|
||||||
|
std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
|
||||||
|
connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtoConnection::~ProtoConnection()
|
||||||
|
{
|
||||||
|
_socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnection::setSkipReply(bool skip)
|
||||||
|
{
|
||||||
|
_skipReply = skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnection::setColor(const ColorRgb & color, int priority, int duration)
|
||||||
|
{
|
||||||
|
proto::HyperionRequest request;
|
||||||
|
request.set_command(proto::HyperionRequest::COLOR);
|
||||||
|
proto::ColorRequest * colorRequest = request.MutableExtension(proto::ColorRequest::colorRequest);
|
||||||
|
colorRequest->set_rgbcolor((color.red << 16) | (color.green << 8) | color.blue);
|
||||||
|
colorRequest->set_priority(priority);
|
||||||
|
colorRequest->set_duration(duration);
|
||||||
|
|
||||||
|
// send command message
|
||||||
|
sendMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnection::setImage(const Image<ColorRgb> &image, int priority, int duration)
|
||||||
|
{
|
||||||
|
proto::HyperionRequest request;
|
||||||
|
request.set_command(proto::HyperionRequest::IMAGE);
|
||||||
|
proto::ImageRequest * imageRequest = request.MutableExtension(proto::ImageRequest::imageRequest);
|
||||||
|
imageRequest->set_imagedata(image.memptr(), image.width() * image.height() * 3);
|
||||||
|
imageRequest->set_imagewidth(image.width());
|
||||||
|
imageRequest->set_imageheight(image.height());
|
||||||
|
imageRequest->set_priority(priority);
|
||||||
|
imageRequest->set_duration(duration);
|
||||||
|
|
||||||
|
// send command message
|
||||||
|
sendMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnection::clear(int priority)
|
||||||
|
{
|
||||||
|
proto::HyperionRequest request;
|
||||||
|
request.set_command(proto::HyperionRequest::CLEAR);
|
||||||
|
proto::ClearRequest * clearRequest = request.MutableExtension(proto::ClearRequest::clearRequest);
|
||||||
|
clearRequest->set_priority(priority);
|
||||||
|
|
||||||
|
// send command message
|
||||||
|
sendMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnection::clearAll()
|
||||||
|
{
|
||||||
|
proto::HyperionRequest request;
|
||||||
|
request.set_command(proto::HyperionRequest::CLEARALL);
|
||||||
|
|
||||||
|
// send command message
|
||||||
|
sendMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnection::connectToHost()
|
||||||
|
{
|
||||||
|
_socket.connectToHost(_host, _port);
|
||||||
|
if (_socket.waitForConnected()) {
|
||||||
|
std::cout << "Connected to Hyperion host" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnection::sendMessage(const proto::HyperionRequest &message)
|
||||||
|
{
|
||||||
|
if (_socket.state() == QAbstractSocket::UnconnectedState)
|
||||||
|
{
|
||||||
|
std::cout << "Currently disconnected: trying to connect to host" << std::endl;
|
||||||
|
connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_socket.state() != QAbstractSocket::ConnectedState)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only get here if we are connected
|
||||||
|
|
||||||
|
// serialize message (FastWriter already appends a newline)
|
||||||
|
std::string serializedMessage = message.SerializeAsString();
|
||||||
|
|
||||||
|
int length = serializedMessage.size();
|
||||||
|
const uint8_t header[] = {
|
||||||
|
uint8_t((length >> 24) & 0xFF),
|
||||||
|
uint8_t((length >> 16) & 0xFF),
|
||||||
|
uint8_t((length >> 8) & 0xFF),
|
||||||
|
uint8_t((length ) & 0xFF)};
|
||||||
|
|
||||||
|
// write message
|
||||||
|
int count = 0;
|
||||||
|
count += _socket.write(reinterpret_cast<const char *>(header), 4);
|
||||||
|
count += _socket.write(reinterpret_cast<const char *>(serializedMessage.data()), length);
|
||||||
|
if (!_socket.waitForBytesWritten())
|
||||||
|
{
|
||||||
|
std::cerr << "Error while writing data to host" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_skipReply)
|
||||||
|
{
|
||||||
|
// read reply data
|
||||||
|
QByteArray serializedReply;
|
||||||
|
length = -1;
|
||||||
|
while (length < 0 && serializedReply.size() < length+4)
|
||||||
|
{
|
||||||
|
// receive reply
|
||||||
|
if (!_socket.waitForReadyRead())
|
||||||
|
{
|
||||||
|
std::cerr << "Error while reading data from host" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedReply += _socket.readAll();
|
||||||
|
|
||||||
|
if (length < 0 && serializedReply.size() >= 4)
|
||||||
|
{
|
||||||
|
// read the message size
|
||||||
|
length =
|
||||||
|
((serializedReply[0]<<24) & 0xFF000000) |
|
||||||
|
((serializedReply[1]<<16) & 0x00FF0000) |
|
||||||
|
((serializedReply[2]<< 8) & 0x0000FF00) |
|
||||||
|
((serializedReply[3] ) & 0x000000FF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse reply data
|
||||||
|
proto::HyperionReply reply;
|
||||||
|
reply.ParseFromArray(serializedReply.constData()+4, length);
|
||||||
|
|
||||||
|
// parse reply message
|
||||||
|
parseReply(reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProtoConnection::parseReply(const proto::HyperionReply &reply)
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if (!reply.success())
|
||||||
|
{
|
||||||
|
if (reply.has_error())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Error: " + reply.error());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Error: No error info");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
102
src/hyperion-v4l2/ProtoConnection.h
Normal file
102
src/hyperion-v4l2/ProtoConnection.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// stl includes
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QColor>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
// hyperion util
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
// jsoncpp includes
|
||||||
|
#include <message.pb.h>
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Connection class to setup an connection to the hyperion server and execute commands
|
||||||
|
///
|
||||||
|
class ProtoConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Constructor
|
||||||
|
///
|
||||||
|
/// @param address The address of the Hyperion server (for example "192.168.0.32:19444)
|
||||||
|
///
|
||||||
|
ProtoConnection(const std::string & address);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Destructor
|
||||||
|
///
|
||||||
|
~ProtoConnection();
|
||||||
|
|
||||||
|
/// Do not read reply messages from Hyperion if set to true
|
||||||
|
void setSkipReply(bool skip);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set all leds to the specified color
|
||||||
|
///
|
||||||
|
/// @param color The color
|
||||||
|
/// @param priority The priority
|
||||||
|
/// @param duration The duration in milliseconds
|
||||||
|
///
|
||||||
|
void setColor(const ColorRgb & color, int priority, int duration = 1);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set the leds according to the given image (assume the image is stretched to the display size)
|
||||||
|
///
|
||||||
|
/// @param image The image
|
||||||
|
/// @param priority The priority
|
||||||
|
/// @param duration The duration in milliseconds
|
||||||
|
///
|
||||||
|
void setImage(const Image<ColorRgb> & image, int priority, int duration = -1);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Clear the given priority channel
|
||||||
|
///
|
||||||
|
/// @param priority The priority
|
||||||
|
///
|
||||||
|
void clear(int priority);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Clear all priority channels
|
||||||
|
///
|
||||||
|
void clearAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Try to connect to the Hyperion host
|
||||||
|
void connectToHost();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Send a command message and receive its reply
|
||||||
|
///
|
||||||
|
/// @param message The message to send
|
||||||
|
///
|
||||||
|
void sendMessage(const proto::HyperionRequest & message);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Parse a reply message
|
||||||
|
///
|
||||||
|
/// @param reply The received reply
|
||||||
|
///
|
||||||
|
/// @return true if the reply indicates success
|
||||||
|
///
|
||||||
|
bool parseReply(const proto::HyperionReply & reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// The TCP-Socket with the connection to the server
|
||||||
|
QTcpSocket _socket;
|
||||||
|
|
||||||
|
/// Host address
|
||||||
|
QString _host;
|
||||||
|
|
||||||
|
/// Host port
|
||||||
|
uint16_t _port;
|
||||||
|
|
||||||
|
/// Skip receiving reply messages from Hyperion if set
|
||||||
|
bool _skipReply;
|
||||||
|
};
|
711
src/hyperion-v4l2/V4L2Grabber.cpp
Normal file
711
src/hyperion-v4l2/V4L2Grabber.cpp
Normal file
@ -0,0 +1,711 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/videodev2.h>
|
||||||
|
|
||||||
|
#include "V4L2Grabber.h"
|
||||||
|
|
||||||
|
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||||
|
|
||||||
|
static inline uint8_t clamp(int x)
|
||||||
|
{
|
||||||
|
return (x<0) ? 0 : ((x>255) ? 255 : uint8_t(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, uint8_t & b)
|
||||||
|
{
|
||||||
|
// see: http://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion
|
||||||
|
int c = y - 16;
|
||||||
|
int d = u - 128;
|
||||||
|
int e = v - 128;
|
||||||
|
|
||||||
|
r = clamp((298 * c + 409 * e + 128) >> 8);
|
||||||
|
g = clamp((298 * c - 100 * d - 208 * e + 128) >> 8);
|
||||||
|
b = clamp((298 * c + 516 * d + 128) >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
V4L2Grabber::V4L2Grabber(const std::string &device, int input, VideoStandard videoStandard, int width, int height, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation) :
|
||||||
|
_deviceName(device),
|
||||||
|
_ioMethod(IO_METHOD_MMAP),
|
||||||
|
_fileDescriptor(-1),
|
||||||
|
_buffers(),
|
||||||
|
_pixelFormat(0),
|
||||||
|
_width(width),
|
||||||
|
_height(height),
|
||||||
|
_cropWidth(cropHorizontal),
|
||||||
|
_cropHeight(cropVertical),
|
||||||
|
_frameDecimation(std::max(1, frameDecimation)),
|
||||||
|
_pixelDecimation(std::max(1, pixelDecimation)),
|
||||||
|
_currentFrame(0),
|
||||||
|
_callback(nullptr),
|
||||||
|
_callbackArg(nullptr)
|
||||||
|
{
|
||||||
|
open_device();
|
||||||
|
init_device(videoStandard, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
V4L2Grabber::~V4L2Grabber()
|
||||||
|
{
|
||||||
|
uninit_device();
|
||||||
|
close_device();
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::setCallback(V4L2Grabber::ImageCallback callback, void *arg)
|
||||||
|
{
|
||||||
|
_callback = callback;
|
||||||
|
_callbackArg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::start()
|
||||||
|
{
|
||||||
|
start_capturing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::capture(int frameCount)
|
||||||
|
{
|
||||||
|
for (int count = 0; count < frameCount || frameCount < 0; ++count)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// the set of file descriptors for select
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(_fileDescriptor, &fds);
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = 2;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
// block until data is available
|
||||||
|
int r = select(_fileDescriptor + 1, &fds, NULL, NULL, &tv);
|
||||||
|
|
||||||
|
if (-1 == r)
|
||||||
|
{
|
||||||
|
if (EINTR == errno)
|
||||||
|
continue;
|
||||||
|
throw_errno_exception("select");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == r)
|
||||||
|
{
|
||||||
|
throw_exception("select timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_frame())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EAGAIN - continue select loop. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::stop()
|
||||||
|
{
|
||||||
|
stop_capturing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::open_device()
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (-1 == stat(_deviceName.c_str(), &st))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Cannot identify '" << _deviceName << "'";
|
||||||
|
throw_errno_exception(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISCHR(st.st_mode))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "'" << _deviceName << "' is no device";
|
||||||
|
throw_exception(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileDescriptor = open(_deviceName.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
|
||||||
|
|
||||||
|
if (-1 == _fileDescriptor)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Cannot open '" << _deviceName << "'";
|
||||||
|
throw_errno_exception(oss.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::close_device()
|
||||||
|
{
|
||||||
|
if (-1 == close(_fileDescriptor))
|
||||||
|
throw_errno_exception("close");
|
||||||
|
|
||||||
|
_fileDescriptor = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::init_read(unsigned int buffer_size)
|
||||||
|
{
|
||||||
|
_buffers.resize(1);
|
||||||
|
|
||||||
|
_buffers[0].length = buffer_size;
|
||||||
|
_buffers[0].start = malloc(buffer_size);
|
||||||
|
|
||||||
|
if (!_buffers[0].start) {
|
||||||
|
throw_exception("Out of memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::init_mmap()
|
||||||
|
{
|
||||||
|
struct v4l2_requestbuffers req;
|
||||||
|
|
||||||
|
CLEAR(req);
|
||||||
|
|
||||||
|
req.count = 4;
|
||||||
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
req.memory = V4L2_MEMORY_MMAP;
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_REQBUFS, &req)) {
|
||||||
|
if (EINVAL == errno) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "'" << _deviceName << "' does not support memory mapping";
|
||||||
|
throw_exception(oss.str());
|
||||||
|
} else {
|
||||||
|
throw_errno_exception("VIDIOC_REQBUFS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.count < 2) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Insufficient buffer memory on " << _deviceName;
|
||||||
|
throw_exception(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffers.resize(req.count);
|
||||||
|
|
||||||
|
for (size_t n_buffers = 0; n_buffers < req.count; ++n_buffers) {
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
|
||||||
|
CLEAR(buf);
|
||||||
|
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
buf.index = n_buffers;
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_QUERYBUF, &buf))
|
||||||
|
throw_errno_exception("VIDIOC_QUERYBUF");
|
||||||
|
|
||||||
|
_buffers[n_buffers].length = buf.length;
|
||||||
|
_buffers[n_buffers].start =
|
||||||
|
mmap(NULL /* start anywhere */,
|
||||||
|
buf.length,
|
||||||
|
PROT_READ | PROT_WRITE /* required */,
|
||||||
|
MAP_SHARED /* recommended */,
|
||||||
|
_fileDescriptor, buf.m.offset);
|
||||||
|
|
||||||
|
if (MAP_FAILED == _buffers[n_buffers].start)
|
||||||
|
throw_errno_exception("mmap");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::init_userp(unsigned int buffer_size)
|
||||||
|
{
|
||||||
|
struct v4l2_requestbuffers req;
|
||||||
|
|
||||||
|
CLEAR(req);
|
||||||
|
|
||||||
|
req.count = 4;
|
||||||
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
req.memory = V4L2_MEMORY_USERPTR;
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_REQBUFS, &req)) {
|
||||||
|
if (EINVAL == errno)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "'" << _deviceName << "' does not support user pointer";
|
||||||
|
throw_exception(oss.str());
|
||||||
|
} else {
|
||||||
|
throw_errno_exception("VIDIOC_REQBUFS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffers.resize(4);
|
||||||
|
|
||||||
|
for (size_t n_buffers = 0; n_buffers < 4; ++n_buffers) {
|
||||||
|
_buffers[n_buffers].length = buffer_size;
|
||||||
|
_buffers[n_buffers].start = malloc(buffer_size);
|
||||||
|
|
||||||
|
if (!_buffers[n_buffers].start) {
|
||||||
|
throw_exception("Out of memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||||
|
{
|
||||||
|
struct v4l2_capability cap;
|
||||||
|
if (-1 == xioctl(VIDIOC_QUERYCAP, &cap))
|
||||||
|
{
|
||||||
|
if (EINVAL == errno) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "'" << _deviceName << "' is no V4L2 device";
|
||||||
|
throw_exception(oss.str());
|
||||||
|
} else {
|
||||||
|
throw_errno_exception("VIDIOC_QUERYCAP");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "'" << _deviceName << "' is no video capture device";
|
||||||
|
throw_exception(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_ioMethod) {
|
||||||
|
case IO_METHOD_READ:
|
||||||
|
if (!(cap.capabilities & V4L2_CAP_READWRITE))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "'" << _deviceName << "' does not support read i/o";
|
||||||
|
throw_exception(oss.str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_MMAP:
|
||||||
|
case IO_METHOD_USERPTR:
|
||||||
|
if (!(cap.capabilities & V4L2_CAP_STREAMING))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "'" << _deviceName << "' does not support streaming i/o";
|
||||||
|
throw_exception(oss.str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Select video input, video standard and tune here. */
|
||||||
|
|
||||||
|
struct v4l2_cropcap cropcap;
|
||||||
|
CLEAR(cropcap);
|
||||||
|
|
||||||
|
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
|
||||||
|
if (0 == xioctl(VIDIOC_CROPCAP, &cropcap)) {
|
||||||
|
struct v4l2_crop crop;
|
||||||
|
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
crop.c = cropcap.defrect; /* reset to default */
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_S_CROP, &crop)) {
|
||||||
|
switch (errno) {
|
||||||
|
case EINVAL:
|
||||||
|
/* Cropping not supported. */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Errors ignored. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Errors ignored. */
|
||||||
|
}
|
||||||
|
|
||||||
|
// set input if needed
|
||||||
|
if (input >= 0)
|
||||||
|
{
|
||||||
|
if (-1 == xioctl(VIDIOC_S_INPUT, &input))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_S_INPUT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the video standard if needed
|
||||||
|
switch (videoStandard)
|
||||||
|
{
|
||||||
|
case PAL:
|
||||||
|
{
|
||||||
|
v4l2_std_id std_id = V4L2_STD_PAL;
|
||||||
|
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_S_STD");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NTSC:
|
||||||
|
{
|
||||||
|
v4l2_std_id std_id = V4L2_STD_NTSC;
|
||||||
|
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_S_STD");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NO_CHANGE:
|
||||||
|
default:
|
||||||
|
// No change to device settings
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get the current settings
|
||||||
|
struct v4l2_format fmt;
|
||||||
|
CLEAR(fmt);
|
||||||
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_G_FMT");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check pixel format
|
||||||
|
switch (fmt.fmt.pix.pixelformat)
|
||||||
|
{
|
||||||
|
case V4L2_PIX_FMT_UYVY:
|
||||||
|
case V4L2_PIX_FMT_YUYV:
|
||||||
|
_pixelFormat = fmt.fmt.pix.pixelformat;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw_exception("Only pixel formats UYVY and YUYV are supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_width > 0 || _height > 0)
|
||||||
|
{
|
||||||
|
if (_width > 0)
|
||||||
|
{
|
||||||
|
fmt.fmt.pix.width = _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.fmt.pix.height > 0)
|
||||||
|
{
|
||||||
|
fmt.fmt.pix.height = _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the settings
|
||||||
|
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_S_FMT");
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the format settings again
|
||||||
|
// (the size may not have been accepted without an error)
|
||||||
|
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_G_FMT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store width & height
|
||||||
|
_width = fmt.fmt.pix.width;
|
||||||
|
_height = fmt.fmt.pix.height;
|
||||||
|
|
||||||
|
// print the eventually used width and height
|
||||||
|
std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl;
|
||||||
|
|
||||||
|
switch (_ioMethod) {
|
||||||
|
case IO_METHOD_READ:
|
||||||
|
init_read(fmt.fmt.pix.sizeimage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_MMAP:
|
||||||
|
init_mmap();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_USERPTR:
|
||||||
|
init_userp(fmt.fmt.pix.sizeimage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::uninit_device()
|
||||||
|
{
|
||||||
|
switch (_ioMethod) {
|
||||||
|
case IO_METHOD_READ:
|
||||||
|
free(_buffers[0].start);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_MMAP:
|
||||||
|
for (size_t i = 0; i < _buffers.size(); ++i)
|
||||||
|
if (-1 == munmap(_buffers[i].start, _buffers[i].length))
|
||||||
|
throw_errno_exception("munmap");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_USERPTR:
|
||||||
|
for (size_t i = 0; i < _buffers.size(); ++i)
|
||||||
|
free(_buffers[i].start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffers.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::start_capturing()
|
||||||
|
{
|
||||||
|
switch (_ioMethod) {
|
||||||
|
case IO_METHOD_READ:
|
||||||
|
/* Nothing to do. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_MMAP:
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < _buffers.size(); ++i) {
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
|
||||||
|
CLEAR(buf);
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
buf.index = i;
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||||
|
throw_errno_exception("VIDIOC_QBUF");
|
||||||
|
}
|
||||||
|
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
if (-1 == xioctl(VIDIOC_STREAMON, &type))
|
||||||
|
throw_errno_exception("VIDIOC_STREAMON");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IO_METHOD_USERPTR:
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < _buffers.size(); ++i) {
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
|
||||||
|
CLEAR(buf);
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_USERPTR;
|
||||||
|
buf.index = i;
|
||||||
|
buf.m.userptr = (unsigned long)_buffers[i].start;
|
||||||
|
buf.length = _buffers[i].length;
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||||
|
throw_errno_exception("VIDIOC_QBUF");
|
||||||
|
}
|
||||||
|
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
if (-1 == xioctl(VIDIOC_STREAMON, &type))
|
||||||
|
throw_errno_exception("VIDIOC_STREAMON");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::stop_capturing()
|
||||||
|
{
|
||||||
|
enum v4l2_buf_type type;
|
||||||
|
|
||||||
|
switch (_ioMethod) {
|
||||||
|
case IO_METHOD_READ:
|
||||||
|
/* Nothing to do. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_MMAP:
|
||||||
|
case IO_METHOD_USERPTR:
|
||||||
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
if (-1 == xioctl(VIDIOC_STREAMOFF, &type))
|
||||||
|
throw_errno_exception("VIDIOC_STREAMOFF");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int V4L2Grabber::read_frame()
|
||||||
|
{
|
||||||
|
bool rc = false;
|
||||||
|
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
|
||||||
|
switch (_ioMethod) {
|
||||||
|
case IO_METHOD_READ:
|
||||||
|
int size;
|
||||||
|
if ((size = read(_fileDescriptor, _buffers[0].start, _buffers[0].length)) == -1)
|
||||||
|
{
|
||||||
|
switch (errno)
|
||||||
|
{
|
||||||
|
case EAGAIN:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case EIO:
|
||||||
|
/* Could ignore EIO, see spec. */
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw_errno_exception("read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = process_image(_buffers[0].start, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_MMAP:
|
||||||
|
CLEAR(buf);
|
||||||
|
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_DQBUF, &buf))
|
||||||
|
{
|
||||||
|
switch (errno)
|
||||||
|
{
|
||||||
|
case EAGAIN:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case EIO:
|
||||||
|
/* Could ignore EIO, see spec. */
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw_errno_exception("VIDIOC_DQBUF");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(buf.index < _buffers.size());
|
||||||
|
|
||||||
|
rc = process_image(_buffers[buf.index].start, buf.bytesused);
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_QBUF");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IO_METHOD_USERPTR:
|
||||||
|
CLEAR(buf);
|
||||||
|
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_USERPTR;
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_DQBUF, &buf))
|
||||||
|
{
|
||||||
|
switch (errno)
|
||||||
|
{
|
||||||
|
case EAGAIN:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case EIO:
|
||||||
|
/* Could ignore EIO, see spec. */
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw_errno_exception("VIDIOC_DQBUF");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _buffers.size(); ++i)
|
||||||
|
{
|
||||||
|
if (buf.m.userptr == (unsigned long)_buffers[i].start && buf.length == _buffers[i].length)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = process_image((void *)buf.m.userptr, buf.bytesused);
|
||||||
|
|
||||||
|
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||||
|
{
|
||||||
|
throw_errno_exception("VIDIOC_QBUF");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool V4L2Grabber::process_image(const void *p, int size)
|
||||||
|
{
|
||||||
|
if (++_currentFrame >= _frameDecimation)
|
||||||
|
{
|
||||||
|
// We do want a new frame...
|
||||||
|
|
||||||
|
if (size != 2*_width*_height)
|
||||||
|
{
|
||||||
|
std::cout << "Frame too small: " << size << " != " << (2*_width*_height) << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
process_image(reinterpret_cast<const uint8_t *>(p));
|
||||||
|
_currentFrame = 0; // restart counting
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::process_image(const uint8_t * data)
|
||||||
|
{
|
||||||
|
int width = (_width - 2 * _cropWidth + _pixelDecimation/2) / _pixelDecimation;
|
||||||
|
int height = (_height - 2 * _cropHeight + _pixelDecimation/2) / _pixelDecimation;
|
||||||
|
|
||||||
|
Image<ColorRgb> image(width, height);
|
||||||
|
|
||||||
|
for (int ySource = _cropHeight + _pixelDecimation/2, yDest = 0; ySource < _height - _cropHeight; ySource += _pixelDecimation, ++yDest)
|
||||||
|
{
|
||||||
|
for (int xSource = _cropWidth + _pixelDecimation/2, xDest = 0; xSource < _width - _cropWidth; xSource += _pixelDecimation, ++xDest)
|
||||||
|
{
|
||||||
|
int index = (_width * ySource + xSource) * 2;
|
||||||
|
uint8_t y = 0;
|
||||||
|
uint8_t u = 0;
|
||||||
|
uint8_t v = 0;
|
||||||
|
|
||||||
|
switch (_pixelFormat)
|
||||||
|
{
|
||||||
|
case V4L2_PIX_FMT_UYVY:
|
||||||
|
y = data[index+1];
|
||||||
|
u = (xSource%2 == 0) ? data[index ] : data[index-2];
|
||||||
|
v = (xSource%2 == 0) ? data[index+2] : data[index ];
|
||||||
|
break;
|
||||||
|
case V4L2_PIX_FMT_YUYV:
|
||||||
|
y = data[index];
|
||||||
|
u = (xSource%2 == 0) ? data[index+1] : data[index-1];
|
||||||
|
v = (xSource%2 == 0) ? data[index+3] : data[index+1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorRgb & rgb = image(xDest, yDest);
|
||||||
|
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_callback != nullptr)
|
||||||
|
{
|
||||||
|
(*_callback)(_callbackArg, image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int V4L2Grabber::xioctl(int request, void *arg)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
r = ioctl(_fileDescriptor, request, arg);
|
||||||
|
}
|
||||||
|
while (-1 == r && EINTR == errno);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::throw_exception(const std::string & error)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << error << " error";
|
||||||
|
throw std::runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::throw_errno_exception(const std::string & error)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << error << " error " << errno << ", " << strerror(errno);
|
||||||
|
throw std::runtime_error(oss.str());
|
||||||
|
}
|
96
src/hyperion-v4l2/V4L2Grabber.h
Normal file
96
src/hyperion-v4l2/V4L2Grabber.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// stl includes
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// util includes
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
/// Capture class for V4L2 devices
|
||||||
|
///
|
||||||
|
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
|
||||||
|
class V4L2Grabber
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef void (*ImageCallback)(void * arg, const Image<ColorRgb> & image);
|
||||||
|
|
||||||
|
enum VideoStandard {
|
||||||
|
PAL, NTSC, NO_CHANGE
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
V4L2Grabber(const std::string & device, int input, VideoStandard videoStandard, int width, int height, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation);
|
||||||
|
virtual ~V4L2Grabber();
|
||||||
|
|
||||||
|
void setCallback(ImageCallback callback, void * arg);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void capture(int frameCount = -1);
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void open_device();
|
||||||
|
|
||||||
|
void close_device();
|
||||||
|
|
||||||
|
void init_read(unsigned int buffer_size);
|
||||||
|
|
||||||
|
void init_mmap();
|
||||||
|
|
||||||
|
void init_userp(unsigned int buffer_size);
|
||||||
|
|
||||||
|
void init_device(VideoStandard videoStandard, int input);
|
||||||
|
|
||||||
|
void uninit_device();
|
||||||
|
|
||||||
|
void start_capturing();
|
||||||
|
|
||||||
|
void stop_capturing();
|
||||||
|
|
||||||
|
int read_frame();
|
||||||
|
|
||||||
|
bool process_image(const void *p, int size);
|
||||||
|
|
||||||
|
void process_image(const uint8_t *p);
|
||||||
|
|
||||||
|
int xioctl(int request, void *arg);
|
||||||
|
|
||||||
|
void throw_exception(const std::string &error);
|
||||||
|
|
||||||
|
void throw_errno_exception(const std::string &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum io_method {
|
||||||
|
IO_METHOD_READ,
|
||||||
|
IO_METHOD_MMAP,
|
||||||
|
IO_METHOD_USERPTR
|
||||||
|
};
|
||||||
|
|
||||||
|
struct buffer {
|
||||||
|
void *start;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string _deviceName;
|
||||||
|
const io_method _ioMethod;
|
||||||
|
int _fileDescriptor;
|
||||||
|
std::vector<buffer> _buffers;
|
||||||
|
|
||||||
|
uint32_t _pixelFormat;
|
||||||
|
int _width;
|
||||||
|
int _height;
|
||||||
|
const int _cropWidth;
|
||||||
|
const int _cropHeight;
|
||||||
|
const int _frameDecimation;
|
||||||
|
const int _pixelDecimation;
|
||||||
|
|
||||||
|
int _currentFrame;
|
||||||
|
|
||||||
|
ImageCallback _callback;
|
||||||
|
void * _callbackArg;
|
||||||
|
};
|
36
src/hyperion-v4l2/VideoStandardParameter.h
Normal file
36
src/hyperion-v4l2/VideoStandardParameter.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// getoptPlusPLus includes
|
||||||
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
|
|
||||||
|
using namespace vlofgren;
|
||||||
|
|
||||||
|
/// Data parameter for the video standard
|
||||||
|
typedef vlofgren::PODParameter<V4L2Grabber::VideoStandard> VideoStandardParameter;
|
||||||
|
|
||||||
|
namespace vlofgren {
|
||||||
|
/// Translates a string (as passed on the commandline) to a color standard
|
||||||
|
///
|
||||||
|
/// @param[in] s The string (as passed on the commandline)
|
||||||
|
/// @return The color standard
|
||||||
|
/// @throws Parameter::ParameterRejected If the string did not result in a video standard
|
||||||
|
template<>
|
||||||
|
V4L2Grabber::VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
||||||
|
{
|
||||||
|
QString input = QString::fromStdString(s).toLower();
|
||||||
|
|
||||||
|
if (input == "pal")
|
||||||
|
{
|
||||||
|
return V4L2Grabber::PAL;
|
||||||
|
}
|
||||||
|
else if (input == "ntsc")
|
||||||
|
{
|
||||||
|
return V4L2Grabber::NTSC;
|
||||||
|
}
|
||||||
|
else if (input == "no-change")
|
||||||
|
{
|
||||||
|
return V4L2Grabber::NO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE");
|
||||||
|
return V4L2Grabber::NO_CHANGE;
|
||||||
|
}
|
||||||
|
}
|
112
src/hyperion-v4l2/hyperion-v4l2.cpp
Normal file
112
src/hyperion-v4l2/hyperion-v4l2.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
// STL includes
|
||||||
|
#include <csignal>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
// QT includes
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
// getoptPlusPLus includes
|
||||||
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
|
|
||||||
|
// blackborder includes
|
||||||
|
#include <blackborder/BlackBorderProcessor.h>
|
||||||
|
|
||||||
|
// hyperion-v4l2 includes
|
||||||
|
#include "V4L2Grabber.h"
|
||||||
|
#include "ProtoConnection.h"
|
||||||
|
#include "VideoStandardParameter.h"
|
||||||
|
#include "ImageHandler.h"
|
||||||
|
|
||||||
|
using namespace vlofgren;
|
||||||
|
|
||||||
|
// save the image as screenshot
|
||||||
|
void saveScreenshot(void *, 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("screenshot.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// create the option parser and initialize all parameters
|
||||||
|
OptionsParser optionParser("V4L capture application for Hyperion");
|
||||||
|
ParameterSet & parameters = optionParser.getParameters();
|
||||||
|
|
||||||
|
StringParameter & argDevice = parameters.add<StringParameter> ('d', "device", "The device to use [default=/dev/video0]");
|
||||||
|
VideoStandardParameter & argVideoStandard = parameters.add<VideoStandardParameter>('v', "video-standard", "The used video standard. Valid values are PAL or NTSC (optional)");
|
||||||
|
IntParameter & argInput = parameters.add<IntParameter> (0x0, "input", "Input channel (optional)");
|
||||||
|
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Try to set the width of the video input (optional)");
|
||||||
|
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Try to set the height of the video input (optional)");
|
||||||
|
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]");
|
||||||
|
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]");
|
||||||
|
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=1]");
|
||||||
|
IntParameter & argFrameDecimation = parameters.add<IntParameter> ('f', "frame-decimator", "Decimation factor for the video frames [default=1]");
|
||||||
|
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
|
||||||
|
DoubleParameter & argSignalThreshold = parameters.add<DoubleParameter> ('t', "signal-threshold", "The signal threshold for detecting the presence of a signal. Value should be between 0.0 and 1.0.");
|
||||||
|
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
|
||||||
|
argDevice.setDefault("/dev/video0");
|
||||||
|
argVideoStandard.setDefault(V4L2Grabber::NO_CHANGE);
|
||||||
|
argInput.setDefault(-1);
|
||||||
|
argWidth.setDefault(-1);
|
||||||
|
argHeight.setDefault(-1);
|
||||||
|
argCropWidth.setDefault(0);
|
||||||
|
argCropHeight.setDefault(0);
|
||||||
|
argSizeDecimation.setDefault(1);
|
||||||
|
argFrameDecimation.setDefault(1);
|
||||||
|
argAddress.setDefault("127.0.0.1:19445");
|
||||||
|
argPriority.setDefault(800);
|
||||||
|
argSignalThreshold.setDefault(-1);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
V4L2Grabber grabber(
|
||||||
|
argDevice.getValue(),
|
||||||
|
argInput.getValue(),
|
||||||
|
argVideoStandard.getValue(),
|
||||||
|
argWidth.getValue(),
|
||||||
|
argHeight.getValue(),
|
||||||
|
std::max(0, argCropWidth.getValue()),
|
||||||
|
std::max(0, argCropHeight.getValue()),
|
||||||
|
std::max(1, argFrameDecimation.getValue()),
|
||||||
|
std::max(1, argSizeDecimation.getValue()));
|
||||||
|
|
||||||
|
grabber.start();
|
||||||
|
if (argScreenshot.isSet())
|
||||||
|
{
|
||||||
|
grabber.setCallback(&saveScreenshot, nullptr);
|
||||||
|
grabber.capture(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSignalThreshold.getValue(), argSkipReply.isSet());
|
||||||
|
grabber.setCallback(&ImageHandler::imageCallback, &handler);
|
||||||
|
grabber.capture();
|
||||||
|
}
|
||||||
|
grabber.stop();
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error & e)
|
||||||
|
{
|
||||||
|
// An error occured. Display error and quit
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -3,9 +3,11 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
// Hyperion includes
|
// Hyperion includes
|
||||||
#include <hyperion/BlackBorderDetector.h>
|
|
||||||
#include <utils/ColorRgb.h>
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
// Blackborder includes
|
||||||
|
#include <blackborder/BlackBorderDetector.h>
|
||||||
|
|
||||||
using namespace hyperion;
|
using namespace hyperion;
|
||||||
|
|
||||||
ColorRgb randomColor()
|
ColorRgb randomColor()
|
||||||
@ -41,7 +43,7 @@ int TC_NO_BORDER()
|
|||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
BlackBorderDetector detector;
|
BlackBorderDetector detector(3);
|
||||||
|
|
||||||
{
|
{
|
||||||
Image<ColorRgb> image = createImage(64, 64, 0, 0);
|
Image<ColorRgb> image = createImage(64, 64, 0, 0);
|
||||||
@ -60,7 +62,7 @@ int TC_TOP_BORDER()
|
|||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
BlackBorderDetector detector;
|
BlackBorderDetector detector(3);
|
||||||
|
|
||||||
{
|
{
|
||||||
Image<ColorRgb> image = createImage(64, 64, 12, 0);
|
Image<ColorRgb> image = createImage(64, 64, 12, 0);
|
||||||
@ -79,7 +81,7 @@ int TC_LEFT_BORDER()
|
|||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
BlackBorderDetector detector;
|
BlackBorderDetector detector(3);
|
||||||
|
|
||||||
{
|
{
|
||||||
Image<ColorRgb> image = createImage(64, 64, 0, 12);
|
Image<ColorRgb> image = createImage(64, 64, 0, 12);
|
||||||
@ -98,7 +100,7 @@ int TC_DUAL_BORDER()
|
|||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
BlackBorderDetector detector;
|
BlackBorderDetector detector(3);
|
||||||
|
|
||||||
{
|
{
|
||||||
Image<ColorRgb> image = createImage(64, 64, 12, 12);
|
Image<ColorRgb> image = createImage(64, 64, 12, 12);
|
||||||
@ -116,7 +118,7 @@ int TC_UNKNOWN_BORDER()
|
|||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
BlackBorderDetector detector;
|
BlackBorderDetector detector(3);
|
||||||
|
|
||||||
{
|
{
|
||||||
Image<ColorRgb> image = createImage(64, 64, 30, 30);
|
Image<ColorRgb> image = createImage(64, 64, 30, 30);
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
#include <utils/Image.h>
|
#include <utils/Image.h>
|
||||||
#include <utils/ColorRgb.h>
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
// Local-Hyperion includes
|
// Blackborder includes
|
||||||
#include "hyperion/BlackBorderProcessor.h"
|
#include "blackborder/BlackBorderProcessor.h"
|
||||||
|
|
||||||
using namespace hyperion;
|
using namespace hyperion;
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ int main()
|
|||||||
unsigned borderCnt = 50;
|
unsigned borderCnt = 50;
|
||||||
unsigned blurCnt = 0;
|
unsigned blurCnt = 0;
|
||||||
|
|
||||||
BlackBorderProcessor processor(unknownCnt, borderCnt, blurCnt);
|
BlackBorderProcessor processor(unknownCnt, borderCnt, blurCnt, 3);
|
||||||
|
|
||||||
// Start with 'no border' detection
|
// Start with 'no border' detection
|
||||||
Image<ColorRgb> noBorderImage = createImage(64, 64, 0, 0);
|
Image<ColorRgb> noBorderImage = createImage(64, 64, 0, 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user