2014-01-25 17:35:06 +01:00
// STL includes
# include <csignal>
# include <iomanip>
2014-01-28 00:39:04 +01:00
# include <clocale>
2014-01-25 17:35:06 +01:00
// QT includes
2014-02-19 21:52:37 +01:00
# include <QCoreApplication>
2014-01-25 17:35:06 +01:00
2014-01-26 15:51:02 +01:00
// blackborder includes
# include <blackborder/BlackBorderProcessor.h>
2014-02-07 21:11:50 +01:00
// grabber includes
# include "grabber/V4L2Grabber.h"
2018-12-30 22:07:53 +01:00
// flatbuf includes
# include <flatbufserver/FlatBufferConnection.h>
2014-11-21 21:24:33 +01:00
2014-01-26 15:51:02 +01:00
// hyperion-v4l2 includes
2014-02-19 21:52:37 +01:00
# include "ScreenshotHandler.h"
2014-01-25 17:35:06 +01:00
2016-03-10 12:01:10 +01:00
# include "HyperionConfig.h"
2016-08-28 15:10:43 +02:00
# include <commandline/Parser.h>
2016-03-10 12:01:10 +01:00
2018-12-30 22:07:53 +01:00
// ssdp discover
# include <ssdp/SSDPDiscover.h>
2016-08-28 15:10:43 +02:00
using namespace commandline ;
2014-01-25 17:35:06 +01:00
// save the image as screenshot
2016-08-28 15:10:43 +02:00
void saveScreenshot ( QString filename , const Image < ColorRgb > & image )
2014-01-25 17:35:06 +01:00
{
2016-05-26 23:44:27 +02:00
// store as PNG
QImage pngImage ( ( const uint8_t * ) image . memptr ( ) , image . width ( ) , image . height ( ) , 3 * image . width ( ) , QImage : : Format_RGB888 ) ;
2016-08-28 15:10:43 +02:00
pngImage . save ( filename ) ;
2014-01-25 17:35:06 +01:00
}
int main ( int argc , char * * argv )
{
2016-12-16 19:48:43 +01:00
Logger * log = Logger : : getInstance ( " V4L2GRABBER " ) ;
2016-12-18 00:47:53 +01:00
Logger : : setLogLevel ( Logger : : WARNING ) ;
2016-06-24 23:22:31 +02:00
std : : cout
< < " hyperion-v4l2: " < < std : : endl
< < " \t Version : " < < HYPERION_VERSION < < " ( " < < HYPERION_BUILD_ID < < " ) " < < std : : endl
< < " \t build time: " < < __DATE__ < < " " < < __TIME__ < < std : : endl ;
2016-05-26 23:44:27 +02:00
QCoreApplication app ( argc , argv ) ;
// force the locale
setlocale ( LC_ALL , " C " ) ;
QLocale : : setDefault ( QLocale : : c ( ) ) ;
// register the image type to use in signals
qRegisterMetaType < Image < ColorRgb > > ( " Image<ColorRgb> " ) ;
try
{
// create the option parser and initialize all parameters
2018-12-30 22:07:53 +01:00
Parser parser ( " V4L capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used. " ) ;
2016-08-28 15:10:43 +02:00
2018-12-28 18:12:45 +01:00
Option & argDevice = parser . add < Option > ( ' d ' , " device " , " The device to use, can be /dev/video0 [default: %1 (auto detected)] " , " auto " ) ;
2017-10-04 21:23:45 +02:00
SwitchOption < VideoStandard > & argVideoStandard = parser . add < SwitchOption < VideoStandard > > ( ' v ' , " video-standard " , " The used video standard. Valid values are PAL, NTSC, SECAM or no-change. [default: %1] " , " no-change " ) ;
2016-09-17 00:40:29 +02:00
SwitchOption < PixelFormat > & argPixelFormat = parser . add < SwitchOption < PixelFormat > > ( 0x0 , " pixel-format " , " The use pixel format. Valid values are YUYV, UYVY, RGB32 or no-change. [default: %1] " , " no-change " ) ;
IntOption & argCropWidth = parser . add < IntOption > ( 0x0 , " crop-width " , " Number of pixels to crop from the left and right sides of the picture before decimation [default: %1] " , " 0 " ) ;
IntOption & argCropHeight = parser . add < IntOption > ( 0x0 , " crop-height " , " Number of pixels to crop from the top and the bottom of the picture before decimation [default: %1] " , " 0 " ) ;
IntOption & argCropLeft = parser . add < IntOption > ( 0x0 , " crop-left " , " Number of pixels to crop from the left of the picture before decimation (overrides --crop-width) " ) ;
IntOption & argCropRight = parser . add < IntOption > ( 0x0 , " crop-right " , " Number of pixels to crop from the right of the picture before decimation (overrides --crop-width) " ) ;
IntOption & argCropTop = parser . add < IntOption > ( 0x0 , " crop-top " , " Number of pixels to crop from the top of the picture before decimation (overrides --crop-height) " ) ;
IntOption & argCropBottom = parser . add < IntOption > ( 0x0 , " crop-bottom " , " Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height) " ) ;
2018-12-31 15:48:29 +01:00
IntOption & argSizeDecimation = parser . add < IntOption > ( ' s ' , " size-decimator " , " Decimation factor for the output size [default=%1] " , " 6 " , 1 ) ;
2016-09-17 00:40:29 +02:00
BooleanOption & argScreenshot = parser . add < BooleanOption > ( 0x0 , " screenshot " , " Take a single screenshot, save it to file and quit " ) ;
2016-12-16 19:48:43 +01:00
2017-03-15 20:33:11 +01:00
BooleanOption & argSignalDetection = parser . add < BooleanOption > ( ' s ' , " signal-detection-disabled " , " disable signal detection " ) ;
2016-09-17 00:40:29 +02:00
DoubleOption & argSignalThreshold = parser . add < DoubleOption > ( ' t ' , " signal-threshold " , " The signal threshold for detecting the presence of a signal. Value should be between 0.0 and 1.0. " , QString ( ) , 0.0 , 1.0 ) ;
DoubleOption & argRedSignalThreshold = parser . add < DoubleOption > ( 0x0 , " red-threshold " , " The red signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold) " ) ;
DoubleOption & argGreenSignalThreshold = parser . add < DoubleOption > ( 0x0 , " green-threshold " , " The green signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold) " ) ;
DoubleOption & argBlueSignalThreshold = parser . add < DoubleOption > ( 0x0 , " blue-threshold " , " The blue signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold) " ) ;
2016-12-16 19:48:43 +01:00
DoubleOption & argSignalHorizontalMin = parser . add < DoubleOption > ( 0x0 , " signal-horizontal-min " , " area for signal detection - horizontal minimum offset value. Values between 0.0 and 1.0 " ) ;
DoubleOption & argSignalVerticalMin = parser . add < DoubleOption > ( 0x0 , " signal-vertical-min " , " area for signal detection - vertical minimum offset value. Values between 0.0 and 1.0 " ) ;
DoubleOption & argSignalHorizontalMax = parser . add < DoubleOption > ( 0x0 , " signal-horizontal-max " , " area for signal detection - horizontal maximum offset value. Values between 0.0 and 1.0 " ) ;
DoubleOption & argSignalVerticalMax = parser . add < DoubleOption > ( 0x0 , " signal-vertical-max " , " area for signal detection - vertical maximum offset value. Values between 0.0 and 1.0 " ) ;
2016-09-17 00:40:29 +02:00
BooleanOption & arg3DSBS = parser . add < BooleanOption > ( 0x0 , " 3DSBS " , " Interpret the incoming video stream as 3D side-by-side " ) ;
BooleanOption & arg3DTAB = parser . add < BooleanOption > ( 0x0 , " 3DTAB " , " Interpret the incoming video stream as 3D top-and-bottom " ) ;
2019-02-05 19:55:48 +01:00
Option & argAddress = parser . add < Option > ( ' a ' , " address " , " Set the address of the hyperion server [default: %1] " , " 127.0.0.1:19400 " ) ;
2017-01-17 21:53:35 +01:00
IntOption & argPriority = parser . add < IntOption > ( ' p ' , " priority " , " Use the provided priority channel (suggested 100-199) [default: %1] " , " 150 " ) ;
2016-09-17 00:40:29 +02:00
BooleanOption & argSkipReply = parser . add < BooleanOption > ( 0x0 , " skip-reply " , " Do not receive and check reply messages from Hyperion " ) ;
BooleanOption & argHelp = parser . add < BooleanOption > ( ' h ' , " help " , " Show this help message and exit " ) ;
2016-08-28 15:10:43 +02:00
argVideoStandard . addSwitch ( " pal " , VIDEOSTANDARD_PAL ) ;
argVideoStandard . addSwitch ( " ntsc " , VIDEOSTANDARD_NTSC ) ;
2017-10-04 21:23:45 +02:00
argVideoStandard . addSwitch ( " secam " , VIDEOSTANDARD_SECAM ) ;
2016-08-28 15:10:43 +02:00
argVideoStandard . addSwitch ( " no-change " , VIDEOSTANDARD_NO_CHANGE ) ;
argPixelFormat . addSwitch ( " yuyv " , PIXELFORMAT_YUYV ) ;
argPixelFormat . addSwitch ( " uyvy " , PIXELFORMAT_UYVY ) ;
argPixelFormat . addSwitch ( " rgb32 " , PIXELFORMAT_RGB32 ) ;
argPixelFormat . addSwitch ( " no-change " , PIXELFORMAT_NO_CHANGE ) ;
2016-05-26 23:44:27 +02:00
// parse all options
2016-08-28 15:10:43 +02:00
parser . process ( app ) ;
2016-05-26 23:44:27 +02:00
// check if we need to display the usage. exit if we do.
2016-08-28 15:10:43 +02:00
if ( parser . isSet ( argHelp ) )
2016-05-26 23:44:27 +02:00
{
2016-08-28 15:10:43 +02:00
parser . showHelp ( 0 ) ;
2016-05-26 23:44:27 +02:00
}
// initialize the grabber
V4L2Grabber grabber (
2017-03-04 22:17:42 +01:00
argDevice . value ( parser ) ,
2016-08-28 15:10:43 +02:00
argVideoStandard . switchValue ( parser ) ,
argPixelFormat . switchValue ( parser ) ,
std : : max ( 1 , argSizeDecimation . getInt ( parser ) ) ) ;
2016-05-26 23:44:27 +02:00
// set signal detection
2017-03-15 20:33:11 +01:00
grabber . setSignalDetectionEnable ( ! parser . isSet ( argSignalDetection ) ) ;
2016-05-26 23:44:27 +02:00
grabber . setSignalThreshold (
2016-12-16 19:48:43 +01:00
std : : min ( 1.0 , std : : max ( 0.0 , parser . isSet ( argRedSignalThreshold ) ? argRedSignalThreshold . getDouble ( parser ) : argSignalThreshold . getDouble ( parser ) ) ) ,
2016-08-28 15:10:43 +02:00
std : : min ( 1.0 , std : : max ( 0.0 , parser . isSet ( argGreenSignalThreshold ) ? argGreenSignalThreshold . getDouble ( parser ) : argSignalThreshold . getDouble ( parser ) ) ) ,
2016-12-16 19:48:43 +01:00
std : : min ( 1.0 , std : : max ( 0.0 , parser . isSet ( argBlueSignalThreshold ) ? argBlueSignalThreshold . getDouble ( parser ) : argSignalThreshold . getDouble ( parser ) ) ) ,
2016-05-26 23:44:27 +02:00
50 ) ;
// set cropping values
grabber . setCropping (
2016-12-16 19:48:43 +01:00
parser . isSet ( argCropLeft ) ? argCropLeft . getInt ( parser ) : argCropWidth . getInt ( parser ) ,
parser . isSet ( argCropRight ) ? argCropRight . getInt ( parser ) : argCropWidth . getInt ( parser ) ,
parser . isSet ( argCropTop ) ? argCropTop . getInt ( parser ) : argCropHeight . getInt ( parser ) ,
2016-08-28 15:10:43 +02:00
parser . isSet ( argCropBottom ) ? argCropBottom . getInt ( parser ) : argCropHeight . getInt ( parser ) ) ;
2016-05-26 23:44:27 +02:00
2016-12-16 19:48:43 +01:00
bool signalAreaOptsOk = true ;
if ( parser . isSet ( argSignalHorizontalMin ) ! = parser . isSet ( argSignalVerticalMin ) )
{
signalAreaOptsOk = false ;
}
if ( parser . isSet ( argSignalHorizontalMin ) ! = parser . isSet ( argSignalHorizontalMax ) )
{
signalAreaOptsOk = false ;
}
if ( parser . isSet ( argSignalHorizontalMin ) ! = parser . isSet ( argSignalVerticalMax ) )
{
signalAreaOptsOk = false ;
}
if ( ! signalAreaOptsOk )
{
Error ( log , " aborting, because --signal-[vertical|horizontal]-[min|max] options must be used together " ) ;
return 1 ;
}
double x_frac_min = argSignalHorizontalMin . getDouble ( parser ) ;
double y_frac_min = argSignalVerticalMin . getDouble ( parser ) ;
double x_frac_max = argSignalHorizontalMax . getDouble ( parser ) ;
double y_frac_max = argSignalVerticalMax . getDouble ( parser ) ;
if ( x_frac_min < 0.0 | | y_frac_min < 0.0 | | x_frac_max < 0.0 | | y_frac_max < 0.0 | | x_frac_min > 1.0 | | y_frac_min > 1.0 | | x_frac_max > 1.0 | | y_frac_max > 1.0 )
{
Error ( log , " aborting, because --signal-[vertical|horizontal]-[min|max] values have to be between 0.0 and 1.0 " ) ;
return 1 ;
}
if ( parser . isSet ( argSignalHorizontalMin ) )
{
grabber . setSignalDetectionOffset ( x_frac_min , y_frac_min , x_frac_max , y_frac_max ) ;
}
2016-05-26 23:44:27 +02:00
// set 3D mode if applicable
2016-08-28 15:10:43 +02:00
if ( parser . isSet ( arg3DSBS ) )
2016-05-26 23:44:27 +02:00
{
2017-08-04 23:08:15 +02:00
grabber . setVideoMode ( VIDEO_3DSBS ) ;
2016-05-26 23:44:27 +02:00
}
2016-08-28 15:10:43 +02:00
else if ( parser . isSet ( arg3DTAB ) )
2016-05-26 23:44:27 +02:00
{
2017-08-04 23:08:15 +02:00
grabber . setVideoMode ( VIDEO_3DTAB ) ;
2016-05-26 23:44:27 +02:00
}
// run the grabber
2016-08-28 15:10:43 +02:00
if ( parser . isSet ( argScreenshot ) )
2016-05-26 23:44:27 +02:00
{
2016-12-16 19:48:43 +01:00
const QRectF signalDetectionOffset = grabber . getSignalDetectionOffset ( ) ;
ScreenshotHandler handler ( " screenshot.png " , signalDetectionOffset ) ;
2016-05-26 23:44:27 +02:00
QObject : : connect ( & grabber , SIGNAL ( newFrame ( Image < ColorRgb > ) ) , & handler , SLOT ( receiveImage ( Image < ColorRgb > ) ) ) ;
grabber . start ( ) ;
QCoreApplication : : exec ( ) ;
grabber . stop ( ) ;
}
else
{
2018-12-30 22:07:53 +01:00
// server searching by ssdp
QString address ;
if ( parser . isSet ( argAddress ) )
{
address = argAddress . value ( parser ) ;
}
else
{
SSDPDiscover discover ;
address = discover . getFirstService ( STY_FLATBUFSERVER ) ;
if ( address . isEmpty ( ) )
{
address = argAddress . value ( parser ) ;
}
}
// Create the Flatbuf-connection
FlatBufferConnection flatbuf ( " V4L2 Standalone " , address , argPriority . getInt ( parser ) , parser . isSet ( argSkipReply ) ) ;
// Connect the screen capturing to flatbuf connection processing
QObject : : connect ( & grabber , SIGNAL ( newFrame ( const Image < ColorRgb > & ) ) , & flatbuf , SLOT ( setImage ( Image < ColorRgb > ) ) ) ;
2016-07-14 23:40:10 +02:00
if ( grabber . start ( ) )
QCoreApplication : : exec ( ) ;
2016-05-26 23:44:27 +02:00
grabber . stop ( ) ;
}
}
catch ( const std : : runtime_error & e )
{
// An error occured. Display error and quit
2016-12-16 19:48:43 +01:00
Error ( log , " %s " , e . what ( ) ) ;
2016-05-26 23:44:27 +02:00
return 1 ;
}
return 0 ;
2014-01-25 17:35:06 +01:00
}