mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Merge branch 'add_x11'
Former-commit-id: d80efce3aa0413e4f63e9171dab438d03db9021c
This commit is contained in:
commit
f6ce35813a
@ -11,7 +11,7 @@ option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ON)
|
|||||||
message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX})
|
message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX})
|
||||||
|
|
||||||
option(ENABLE_SPIDEV "Enable the SPIDEV device" ON)
|
option(ENABLE_SPIDEV "Enable the SPIDEV device" ON)
|
||||||
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
|
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
|
||||||
|
|
||||||
option(ENABLE_WS2812BPWM "Enable the WS2812b-PWM device" OFF)
|
option(ENABLE_WS2812BPWM "Enable the WS2812b-PWM device" OFF)
|
||||||
message(STATUS "ENABLE_WS2812BPWM = " ${ENABLE_WS2812BPWM})
|
message(STATUS "ENABLE_WS2812BPWM = " ${ENABLE_WS2812BPWM})
|
||||||
@ -19,6 +19,9 @@ message(STATUS "ENABLE_WS2812BPWM = " ${ENABLE_WS2812BPWM})
|
|||||||
option(ENABLE_V4L2 "Enable the V4L2 grabber" ON)
|
option(ENABLE_V4L2 "Enable the V4L2 grabber" ON)
|
||||||
message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
|
message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
|
||||||
|
|
||||||
|
option(ENABLE_X11 "Enable the X11 grabber" ON)
|
||||||
|
message(STATUS "ENABLE_X11 = " ${ENABLE_X11})
|
||||||
|
|
||||||
option(ENABLE_TINKERFORGE "Enable the TINKERFORGE device" ON)
|
option(ENABLE_TINKERFORGE "Enable the TINKERFORGE device" ON)
|
||||||
message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE})
|
message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE})
|
||||||
|
|
||||||
@ -54,8 +57,8 @@ 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 "Debug")
|
||||||
set(CMAKE_BUILD_TYPE "Release")
|
#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")
|
||||||
|
268
dependencies/build/getoptPlusPlus/getoptpp.cc
vendored
268
dependencies/build/getoptPlusPlus/getoptpp.cc
vendored
@ -17,6 +17,9 @@
|
|||||||
#include "getoptpp.h"
|
#include "getoptpp.h"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -34,107 +37,124 @@ OptionsParser::OptionsParser(const char* programDesc) : fprogramDesc(programDesc
|
|||||||
OptionsParser::~OptionsParser() {}
|
OptionsParser::~OptionsParser() {}
|
||||||
|
|
||||||
ParameterSet& OptionsParser::getParameters() {
|
ParameterSet& OptionsParser::getParameters() {
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptionsParser::parse(int argc, const char* argv[]) throw(runtime_error)
|
void OptionsParser::parse(int argc, const char* argv[]) throw(runtime_error)
|
||||||
{
|
{
|
||||||
argv0 = argv[0];
|
argv0 = argv[0];
|
||||||
|
|
||||||
if(argc == 1) return;
|
if(argc == 1) return;
|
||||||
|
|
||||||
vector<string> v(&argv[1], &argv[argc]);
|
vector<string> v(&argv[1], &argv[argc]);
|
||||||
|
|
||||||
ParserState state(/* *this,*/ v);
|
ParserState state(/* *this,*/ v);
|
||||||
|
|
||||||
for(; !state.end(); state.advance()) {
|
for(; !state.end(); state.advance()) {
|
||||||
|
|
||||||
std::list<Parameter*>::iterator i;
|
std::list<Parameter*>::iterator i;
|
||||||
|
|
||||||
for(i = parameters.parameters.begin();
|
for(i = parameters.parameters.begin();
|
||||||
i != parameters.parameters.end(); i++)
|
i != parameters.parameters.end(); i++)
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
n = (*i)->receive(state);
|
n = (*i)->receive(state);
|
||||||
}
|
}
|
||||||
catch(Parameter::ExpectedArgument &)
|
catch(Parameter::ExpectedArgument &)
|
||||||
{
|
{
|
||||||
throw Parameter::ExpectedArgument(state.get() + ": expected an argument");
|
throw Parameter::ExpectedArgument(state.get() + ": expected an argument");
|
||||||
}
|
}
|
||||||
catch(Parameter::UnexpectedArgument &)
|
catch(Parameter::UnexpectedArgument &)
|
||||||
{
|
{
|
||||||
throw Parameter::UnexpectedArgument(state.get() + ": did not expect an argument");
|
throw Parameter::UnexpectedArgument(state.get() + ": did not expect an argument");
|
||||||
}
|
}
|
||||||
catch(Switchable::SwitchingError &)
|
catch(Switchable::SwitchingError &)
|
||||||
{
|
{
|
||||||
throw Parameter::ParameterRejected(state.get() + ": parameter already set");
|
throw Parameter::ParameterRejected(state.get() + ": parameter already set");
|
||||||
}
|
}
|
||||||
catch(Parameter::ParameterRejected & pr) {
|
catch(Parameter::ParameterRejected & pr) {
|
||||||
std::string what = pr.what();
|
std::string what = pr.what();
|
||||||
if(what.length())
|
if(what.length())
|
||||||
{
|
{
|
||||||
throw Parameter::ParameterRejected(state.get() + ": " + what);
|
throw Parameter::ParameterRejected(state.get() + ": " + what);
|
||||||
}
|
}
|
||||||
throw Parameter::ParameterRejected(state.get() + " (unspecified error)");
|
throw Parameter::ParameterRejected(state.get() + " (unspecified error)");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 1; j < n; ++j)
|
for (int j = 1; j < n; ++j)
|
||||||
{
|
{
|
||||||
state.advance();
|
state.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(n != 0)
|
if(n != 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i == parameters.parameters.end()) {
|
if(i == parameters.parameters.end()) {
|
||||||
std::string file = state.get();
|
std::string file = state.get();
|
||||||
if(file == "--") {
|
if(file == "--") {
|
||||||
state.advance();
|
state.advance();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(file.at(0) == '-')
|
else if(file.at(0) == '-')
|
||||||
throw Parameter::ParameterRejected(string("Bad parameter: ") + file);
|
throw Parameter::ParameterRejected(string("Bad parameter: ") + file);
|
||||||
else files.push_back(state.get());
|
else files.push_back(state.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!state.end()) for(; !state.end(); state.advance()) {
|
if(!state.end()) for(; !state.end(); state.advance()) {
|
||||||
files.push_back(state.get());
|
files.push_back(state.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptionsParser::usage() const {
|
void OptionsParser::usage() const {
|
||||||
cerr << fprogramDesc << endl;
|
cerr << fprogramDesc << endl;
|
||||||
cerr << "Build time: " << __DATE__ << " " << __TIME__ << endl << endl;
|
cerr << "Build time: " << __DATE__ << " " << __TIME__ << endl << endl;
|
||||||
cerr << "Usage: " << programName() << " [OPTIONS]" << endl << endl;
|
cerr << "Usage: " << programName() << " [OPTIONS]" << endl << endl;
|
||||||
|
|
||||||
cerr << "Parameters: " << endl;
|
cerr << "Parameters: " << endl;
|
||||||
|
|
||||||
std::list<Parameter*>::const_iterator i;
|
int totalWidth = 80;
|
||||||
for(i = parameters.parameters.begin();
|
int usageWidth = 33;
|
||||||
i != parameters.parameters.end(); i++)
|
|
||||||
{
|
|
||||||
cerr.width(33);
|
|
||||||
cerr << std::left << " " + (*i)->usageLine();
|
|
||||||
|
|
||||||
cerr.width(40);
|
// read total width from the terminal
|
||||||
cerr << std::left << (*i)->description() << endl;
|
struct winsize w;
|
||||||
|
if (ioctl(0, TIOCGWINSZ, &w) == 0)
|
||||||
|
{
|
||||||
|
if (w.ws_col > totalWidth)
|
||||||
|
totalWidth = w.ws_col;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
std::list<Parameter*>::const_iterator i;
|
||||||
|
for(i = parameters.parameters.begin();
|
||||||
|
i != parameters.parameters.end(); i++)
|
||||||
|
{
|
||||||
|
cerr.width(usageWidth);
|
||||||
|
cerr << std::left << " " + (*i)->usageLine();
|
||||||
|
|
||||||
|
std::string description = (*i)->description();
|
||||||
|
while (int(description.length()) > (totalWidth - usageWidth))
|
||||||
|
{
|
||||||
|
size_t pos = description.find_last_of(' ', totalWidth - usageWidth);
|
||||||
|
cerr << description.substr(0, pos) << std::endl << std::string(usageWidth - 1, ' ');
|
||||||
|
description = description.substr(pos);
|
||||||
|
}
|
||||||
|
cerr << description << endl;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const vector<string>& OptionsParser::getFiles() const {
|
const vector<string>& OptionsParser::getFiles() const {
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string& OptionsParser::programName() const {
|
const string& OptionsParser::programName() const {
|
||||||
return argv0;
|
return argv0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -144,15 +164,15 @@ const string& OptionsParser::programName() const {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
ParameterSet::ParameterSet(const ParameterSet& ps) {
|
ParameterSet::ParameterSet(const ParameterSet& ps) {
|
||||||
throw new runtime_error("ParameterSet not copyable");
|
throw new runtime_error("ParameterSet not copyable");
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterSet::~ParameterSet() {
|
ParameterSet::~ParameterSet() {
|
||||||
for(std::list<Parameter*>::iterator i = parameters.begin();
|
for(std::list<Parameter*>::iterator i = parameters.begin();
|
||||||
i != parameters.end(); i++)
|
i != parameters.end(); i++)
|
||||||
{
|
{
|
||||||
delete *i;
|
delete *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,18 +181,18 @@ ParameterSet::~ParameterSet() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Parameter& ParameterSet::operator[](char c) const {
|
Parameter& ParameterSet::operator[](char c) const {
|
||||||
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
|
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
|
||||||
if((*i)->shortOption() == c) return *(*i);
|
if((*i)->shortOption() == c) return *(*i);
|
||||||
}
|
}
|
||||||
throw out_of_range("ParameterSet["+string(&c)+string("]"));
|
throw out_of_range("ParameterSet["+string(&c)+string("]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Parameter& ParameterSet::operator[](const string& param) const {
|
Parameter& ParameterSet::operator[](const string& param) const {
|
||||||
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
|
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
|
||||||
if((*i)->longOption() == param) return *(*i);
|
if((*i)->longOption() == param) return *(*i);
|
||||||
}
|
}
|
||||||
throw out_of_range("ParameterSet["+param+"]");
|
throw out_of_range("ParameterSet["+param+"]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -186,29 +206,29 @@ Parameter& ParameterSet::operator[](const string& param) const {
|
|||||||
|
|
||||||
|
|
||||||
ParserState::ParserState(/*OptionsParser &opts, */vector<string>& args) :
|
ParserState::ParserState(/*OptionsParser &opts, */vector<string>& args) :
|
||||||
/*opts(opts),*/ arguments(args), iterator(args.begin())
|
/*opts(opts),*/ arguments(args), iterator(args.begin())
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const string ParserState::peek() const {
|
const string ParserState::peek() const {
|
||||||
vector<string>::const_iterator next = iterator+1;
|
vector<string>::const_iterator next = iterator+1;
|
||||||
if(next != arguments.end()) return *next;
|
if(next != arguments.end()) return *next;
|
||||||
else return "";
|
else return "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const string ParserState::get() const {
|
const string ParserState::get() const {
|
||||||
if(!end()) return *iterator;
|
if(!end()) return *iterator;
|
||||||
else return "";
|
else return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserState::advance() {
|
void ParserState::advance() {
|
||||||
iterator++;
|
iterator++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParserState::end() const {
|
bool ParserState::end() const {
|
||||||
return iterator == arguments.end();
|
return iterator == arguments.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -222,7 +242,7 @@ bool ParserState::end() const {
|
|||||||
|
|
||||||
|
|
||||||
Parameter::Parameter(char shortOption, const std::string & longOption, const std::string & description) :
|
Parameter::Parameter(char shortOption, const std::string & longOption, const std::string & description) :
|
||||||
fshortOption(shortOption), flongOption(longOption), fdescription(description)
|
fshortOption(shortOption), flongOption(longOption), fdescription(description)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -250,22 +270,22 @@ MultiSwitchable::~MultiSwitchable() {}
|
|||||||
|
|
||||||
|
|
||||||
void UniquelySwitchable::set() throw (Switchable::SwitchingError) {
|
void UniquelySwitchable::set() throw (Switchable::SwitchingError) {
|
||||||
if(UniquelySwitchable::isSet()) throw Switchable::SwitchingError();
|
if(UniquelySwitchable::isSet()) throw Switchable::SwitchingError();
|
||||||
fset = true;
|
fset = true;
|
||||||
}
|
}
|
||||||
UniquelySwitchable::~UniquelySwitchable() {}
|
UniquelySwitchable::~UniquelySwitchable() {}
|
||||||
|
|
||||||
|
|
||||||
PresettableUniquelySwitchable::~PresettableUniquelySwitchable() {}
|
PresettableUniquelySwitchable::~PresettableUniquelySwitchable() {}
|
||||||
bool PresettableUniquelySwitchable::isSet() const {
|
bool PresettableUniquelySwitchable::isSet() const {
|
||||||
return UniquelySwitchable::isSet() || fpreset.isSet();
|
return UniquelySwitchable::isSet() || fpreset.isSet();
|
||||||
}
|
}
|
||||||
void PresettableUniquelySwitchable::set() throw (Switchable::SwitchingError)
|
void PresettableUniquelySwitchable::set() throw (Switchable::SwitchingError)
|
||||||
{
|
{
|
||||||
UniquelySwitchable::set();
|
UniquelySwitchable::set();
|
||||||
}
|
}
|
||||||
void PresettableUniquelySwitchable::preset() {
|
void PresettableUniquelySwitchable::preset() {
|
||||||
fpreset.set();
|
fpreset.set();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -279,58 +299,58 @@ void PresettableUniquelySwitchable::preset() {
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
|
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
|
||||||
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
|
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
int PODParameter<int>::validate(const string &s) throw(Parameter::ParameterRejected)
|
int PODParameter<int>::validate(const string &s) throw(Parameter::ParameterRejected)
|
||||||
{
|
{
|
||||||
// This is sadly necessary for strto*-functions to operate on
|
// This is sadly necessary for strto*-functions to operate on
|
||||||
// const char*. The function doesn't write to the memory, though,
|
// const char*. The function doesn't write to the memory, though,
|
||||||
// so it's quite safe.
|
// so it's quite safe.
|
||||||
|
|
||||||
char* cstr = const_cast<char*>(s.c_str());
|
char* cstr = const_cast<char*>(s.c_str());
|
||||||
if(*cstr == '\0') throw ParameterRejected("No argument given");
|
if(*cstr == '\0') throw ParameterRejected("No argument given");
|
||||||
|
|
||||||
long l = strtol(cstr, &cstr, 10);
|
long l = strtol(cstr, &cstr, 10);
|
||||||
if(*cstr != '\0') throw ParameterRejected("Expected int");
|
if(*cstr != '\0') throw ParameterRejected("Expected int");
|
||||||
|
|
||||||
if(l > INT_MAX || l < INT_MIN) {
|
if(l > INT_MAX || l < INT_MIN) {
|
||||||
throw ParameterRejected("Expected int");
|
throw ParameterRejected("Expected int");
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
long PODParameter<long>::validate(const string &s) throw(Parameter::ParameterRejected)
|
long PODParameter<long>::validate(const string &s) throw(Parameter::ParameterRejected)
|
||||||
{
|
{
|
||||||
char* cstr = const_cast<char*>(s.c_str());
|
char* cstr = const_cast<char*>(s.c_str());
|
||||||
if(*cstr == '\0') throw ParameterRejected("No argument given");
|
if(*cstr == '\0') throw ParameterRejected("No argument given");
|
||||||
|
|
||||||
long l = strtol(cstr, &cstr, 10);
|
long l = strtol(cstr, &cstr, 10);
|
||||||
if(*cstr != '\0') throw ParameterRejected("Expected long");
|
if(*cstr != '\0') throw ParameterRejected("Expected long");
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
double PODParameter<double>::validate(const string &s) throw(Parameter::ParameterRejected)
|
double PODParameter<double>::validate(const string &s) throw(Parameter::ParameterRejected)
|
||||||
{
|
{
|
||||||
char* cstr = const_cast<char*>(s.c_str());
|
char* cstr = const_cast<char*>(s.c_str());
|
||||||
if(*cstr == '\0') throw ParameterRejected("No argument given");
|
if(*cstr == '\0') throw ParameterRejected("No argument given");
|
||||||
|
|
||||||
double d = strtod(cstr, &cstr);
|
double d = strtod(cstr, &cstr);
|
||||||
if(*cstr != '\0') throw ParameterRejected("Expected double");
|
if(*cstr != '\0') throw ParameterRejected("Expected double");
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
string PODParameter<string>::validate(const string &s) throw(Parameter::ParameterRejected)
|
string PODParameter<string>::validate(const string &s) throw(Parameter::ParameterRejected)
|
||||||
{
|
{
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,119 +11,115 @@
|
|||||||
// util includes
|
// util includes
|
||||||
#include <utils/Image.h>
|
#include <utils/Image.h>
|
||||||
#include <utils/ColorRgb.h>
|
#include <utils/ColorRgb.h>
|
||||||
|
#include <utils/PixelFormat.h>
|
||||||
#include <utils/VideoMode.h>
|
#include <utils/VideoMode.h>
|
||||||
|
#include <utils/ImageResampler.h>
|
||||||
|
|
||||||
// grabber includes
|
// grabber includes
|
||||||
#include <grabber/VideoStandard.h>
|
#include <grabber/VideoStandard.h>
|
||||||
#include <grabber/PixelFormat.h>
|
|
||||||
|
|
||||||
/// Capture class for V4L2 devices
|
/// Capture class for V4L2 devices
|
||||||
///
|
///
|
||||||
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
|
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
|
||||||
class V4L2Grabber : public QObject
|
class V4L2Grabber : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
V4L2Grabber(const std::string & device,
|
V4L2Grabber(const std::string & device,
|
||||||
int input,
|
int input,
|
||||||
VideoStandard videoStandard, PixelFormat pixelFormat,
|
VideoStandard videoStandard, PixelFormat pixelFormat,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int frameDecimation,
|
int frameDecimation,
|
||||||
int horizontalPixelDecimation,
|
int horizontalPixelDecimation,
|
||||||
int verticalPixelDecimation);
|
int verticalPixelDecimation);
|
||||||
virtual ~V4L2Grabber();
|
virtual ~V4L2Grabber();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setCropping(int cropLeft,
|
void setCropping(int cropLeft,
|
||||||
int cropRight,
|
int cropRight,
|
||||||
int cropTop,
|
int cropTop,
|
||||||
int cropBottom);
|
int cropBottom);
|
||||||
|
|
||||||
void set3D(VideoMode mode);
|
void set3D(VideoMode mode);
|
||||||
|
|
||||||
void setSignalThreshold(double redSignalThreshold,
|
void setSignalThreshold(double redSignalThreshold,
|
||||||
double greenSignalThreshold,
|
double greenSignalThreshold,
|
||||||
double blueSignalThreshold,
|
double blueSignalThreshold,
|
||||||
int noSignalCounterThreshold);
|
int noSignalCounterThreshold);
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newFrame(const Image<ColorRgb> & image);
|
void newFrame(const Image<ColorRgb> & image);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
int read_frame();
|
int read_frame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void open_device();
|
void open_device();
|
||||||
|
|
||||||
void close_device();
|
void close_device();
|
||||||
|
|
||||||
void init_read(unsigned int buffer_size);
|
void init_read(unsigned int buffer_size);
|
||||||
|
|
||||||
void init_mmap();
|
void init_mmap();
|
||||||
|
|
||||||
void init_userp(unsigned int buffer_size);
|
void init_userp(unsigned int buffer_size);
|
||||||
|
|
||||||
void init_device(VideoStandard videoStandard, int input);
|
void init_device(VideoStandard videoStandard, int input);
|
||||||
|
|
||||||
void uninit_device();
|
void uninit_device();
|
||||||
|
|
||||||
void start_capturing();
|
void start_capturing();
|
||||||
|
|
||||||
void stop_capturing();
|
void stop_capturing();
|
||||||
|
|
||||||
bool process_image(const void *p, int size);
|
bool process_image(const void *p, int size);
|
||||||
|
|
||||||
void process_image(const uint8_t *p);
|
void process_image(const uint8_t *p);
|
||||||
|
|
||||||
int xioctl(int request, void *arg);
|
int xioctl(int request, void *arg);
|
||||||
|
|
||||||
void throw_exception(const std::string &error);
|
void throw_exception(const std::string &error);
|
||||||
|
|
||||||
void throw_errno_exception(const std::string &error);
|
void throw_errno_exception(const std::string &error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum io_method {
|
enum io_method {
|
||||||
IO_METHOD_READ,
|
IO_METHOD_READ,
|
||||||
IO_METHOD_MMAP,
|
IO_METHOD_MMAP,
|
||||||
IO_METHOD_USERPTR
|
IO_METHOD_USERPTR
|
||||||
};
|
};
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
void *start;
|
void *start;
|
||||||
size_t length;
|
size_t length;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string _deviceName;
|
const std::string _deviceName;
|
||||||
const io_method _ioMethod;
|
const io_method _ioMethod;
|
||||||
int _fileDescriptor;
|
int _fileDescriptor;
|
||||||
std::vector<buffer> _buffers;
|
std::vector<buffer> _buffers;
|
||||||
|
|
||||||
PixelFormat _pixelFormat;
|
PixelFormat _pixelFormat;
|
||||||
int _width;
|
int _width;
|
||||||
int _height;
|
int _height;
|
||||||
int _frameByteSize;
|
int _lineLength;
|
||||||
int _cropLeft;
|
int _frameByteSize;
|
||||||
int _cropRight;
|
int _frameDecimation;
|
||||||
int _cropTop;
|
int _noSignalCounterThreshold;
|
||||||
int _cropBottom;
|
|
||||||
int _frameDecimation;
|
|
||||||
int _horizontalPixelDecimation;
|
|
||||||
int _verticalPixelDecimation;
|
|
||||||
int _noSignalCounterThreshold;
|
|
||||||
|
|
||||||
ColorRgb _noSignalThresholdColor;
|
ColorRgb _noSignalThresholdColor;
|
||||||
|
|
||||||
VideoMode _mode3D;
|
int _currentFrame;
|
||||||
|
int _noSignalCounter;
|
||||||
|
|
||||||
int _currentFrame;
|
QSocketNotifier * _streamNotifier;
|
||||||
int _noSignalCounter;
|
|
||||||
|
|
||||||
QSocketNotifier * _streamNotifier;
|
ImageResampler _imageResampler;
|
||||||
};
|
};
|
||||||
|
39
include/grabber/X11Grabber.h
Normal file
39
include/grabber/X11Grabber.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
// Hyperion-utils includes
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
#include <utils/ImageResampler.h>
|
||||||
|
|
||||||
|
// X11 includes
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
class X11Grabber
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation);
|
||||||
|
|
||||||
|
virtual ~X11Grabber();
|
||||||
|
|
||||||
|
int open();
|
||||||
|
|
||||||
|
Image<ColorRgb> & grab();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImageResampler _imageResampler;
|
||||||
|
|
||||||
|
int _cropLeft;
|
||||||
|
int _cropRight;
|
||||||
|
int _cropTop;
|
||||||
|
int _cropBottom;
|
||||||
|
|
||||||
|
/// Reference to the X11 display (nullptr if not opened)
|
||||||
|
Display * _x11Display;
|
||||||
|
|
||||||
|
unsigned _screenWidth;
|
||||||
|
unsigned _screenHeight;
|
||||||
|
|
||||||
|
Image<ColorRgb> _image;
|
||||||
|
|
||||||
|
int updateScreenDimensions();
|
||||||
|
};
|
102
include/protoserver/ProtoConnection.h
Normal file
102
include/protoserver/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;
|
||||||
|
};
|
34
include/protoserver/ProtoConnectionWrapper.h
Normal file
34
include/protoserver/ProtoConnectionWrapper.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Qt includes
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
// hyperion includes
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
// hyperion proto includes
|
||||||
|
#include "protoserver/ProtoConnection.h"
|
||||||
|
|
||||||
|
/// This class handles callbacks from the V4L2 grabber
|
||||||
|
class ProtoConnectionWrapper : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProtoConnectionWrapper(const std::string & address, int priority, int duration_ms, bool skipProtoReply);
|
||||||
|
virtual ~ProtoConnectionWrapper();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/// Handle a single image
|
||||||
|
/// @param image The image to process
|
||||||
|
void receiveImage(const Image<ColorRgb> & image);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Priority for calls to Hyperion
|
||||||
|
const int _priority;
|
||||||
|
|
||||||
|
/// Duration for color calls to Hyperion
|
||||||
|
const int _duration_ms;
|
||||||
|
|
||||||
|
/// Hyperion proto connection object
|
||||||
|
ProtoConnection _connection;
|
||||||
|
};
|
@ -11,207 +11,202 @@ class Image
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef Pixel_T pixel_type;
|
typedef Pixel_T pixel_type;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Default constructor for an image
|
/// Default constructor for an image
|
||||||
///
|
///
|
||||||
Image() :
|
Image() :
|
||||||
_width(1),
|
_width(1),
|
||||||
_height(1),
|
_height(1),
|
||||||
_pixels(new Pixel_T[2]),
|
_pixels(new Pixel_T[2]),
|
||||||
_endOfPixels(_pixels + 1)
|
_endOfPixels(_pixels + 1)
|
||||||
{
|
{
|
||||||
memset(_pixels, 0, 2*sizeof(Pixel_T));
|
memset(_pixels, 0, 2*sizeof(Pixel_T));
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Constructor for an image with specified width and height
|
/// Constructor for an image with specified width and height
|
||||||
///
|
///
|
||||||
/// @param width The width of the image
|
/// @param width The width of the image
|
||||||
/// @param height The height of the image
|
/// @param height The height of the image
|
||||||
///
|
///
|
||||||
Image(const unsigned width, const unsigned height) :
|
Image(const unsigned width, const unsigned height) :
|
||||||
_width(width),
|
_width(width),
|
||||||
_height(height),
|
_height(height),
|
||||||
_pixels(new Pixel_T[width * height + 1]),
|
_pixels(new Pixel_T[width * height + 1]),
|
||||||
_endOfPixels(_pixels + width * height)
|
_endOfPixels(_pixels + width * height)
|
||||||
{
|
{
|
||||||
memset(_pixels, 0, (_width*_height+1)*sizeof(Pixel_T));
|
memset(_pixels, 0, (_width*_height+1)*sizeof(Pixel_T));
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Constructor for an image with specified width and height
|
/// Constructor for an image with specified width and height
|
||||||
///
|
///
|
||||||
/// @param width The width of the image
|
/// @param width The width of the image
|
||||||
/// @param height The height of the image
|
/// @param height The height of the image
|
||||||
/// @param background The color of the image
|
/// @param background The color of the image
|
||||||
///
|
///
|
||||||
Image(const unsigned width, const unsigned height, const Pixel_T background) :
|
Image(const unsigned width, const unsigned height, const Pixel_T background) :
|
||||||
_width(width),
|
_width(width),
|
||||||
_height(height),
|
_height(height),
|
||||||
_pixels(new Pixel_T[width * height + 1]),
|
_pixels(new Pixel_T[width * height + 1]),
|
||||||
_endOfPixels(_pixels + width * height)
|
_endOfPixels(_pixels + width * height)
|
||||||
{
|
{
|
||||||
std::fill(_pixels, _endOfPixels, background);
|
std::fill(_pixels, _endOfPixels, background);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Copy constructor for an image
|
/// Copy constructor for an image
|
||||||
///
|
///
|
||||||
Image(const Image & other) :
|
Image(const Image & other) :
|
||||||
_width(other._width),
|
_width(other._width),
|
||||||
_height(other._height),
|
_height(other._height),
|
||||||
_pixels(new Pixel_T[other._width * other._height + 1]),
|
_pixels(new Pixel_T[other._width * other._height + 1]),
|
||||||
_endOfPixels(_pixels + other._width * other._height)
|
_endOfPixels(_pixels + other._width * other._height)
|
||||||
{
|
{
|
||||||
memcpy(_pixels, other._pixels, other._width * other._height * sizeof(Pixel_T));
|
memcpy(_pixels, other._pixels, other._width * other._height * sizeof(Pixel_T));
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Destructor
|
/// Destructor
|
||||||
///
|
///
|
||||||
~Image()
|
~Image()
|
||||||
{
|
{
|
||||||
delete[] _pixels;
|
delete[] _pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns the width of the image
|
/// Returns the width of the image
|
||||||
///
|
///
|
||||||
/// @return The width of the image
|
/// @return The width of the image
|
||||||
///
|
///
|
||||||
inline unsigned width() const
|
inline unsigned width() const
|
||||||
{
|
{
|
||||||
return _width;
|
return _width;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns the height of the image
|
/// Returns the height of the image
|
||||||
///
|
///
|
||||||
/// @return The height of the image
|
/// @return The height of the image
|
||||||
///
|
///
|
||||||
inline unsigned height() const
|
inline unsigned height() const
|
||||||
{
|
{
|
||||||
return _height;
|
return _height;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t alpha(const unsigned pixel) const
|
uint8_t red(const unsigned pixel) const
|
||||||
{
|
{
|
||||||
return (_pixels + pixel)->red;
|
return (_pixels + pixel)->red;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t red(const unsigned pixel) const
|
uint8_t green(const unsigned pixel) const
|
||||||
{
|
{
|
||||||
return (_pixels + pixel)->red;
|
return (_pixels + pixel)->green;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t green(const unsigned pixel) const
|
uint8_t blue(const unsigned pixel) const
|
||||||
{
|
{
|
||||||
return (_pixels + pixel)->green;
|
return (_pixels + pixel)->blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t blue(const unsigned pixel) const
|
///
|
||||||
{
|
/// Returns a const reference to a specified pixel in the image
|
||||||
return (_pixels + pixel)->blue;
|
///
|
||||||
}
|
/// @param x The x index
|
||||||
|
/// @param y The y index
|
||||||
|
///
|
||||||
|
/// @return const reference to specified pixel
|
||||||
|
///
|
||||||
|
const Pixel_T& operator()(const unsigned x, const unsigned y) const
|
||||||
|
{
|
||||||
|
return _pixels[toIndex(x,y)];
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns a const reference to a specified pixel in the image
|
/// Returns a reference to a specified pixel in the image
|
||||||
///
|
///
|
||||||
/// @param x The x index
|
/// @param x The x index
|
||||||
/// @param y The y index
|
/// @param y The y index
|
||||||
///
|
///
|
||||||
/// @return const reference to specified pixel
|
/// @return reference to specified pixel
|
||||||
///
|
///
|
||||||
const Pixel_T& operator()(const unsigned x, const unsigned y) const
|
Pixel_T& operator()(const unsigned x, const unsigned y)
|
||||||
{
|
{
|
||||||
return _pixels[toIndex(x,y)];
|
return _pixels[toIndex(x,y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Resize the image
|
||||||
/// Returns a reference to a specified pixel in the image
|
/// @param width The width of the image
|
||||||
///
|
/// @param height The height of the image
|
||||||
/// @param x The x index
|
void resize(const unsigned width, const unsigned height)
|
||||||
/// @param y The y index
|
{
|
||||||
///
|
if ((width*height) > (_endOfPixels-_pixels))
|
||||||
/// @return reference to specified pixel
|
{
|
||||||
///
|
delete[] _pixels;
|
||||||
Pixel_T& operator()(const unsigned x, const unsigned y)
|
_pixels = new Pixel_T[width*height + 1];
|
||||||
{
|
_endOfPixels = _pixels + width*height;
|
||||||
return _pixels[toIndex(x,y)];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize the image
|
_width = width;
|
||||||
/// @param width The width of the image
|
_height = height;
|
||||||
/// @param height The height of the image
|
}
|
||||||
void resize(const unsigned width, const unsigned height)
|
|
||||||
{
|
|
||||||
if ((width*height) > (_endOfPixels-_pixels))
|
|
||||||
{
|
|
||||||
delete[] _pixels;
|
|
||||||
_pixels = new Pixel_T[width*height + 1];
|
|
||||||
_endOfPixels = _pixels + width*height;
|
|
||||||
}
|
|
||||||
|
|
||||||
_width = width;
|
///
|
||||||
_height = height;
|
/// Copies another image into this image. The images should have exactly the same size.
|
||||||
}
|
///
|
||||||
|
/// @param other The image to copy into this
|
||||||
|
///
|
||||||
|
void copy(const Image<Pixel_T>& other)
|
||||||
|
{
|
||||||
|
assert(other._width == _width);
|
||||||
|
assert(other._height == _height);
|
||||||
|
|
||||||
///
|
memcpy(_pixels, other._pixels, _width*_height*sizeof(Pixel_T));
|
||||||
/// Copies another image into this image. The images should have exactly the same size.
|
}
|
||||||
///
|
|
||||||
/// @param other The image to copy into this
|
|
||||||
///
|
|
||||||
void copy(const Image<Pixel_T>& other)
|
|
||||||
{
|
|
||||||
assert(other._width == _width);
|
|
||||||
assert(other._height == _height);
|
|
||||||
|
|
||||||
memcpy(_pixels, other._pixels, _width*_height*sizeof(Pixel_T));
|
///
|
||||||
}
|
/// Returns a memory pointer to the first pixel in the image
|
||||||
|
/// @return The memory pointer to the first pixel
|
||||||
|
///
|
||||||
|
Pixel_T* memptr()
|
||||||
|
{
|
||||||
|
return _pixels;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns a memory pointer to the first pixel in the image
|
/// Returns a const memory pointer to the first pixel in the image
|
||||||
/// @return The memory pointer to the first pixel
|
/// @return The const memory pointer to the first pixel
|
||||||
///
|
///
|
||||||
Pixel_T* memptr()
|
const Pixel_T* memptr() const
|
||||||
{
|
{
|
||||||
return _pixels;
|
return _pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a const memory pointer to the first pixel in the image
|
|
||||||
/// @return The const memory pointer to the first pixel
|
|
||||||
///
|
|
||||||
const Pixel_T* memptr() const
|
|
||||||
{
|
|
||||||
return _pixels;
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Translate x and y coordinate to index of the underlying vector
|
/// Translate x and y coordinate to index of the underlying vector
|
||||||
///
|
///
|
||||||
/// @param x The x index
|
/// @param x The x index
|
||||||
/// @param y The y index
|
/// @param y The y index
|
||||||
///
|
///
|
||||||
/// @return The index into the underlying data-vector
|
/// @return The index into the underlying data-vector
|
||||||
///
|
///
|
||||||
inline unsigned toIndex(const unsigned x, const unsigned y) const
|
inline unsigned toIndex(const unsigned x, const unsigned y) const
|
||||||
{
|
{
|
||||||
return y*_width + x;
|
return y*_width + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// The width of the image
|
/// The width of the image
|
||||||
unsigned _width;
|
unsigned _width;
|
||||||
/// The height of the image
|
/// The height of the image
|
||||||
unsigned _height;
|
unsigned _height;
|
||||||
|
|
||||||
/// The pixels of the image
|
/// The pixels of the image
|
||||||
Pixel_T* _pixels;
|
Pixel_T* _pixels;
|
||||||
|
|
||||||
/// Pointer to the last(extra) pixel
|
/// Pointer to the last(extra) pixel
|
||||||
Pixel_T* _endOfPixels;
|
Pixel_T* _endOfPixels;
|
||||||
};
|
};
|
||||||
|
40
include/utils/ImageResampler.h
Normal file
40
include/utils/ImageResampler.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/VideoMode.h>
|
||||||
|
#include <utils/PixelFormat.h>
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
class ImageResampler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ImageResampler();
|
||||||
|
~ImageResampler();
|
||||||
|
|
||||||
|
void setHorizontalPixelDecimation(int decimator);
|
||||||
|
|
||||||
|
void setVerticalPixelDecimation(int decimator);
|
||||||
|
|
||||||
|
void setCropping(int cropLeft,
|
||||||
|
int cropRight,
|
||||||
|
int cropTop,
|
||||||
|
int cropBottom);
|
||||||
|
|
||||||
|
void set3D(VideoMode mode);
|
||||||
|
|
||||||
|
void processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat,
|
||||||
|
Image<ColorRgb> & outputImage) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static inline uint8_t clamp(int x);
|
||||||
|
static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, uint8_t & b);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _horizontalDecimation;
|
||||||
|
int _verticalDecimation;
|
||||||
|
int _cropLeft;
|
||||||
|
int _cropRight;
|
||||||
|
int _cropTop;
|
||||||
|
int _cropBottom;
|
||||||
|
VideoMode _videoMode;
|
||||||
|
};
|
@ -9,8 +9,9 @@
|
|||||||
enum PixelFormat {
|
enum PixelFormat {
|
||||||
PIXELFORMAT_YUYV,
|
PIXELFORMAT_YUYV,
|
||||||
PIXELFORMAT_UYVY,
|
PIXELFORMAT_UYVY,
|
||||||
PIXELFORMAT_RGB32,
|
PIXELFORMAT_RGB32,
|
||||||
PIXELFORMAT_NO_CHANGE
|
PIXELFORMAT_BGR32,
|
||||||
|
PIXELFORMAT_NO_CHANGE
|
||||||
};
|
};
|
||||||
|
|
||||||
inline PixelFormat parsePixelFormat(std::string pixelFormat)
|
inline PixelFormat parsePixelFormat(std::string pixelFormat)
|
||||||
@ -26,10 +27,14 @@ inline PixelFormat parsePixelFormat(std::string pixelFormat)
|
|||||||
{
|
{
|
||||||
return PIXELFORMAT_UYVY;
|
return PIXELFORMAT_UYVY;
|
||||||
}
|
}
|
||||||
else if (pixelFormat == "rgb32")
|
else if (pixelFormat == "rgb32")
|
||||||
{
|
{
|
||||||
return PIXELFORMAT_RGB32;
|
return PIXELFORMAT_RGB32;
|
||||||
}
|
}
|
||||||
|
else if (pixelFormat == "bgr32")
|
||||||
|
{
|
||||||
|
return PIXELFORMAT_BGR32;
|
||||||
|
}
|
||||||
|
|
||||||
// return the default NO_CHANGE
|
// return the default NO_CHANGE
|
||||||
return PIXELFORMAT_NO_CHANGE;
|
return PIXELFORMAT_NO_CHANGE;
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
if (ENABLE_DISPMANX)
|
if (ENABLE_DISPMANX)
|
||||||
add_subdirectory(dispmanx)
|
add_subdirectory(dispmanx)
|
||||||
endif (ENABLE_DISPMANX)
|
endif (ENABLE_DISPMANX)
|
||||||
@ -6,3 +5,7 @@ endif (ENABLE_DISPMANX)
|
|||||||
if (ENABLE_V4L2)
|
if (ENABLE_V4L2)
|
||||||
add_subdirectory(v4l2)
|
add_subdirectory(v4l2)
|
||||||
endif (ENABLE_V4L2)
|
endif (ENABLE_V4L2)
|
||||||
|
|
||||||
|
if (ENABLE_X11)
|
||||||
|
add_subdirectory(x11)
|
||||||
|
endif()
|
||||||
|
@ -9,7 +9,6 @@ SET(V4L2_QT_HEADERS
|
|||||||
|
|
||||||
SET(V4L2_HEADERS
|
SET(V4L2_HEADERS
|
||||||
${CURRENT_HEADER_DIR}/VideoStandard.h
|
${CURRENT_HEADER_DIR}/VideoStandard.h
|
||||||
${CURRENT_HEADER_DIR}/PixelFormat.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(V4L2_SOURCES
|
SET(V4L2_SOURCES
|
||||||
|
File diff suppressed because it is too large
Load Diff
37
libsrc/grabber/x11/CMakeLists.txt
Normal file
37
libsrc/grabber/x11/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Define the current source locations
|
||||||
|
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||||
|
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/x11)
|
||||||
|
|
||||||
|
# Find X11
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${QT_INCLUDES}
|
||||||
|
${X11_INCLUDES}
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(X11_QT_HEADERS
|
||||||
|
${CURRENT_HEADER_DIR}/X11Grabber.h
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(X11_HEADERS
|
||||||
|
${CURRENT_HEADER_DIR}/X11Grabber.h
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(X11_SOURCES
|
||||||
|
${CURRENT_SOURCE_DIR}/X11Grabber.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
QT4_WRAP_CPP(X11_HEADERS_MOC ${X11_QT_HEADERS})
|
||||||
|
|
||||||
|
add_library(x11-grabber
|
||||||
|
${X11_HEADERS}
|
||||||
|
${X11_SOURCES}
|
||||||
|
${X11_QT_HEADERS}
|
||||||
|
${X11_HEADERS_MOC}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(x11-grabber
|
||||||
|
hyperion
|
||||||
|
${QT_LIBRARIES}
|
||||||
|
)
|
98
libsrc/grabber/x11/X11Grabber.cpp
Normal file
98
libsrc/grabber/x11/X11Grabber.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// STL includes
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// X11 includes
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
// X11Grabber includes
|
||||||
|
#include <grabber/X11Grabber.h>
|
||||||
|
|
||||||
|
X11Grabber::X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) :
|
||||||
|
_imageResampler(),
|
||||||
|
_cropLeft(cropLeft),
|
||||||
|
_cropRight(cropRight),
|
||||||
|
_cropTop(cropTop),
|
||||||
|
_cropBottom(cropBottom),
|
||||||
|
_x11Display(nullptr),
|
||||||
|
_screenWidth(0),
|
||||||
|
_screenHeight(0),
|
||||||
|
_image(0,0)
|
||||||
|
{
|
||||||
|
_imageResampler.setHorizontalPixelDecimation(horizontalPixelDecimation);
|
||||||
|
_imageResampler.setVerticalPixelDecimation(verticalPixelDecimation);
|
||||||
|
_imageResampler.setCropping(0, 0, 0, 0); // cropping is performed by XGetImage
|
||||||
|
}
|
||||||
|
|
||||||
|
X11Grabber::~X11Grabber()
|
||||||
|
{
|
||||||
|
if (_x11Display != nullptr)
|
||||||
|
{
|
||||||
|
XCloseDisplay(_x11Display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int X11Grabber::open()
|
||||||
|
{
|
||||||
|
const char * display_name = nullptr;
|
||||||
|
_x11Display = XOpenDisplay(display_name);
|
||||||
|
|
||||||
|
if (_x11Display == nullptr)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to open the default X11-display" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image<ColorRgb> & X11Grabber::grab()
|
||||||
|
{
|
||||||
|
if (_x11Display == nullptr)
|
||||||
|
{
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScreenDimensions();
|
||||||
|
|
||||||
|
const unsigned croppedWidth = _screenWidth - _cropLeft - _cropRight;
|
||||||
|
const unsigned croppedHeight = _screenHeight - _cropTop - _cropBottom;
|
||||||
|
|
||||||
|
// Capture the current screen
|
||||||
|
XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropLeft, _cropTop, croppedWidth, croppedHeight, AllPlanes, ZPixmap);
|
||||||
|
if (xImage == nullptr)
|
||||||
|
{
|
||||||
|
std::cerr << "Grab failed" << std::endl;
|
||||||
|
return _image;
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageResampler.processImage(reinterpret_cast<const uint8_t *>(xImage->data), xImage->width, xImage->height, xImage->bytes_per_line, PIXELFORMAT_BGR32, _image);
|
||||||
|
|
||||||
|
// Cleanup allocated resources of the X11 grab
|
||||||
|
XDestroyImage(xImage);
|
||||||
|
|
||||||
|
return _image;
|
||||||
|
}
|
||||||
|
|
||||||
|
int X11Grabber::updateScreenDimensions()
|
||||||
|
{
|
||||||
|
XWindowAttributes window_attributes_return;
|
||||||
|
const Status status = XGetWindowAttributes(_x11Display, DefaultRootWindow(_x11Display), &window_attributes_return);
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to obtain window attributes" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_screenWidth == unsigned(window_attributes_return.width) && _screenHeight == unsigned(window_attributes_return.height))
|
||||||
|
{
|
||||||
|
// No update required
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::cout << "Update of screen resolution: [" << _screenWidth << "x" << _screenHeight <<"] => ";
|
||||||
|
_screenWidth = window_attributes_return.width;
|
||||||
|
_screenHeight = window_attributes_return.height;
|
||||||
|
std::cout << "[" << _screenWidth << "x" << _screenHeight <<"]" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -10,7 +10,9 @@ include_directories(
|
|||||||
# Group the headers that go through the MOC compiler
|
# Group the headers that go through the MOC compiler
|
||||||
set(ProtoServer_QT_HEADERS
|
set(ProtoServer_QT_HEADERS
|
||||||
${CURRENT_HEADER_DIR}/ProtoServer.h
|
${CURRENT_HEADER_DIR}/ProtoServer.h
|
||||||
|
${CURRENT_HEADER_DIR}/ProtoConnection.h
|
||||||
${CURRENT_SOURCE_DIR}/ProtoClientConnection.h
|
${CURRENT_SOURCE_DIR}/ProtoClientConnection.h
|
||||||
|
${CURRENT_HEADER_DIR}/ProtoConnectionWrapper.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(ProtoServer_HEADERS
|
set(ProtoServer_HEADERS
|
||||||
@ -19,6 +21,8 @@ set(ProtoServer_HEADERS
|
|||||||
set(ProtoServer_SOURCES
|
set(ProtoServer_SOURCES
|
||||||
${CURRENT_SOURCE_DIR}/ProtoServer.cpp
|
${CURRENT_SOURCE_DIR}/ProtoServer.cpp
|
||||||
${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp
|
${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp
|
||||||
|
${CURRENT_SOURCE_DIR}/ProtoConnection.cpp
|
||||||
|
${CURRENT_SOURCE_DIR}/ProtoConnectionWrapper.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(ProtoServer_PROTOS
|
set(ProtoServer_PROTOS
|
||||||
@ -44,5 +48,5 @@ add_library(protoserver
|
|||||||
target_link_libraries(protoserver
|
target_link_libraries(protoserver
|
||||||
hyperion
|
hyperion
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
${PROTOBUF_LIBRARIES}
|
${PROTOBUF_LIBRARIES}
|
||||||
${QT_LIBRARIES})
|
${QT_LIBRARIES})
|
||||||
|
188
libsrc/protoserver/ProtoConnection.cpp
Normal file
188
libsrc/protoserver/ProtoConnection.cpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// stl includes
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QRgb>
|
||||||
|
|
||||||
|
// protoserver includes
|
||||||
|
#include "protoserver/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;
|
||||||
|
}
|
19
libsrc/protoserver/ProtoConnectionWrapper.cpp
Normal file
19
libsrc/protoserver/ProtoConnectionWrapper.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// protoserver includes
|
||||||
|
#include "protoserver/ProtoConnectionWrapper.h"
|
||||||
|
|
||||||
|
ProtoConnectionWrapper::ProtoConnectionWrapper(const std::string & address, int priority, int duration_ms, bool skipProtoReply) :
|
||||||
|
_priority(priority),
|
||||||
|
_duration_ms(duration_ms),
|
||||||
|
_connection(address)
|
||||||
|
{
|
||||||
|
_connection.setSkipReply(skipProtoReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtoConnectionWrapper::~ProtoConnectionWrapper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConnectionWrapper::receiveImage(const Image<ColorRgb> & image)
|
||||||
|
{
|
||||||
|
_connection.setImage(image, _priority, _duration_ms);
|
||||||
|
}
|
@ -13,6 +13,12 @@ add_library(hyperion-utils
|
|||||||
${CURRENT_HEADER_DIR}/Image.h
|
${CURRENT_HEADER_DIR}/Image.h
|
||||||
${CURRENT_HEADER_DIR}/Sleep.h
|
${CURRENT_HEADER_DIR}/Sleep.h
|
||||||
|
|
||||||
|
${CURRENT_HEADER_DIR}/PixelFormat.h
|
||||||
|
${CURRENT_HEADER_DIR}/VideoMode.h
|
||||||
|
|
||||||
|
${CURRENT_HEADER_DIR}/ImageResampler.h
|
||||||
|
${CURRENT_SOURCE_DIR}/ImageResampler.cpp
|
||||||
|
|
||||||
${CURRENT_HEADER_DIR}/HsvTransform.h
|
${CURRENT_HEADER_DIR}/HsvTransform.h
|
||||||
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
|
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
|
||||||
${CURRENT_HEADER_DIR}/RgbChannelTransform.h
|
${CURRENT_HEADER_DIR}/RgbChannelTransform.h
|
||||||
|
133
libsrc/utils/ImageResampler.cpp
Normal file
133
libsrc/utils/ImageResampler.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include "utils/ImageResampler.h"
|
||||||
|
|
||||||
|
ImageResampler::ImageResampler() :
|
||||||
|
_horizontalDecimation(1),
|
||||||
|
_verticalDecimation(1),
|
||||||
|
_cropLeft(0),
|
||||||
|
_cropRight(0),
|
||||||
|
_cropTop(0),
|
||||||
|
_cropBottom(0),
|
||||||
|
_videoMode(VIDEO_2D)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageResampler::~ImageResampler()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageResampler::setHorizontalPixelDecimation(int decimator)
|
||||||
|
{
|
||||||
|
_horizontalDecimation = decimator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageResampler::setVerticalPixelDecimation(int decimator)
|
||||||
|
{
|
||||||
|
_verticalDecimation = decimator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageResampler::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
|
||||||
|
{
|
||||||
|
_cropLeft = cropLeft;
|
||||||
|
_cropRight = cropRight;
|
||||||
|
_cropTop = cropTop;
|
||||||
|
_cropBottom = cropBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageResampler::set3D(VideoMode mode)
|
||||||
|
{
|
||||||
|
_videoMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageResampler::processImage(const uint8_t * data, int width, int height, int lineLength, PixelFormat pixelFormat, Image<ColorRgb> &outputImage) const
|
||||||
|
{
|
||||||
|
int cropLeft = _cropLeft;
|
||||||
|
int cropRight = _cropRight;
|
||||||
|
int cropTop = _cropTop;
|
||||||
|
int cropBottom = _cropBottom;
|
||||||
|
|
||||||
|
// handle 3D mode
|
||||||
|
switch (_videoMode)
|
||||||
|
{
|
||||||
|
case VIDEO_3DSBS:
|
||||||
|
cropRight = width/2;
|
||||||
|
break;
|
||||||
|
case VIDEO_3DTAB:
|
||||||
|
cropBottom = height/2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the output size
|
||||||
|
int outputWidth = (width - cropLeft - cropRight - _horizontalDecimation/2 + _horizontalDecimation - 1) / _horizontalDecimation;
|
||||||
|
int outputHeight = (height - cropTop - cropBottom - _verticalDecimation/2 + _verticalDecimation - 1) / _verticalDecimation;
|
||||||
|
outputImage.resize(outputWidth, outputHeight);
|
||||||
|
|
||||||
|
for (int yDest = 0, ySource = cropTop + _verticalDecimation/2; yDest < outputHeight; ySource += _verticalDecimation, ++yDest)
|
||||||
|
{
|
||||||
|
for (int xDest = 0, xSource = cropLeft + _horizontalDecimation/2; xDest < outputWidth; xSource += _horizontalDecimation, ++xDest)
|
||||||
|
{
|
||||||
|
ColorRgb & rgb = outputImage(xDest, yDest);
|
||||||
|
|
||||||
|
switch (pixelFormat)
|
||||||
|
{
|
||||||
|
case PIXELFORMAT_UYVY:
|
||||||
|
{
|
||||||
|
int index = lineLength * ySource + xSource * 2;
|
||||||
|
uint8_t y = data[index+1];
|
||||||
|
uint8_t u = ((xSource&1) == 0) ? data[index ] : data[index-2];
|
||||||
|
uint8_t v = ((xSource&1) == 0) ? data[index+2] : data[index ];
|
||||||
|
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PIXELFORMAT_YUYV:
|
||||||
|
{
|
||||||
|
int index = lineLength * ySource + xSource * 2;
|
||||||
|
uint8_t y = data[index];
|
||||||
|
uint8_t u = ((xSource&1) == 0) ? data[index+1] : data[index-1];
|
||||||
|
uint8_t v = ((xSource&1) == 0) ? data[index+3] : data[index+1];
|
||||||
|
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PIXELFORMAT_RGB32:
|
||||||
|
{
|
||||||
|
int index = lineLength * ySource + xSource * 4;
|
||||||
|
rgb.red = data[index ];
|
||||||
|
rgb.green = data[index+1];
|
||||||
|
rgb.blue = data[index+2];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PIXELFORMAT_BGR32:
|
||||||
|
{
|
||||||
|
int index = lineLength * ySource + xSource * 4;
|
||||||
|
rgb.blue = data[index ];
|
||||||
|
rgb.green = data[index+1];
|
||||||
|
rgb.red = data[index+2];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PIXELFORMAT_NO_CHANGE:
|
||||||
|
std::cerr << "Invalid pixel format given" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ImageResampler::clamp(int x)
|
||||||
|
{
|
||||||
|
return (x<0) ? 0 : ((x>255) ? 255 : uint8_t(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageResampler::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);
|
||||||
|
}
|
@ -1,5 +1,12 @@
|
|||||||
add_subdirectory(hyperiond)
|
add_subdirectory(hyperiond)
|
||||||
add_subdirectory(hyperion-remote)
|
add_subdirectory(hyperion-remote)
|
||||||
if (ENABLE_V4L2)
|
|
||||||
|
# Add the 'Video 4 Linux' grabber if it is enabled
|
||||||
|
if(ENABLE_V4L2)
|
||||||
add_subdirectory(hyperion-v4l2)
|
add_subdirectory(hyperion-v4l2)
|
||||||
endif (ENABLE_V4L2)
|
endif(ENABLE_V4L2)
|
||||||
|
|
||||||
|
# Add the X11 grabber if it is enabled
|
||||||
|
if(ENABLE_X11)
|
||||||
|
add_subdirectory(hyperion-x11)
|
||||||
|
endif(ENABLE_X11)
|
||||||
|
@ -2,44 +2,29 @@ cmake_minimum_required(VERSION 2.8)
|
|||||||
|
|
||||||
project(hyperion-v4l2)
|
project(hyperion-v4l2)
|
||||||
|
|
||||||
# add protocol buffers
|
|
||||||
find_package(Protobuf REQUIRED)
|
|
||||||
|
|
||||||
# find Qt4
|
# find Qt4
|
||||||
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork)
|
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${QT_INCLUDES}
|
${QT_INCLUDES}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(Hyperion_V4L2_QT_HEADERS
|
set(Hyperion_V4L2_QT_HEADERS
|
||||||
ImageHandler.h
|
|
||||||
ScreenshotHandler.h
|
ScreenshotHandler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(Hyperion_V4L2_HEADERS
|
set(Hyperion_V4L2_HEADERS
|
||||||
VideoStandardParameter.h
|
VideoStandardParameter.h
|
||||||
PixelFormatParameter.h
|
PixelFormatParameter.h
|
||||||
ProtoConnection.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(Hyperion_V4L2_SOURCES
|
set(Hyperion_V4L2_SOURCES
|
||||||
hyperion-v4l2.cpp
|
hyperion-v4l2.cpp
|
||||||
ProtoConnection.cpp
|
|
||||||
ImageHandler.cpp
|
|
||||||
ScreenshotHandler.cpp
|
ScreenshotHandler.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}
|
|
||||||
)
|
|
||||||
|
|
||||||
QT4_WRAP_CPP(Hyperion_V4L2_MOC_SOURCES ${Hyperion_V4L2_QT_HEADERS})
|
QT4_WRAP_CPP(Hyperion_V4L2_MOC_SOURCES ${Hyperion_V4L2_QT_HEADERS})
|
||||||
|
|
||||||
add_executable(hyperion-v4l2
|
add_executable(hyperion-v4l2
|
||||||
@ -47,8 +32,6 @@ add_executable(hyperion-v4l2
|
|||||||
${Hyperion_V4L2_SOURCES}
|
${Hyperion_V4L2_SOURCES}
|
||||||
${Hyperion_V4L2_QT_HEADERS}
|
${Hyperion_V4L2_QT_HEADERS}
|
||||||
${Hyperion_V4L2_MOC_SOURCES}
|
${Hyperion_V4L2_MOC_SOURCES}
|
||||||
${Hyperion_V4L2_PROTO_SRCS}
|
|
||||||
${Hyperion_V4L2_PROTO_HDRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(hyperion-v4l2
|
target_link_libraries(hyperion-v4l2
|
||||||
@ -56,7 +39,7 @@ target_link_libraries(hyperion-v4l2
|
|||||||
getoptPlusPlus
|
getoptPlusPlus
|
||||||
blackborder
|
blackborder
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
${PROTOBUF_LIBRARIES}
|
protoserver
|
||||||
pthread
|
pthread
|
||||||
${QT_LIBRARIES}
|
${QT_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
// hyperion-v4l2 includes
|
|
||||||
#include "ImageHandler.h"
|
|
||||||
|
|
||||||
ImageHandler::ImageHandler(const std::string & address, int priority, bool skipProtoReply) :
|
|
||||||
_priority(priority),
|
|
||||||
_connection(address)
|
|
||||||
{
|
|
||||||
_connection.setSkipReply(skipProtoReply);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageHandler::~ImageHandler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageHandler::receiveImage(const Image<ColorRgb> & image)
|
|
||||||
{
|
|
||||||
_connection.setImage(image, _priority, 1000);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
// Qt includes
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
// hyperion includes
|
|
||||||
#include <utils/Image.h>
|
|
||||||
#include <utils/ColorRgb.h>
|
|
||||||
|
|
||||||
// hyperion v4l2 includes
|
|
||||||
#include "ProtoConnection.h"
|
|
||||||
|
|
||||||
/// This class handles callbacks from the V4L2 grabber
|
|
||||||
class ImageHandler : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ImageHandler(const std::string & address, int priority, bool skipProtoReply);
|
|
||||||
virtual ~ImageHandler();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
/// Handle a single image
|
|
||||||
/// @param image The image to process
|
|
||||||
void receiveImage(const Image<ColorRgb> & image);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Priority for calls to Hyperion
|
|
||||||
const int _priority;
|
|
||||||
|
|
||||||
/// Hyperion proto connection object
|
|
||||||
ProtoConnection _connection;
|
|
||||||
};
|
|
@ -2,7 +2,7 @@
|
|||||||
#include <getoptPlusPlus/getoptpp.h>
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
|
|
||||||
// grabber includes
|
// grabber includes
|
||||||
#include <grabber/PixelFormat.h>
|
#include <utils/PixelFormat.h>
|
||||||
|
|
||||||
using namespace vlofgren;
|
using namespace vlofgren;
|
||||||
|
|
||||||
@ -10,34 +10,34 @@ using namespace vlofgren;
|
|||||||
typedef vlofgren::PODParameter<PixelFormat> PixelFormatParameter;
|
typedef vlofgren::PODParameter<PixelFormat> PixelFormatParameter;
|
||||||
|
|
||||||
namespace vlofgren {
|
namespace vlofgren {
|
||||||
/// Translates a string (as passed on the commandline) to a pixel format
|
/// Translates a string (as passed on the commandline) to a pixel format
|
||||||
///
|
///
|
||||||
/// @param[in] s The string (as passed on the commandline)
|
/// @param[in] s The string (as passed on the commandline)
|
||||||
/// @return The pixel format
|
/// @return The pixel format
|
||||||
/// @throws Parameter::ParameterRejected If the string did not result in a pixel format
|
/// @throws Parameter::ParameterRejected If the string did not result in a pixel format
|
||||||
template<>
|
template<>
|
||||||
PixelFormat PixelFormatParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
PixelFormat PixelFormatParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
||||||
{
|
{
|
||||||
QString input = QString::fromStdString(s).toLower();
|
QString input = QString::fromStdString(s).toLower();
|
||||||
|
|
||||||
if (input == "yuyv")
|
if (input == "yuyv")
|
||||||
{
|
{
|
||||||
return PIXELFORMAT_YUYV;
|
return PIXELFORMAT_YUYV;
|
||||||
}
|
}
|
||||||
else if (input == "uyvy")
|
else if (input == "uyvy")
|
||||||
{
|
{
|
||||||
return PIXELFORMAT_UYVY;
|
return PIXELFORMAT_UYVY;
|
||||||
}
|
}
|
||||||
else if (input == "rgb32")
|
else if (input == "rgb32")
|
||||||
{
|
{
|
||||||
return PIXELFORMAT_RGB32;
|
return PIXELFORMAT_RGB32;
|
||||||
}
|
}
|
||||||
else if (input == "no-change")
|
else if (input == "no-change")
|
||||||
{
|
{
|
||||||
return PIXELFORMAT_NO_CHANGE;
|
return PIXELFORMAT_NO_CHANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Parameter::ParameterRejected("Invalid value for pixel format. Valid values are: YUYV, UYVY, RGB32, and NO-CHANGE");
|
throw Parameter::ParameterRejected("Invalid value for pixel format. Valid values are: YUYV, UYVY, RGB32, and NO-CHANGE");
|
||||||
return PIXELFORMAT_NO_CHANGE;
|
return PIXELFORMAT_NO_CHANGE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,188 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
#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;
|
|
||||||
};
|
|
@ -15,11 +15,13 @@
|
|||||||
// grabber includes
|
// grabber includes
|
||||||
#include "grabber/V4L2Grabber.h"
|
#include "grabber/V4L2Grabber.h"
|
||||||
|
|
||||||
|
// proto includes
|
||||||
|
#include "protoserver/ProtoConnection.h"
|
||||||
|
#include "protoserver/ProtoConnectionWrapper.h"
|
||||||
|
|
||||||
// hyperion-v4l2 includes
|
// hyperion-v4l2 includes
|
||||||
#include "ProtoConnection.h"
|
|
||||||
#include "VideoStandardParameter.h"
|
#include "VideoStandardParameter.h"
|
||||||
#include "PixelFormatParameter.h"
|
#include "PixelFormatParameter.h"
|
||||||
#include "ImageHandler.h"
|
|
||||||
#include "ScreenshotHandler.h"
|
#include "ScreenshotHandler.h"
|
||||||
|
|
||||||
using namespace vlofgren;
|
using namespace vlofgren;
|
||||||
@ -27,144 +29,145 @@ using namespace vlofgren;
|
|||||||
// save the image as screenshot
|
// save the image as screenshot
|
||||||
void saveScreenshot(void *, const Image<ColorRgb> & image)
|
void saveScreenshot(void *, 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.save("screenshot.png");
|
pngImage.save("screenshot.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
// force the locale
|
// force the locale
|
||||||
setlocale(LC_ALL, "C");
|
setlocale(LC_ALL, "C");
|
||||||
QLocale::setDefault(QLocale::c());
|
QLocale::setDefault(QLocale::c());
|
||||||
|
|
||||||
// register the image type to use in signals
|
// register the image type to use in signals
|
||||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// create the option parser and initialize all parameters
|
// create the option parser and initialize all parameters
|
||||||
OptionsParser optionParser("V4L capture application for Hyperion");
|
OptionsParser optionParser("V4L capture application for Hyperion");
|
||||||
ParameterSet & parameters = optionParser.getParameters();
|
ParameterSet & parameters = optionParser.getParameters();
|
||||||
|
|
||||||
StringParameter & argDevice = parameters.add<StringParameter> ('d', "device", "The device to use [default=/dev/video0]");
|
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)");
|
VideoStandardParameter & argVideoStandard = parameters.add<VideoStandardParameter>('v', "video-standard", "The used video standard. Valid values are PAL or NTSC (optional)");
|
||||||
PixelFormatParameter & argPixelFormat = parameters.add<PixelFormatParameter> (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, and RGB32 (optional)");
|
PixelFormatParameter & argPixelFormat = parameters.add<PixelFormatParameter> (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, and RGB32 (optional)");
|
||||||
IntParameter & argInput = parameters.add<IntParameter> (0x0, "input", "Input channel (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 & 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 & 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 of the picture before decimation [default=0]");
|
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides of 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 of 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 of the picture before decimation [default=0]");
|
||||||
IntParameter & argCropLeft = parameters.add<IntParameter> (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)");
|
IntParameter & argCropLeft = parameters.add<IntParameter> (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)");
|
||||||
IntParameter & argCropRight = parameters.add<IntParameter> (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)");
|
IntParameter & argCropRight = parameters.add<IntParameter> (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)");
|
||||||
IntParameter & argCropTop = parameters.add<IntParameter> (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)");
|
IntParameter & argCropTop = parameters.add<IntParameter> (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)");
|
||||||
IntParameter & argCropBottom = parameters.add<IntParameter> (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)");
|
IntParameter & argCropBottom = parameters.add<IntParameter> (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)");
|
||||||
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=1]");
|
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]");
|
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");
|
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.");
|
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.");
|
||||||
DoubleParameter & argRedSignalThreshold = parameters.add<DoubleParameter> (0x0, "red-threshold", "The red signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
DoubleParameter & argRedSignalThreshold = parameters.add<DoubleParameter> (0x0, "red-threshold", "The red signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
||||||
DoubleParameter & argGreenSignalThreshold = parameters.add<DoubleParameter> (0x0, "green-threshold", "The green signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
DoubleParameter & argGreenSignalThreshold = parameters.add<DoubleParameter> (0x0, "green-threshold", "The green signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
||||||
DoubleParameter & argBlueSignalThreshold = parameters.add<DoubleParameter> (0x0, "blue-threshold", "The blue signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
DoubleParameter & argBlueSignalThreshold = parameters.add<DoubleParameter> (0x0, "blue-threshold", "The blue signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
||||||
SwitchParameter<> & arg3DSBS = parameters.add<SwitchParameter<>> (0x0, "3DSBS", "Interpret the incoming video stream as 3D side-by-side");
|
SwitchParameter<> & arg3DSBS = parameters.add<SwitchParameter<>> (0x0, "3DSBS", "Interpret the incoming video stream as 3D side-by-side");
|
||||||
SwitchParameter<> & arg3DTAB = parameters.add<SwitchParameter<>> (0x0, "3DTAB", "Interpret the incoming video stream as 3D top-and-bottom");
|
SwitchParameter<> & arg3DTAB = parameters.add<SwitchParameter<>> (0x0, "3DTAB", "Interpret the incoming video stream as 3D top-and-bottom");
|
||||||
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
|
||||||
argDevice.setDefault("/dev/video0");
|
argDevice.setDefault("/dev/video0");
|
||||||
argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE);
|
argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE);
|
||||||
argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE);
|
argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE);
|
||||||
argInput.setDefault(-1);
|
argInput.setDefault(-1);
|
||||||
argWidth.setDefault(-1);
|
argWidth.setDefault(-1);
|
||||||
argHeight.setDefault(-1);
|
argHeight.setDefault(-1);
|
||||||
argCropWidth.setDefault(0);
|
argCropWidth.setDefault(0);
|
||||||
argCropHeight.setDefault(0);
|
argCropHeight.setDefault(0);
|
||||||
argSizeDecimation.setDefault(1);
|
argSizeDecimation.setDefault(1);
|
||||||
argFrameDecimation.setDefault(1);
|
argFrameDecimation.setDefault(1);
|
||||||
argAddress.setDefault("127.0.0.1:19445");
|
argAddress.setDefault("127.0.0.1:19445");
|
||||||
argPriority.setDefault(800);
|
argPriority.setDefault(800);
|
||||||
argSignalThreshold.setDefault(-1);
|
argSignalThreshold.setDefault(-1);
|
||||||
|
|
||||||
// parse all options
|
// parse all options
|
||||||
optionParser.parse(argc, const_cast<const char **>(argv));
|
optionParser.parse(argc, const_cast<const char **>(argv));
|
||||||
|
|
||||||
// check if we need to display the usage. exit if we do.
|
// check if we need to display the usage. exit if we do.
|
||||||
if (argHelp.isSet())
|
if (argHelp.isSet())
|
||||||
{
|
{
|
||||||
optionParser.usage();
|
optionParser.usage();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue());
|
// cropping values if not defined
|
||||||
if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue());
|
if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue());
|
||||||
if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue());
|
if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue());
|
||||||
if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue());
|
if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue());
|
||||||
|
if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue());
|
||||||
|
|
||||||
// initialize the grabber
|
// initialize the grabber
|
||||||
V4L2Grabber grabber(
|
V4L2Grabber grabber(
|
||||||
argDevice.getValue(),
|
argDevice.getValue(),
|
||||||
argInput.getValue(),
|
argInput.getValue(),
|
||||||
argVideoStandard.getValue(),
|
argVideoStandard.getValue(),
|
||||||
argPixelFormat.getValue(),
|
argPixelFormat.getValue(),
|
||||||
argWidth.getValue(),
|
argWidth.getValue(),
|
||||||
argHeight.getValue(),
|
argHeight.getValue(),
|
||||||
std::max(1, argFrameDecimation.getValue()),
|
std::max(1, argFrameDecimation.getValue()),
|
||||||
std::max(1, argSizeDecimation.getValue()),
|
std::max(1, argSizeDecimation.getValue()),
|
||||||
std::max(1, argSizeDecimation.getValue()));
|
std::max(1, argSizeDecimation.getValue()));
|
||||||
|
|
||||||
// set signal detection
|
// set signal detection
|
||||||
grabber.setSignalThreshold(
|
grabber.setSignalThreshold(
|
||||||
std::min(1.0, std::max(0.0, argRedSignalThreshold.isSet() ? argRedSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
std::min(1.0, std::max(0.0, argRedSignalThreshold.isSet() ? argRedSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
||||||
std::min(1.0, std::max(0.0, argGreenSignalThreshold.isSet() ? argGreenSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
std::min(1.0, std::max(0.0, argGreenSignalThreshold.isSet() ? argGreenSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
||||||
std::min(1.0, std::max(0.0, argBlueSignalThreshold.isSet() ? argBlueSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
std::min(1.0, std::max(0.0, argBlueSignalThreshold.isSet() ? argBlueSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
||||||
50);
|
50);
|
||||||
|
|
||||||
// set cropping values
|
// set cropping values
|
||||||
grabber.setCropping(
|
grabber.setCropping(
|
||||||
std::max(0, argCropLeft.getValue()),
|
std::max(0, argCropLeft.getValue()),
|
||||||
std::max(0, argCropRight.getValue()),
|
std::max(0, argCropRight.getValue()),
|
||||||
std::max(0, argCropTop.getValue()),
|
std::max(0, argCropTop.getValue()),
|
||||||
std::max(0, argCropBottom.getValue()));
|
std::max(0, argCropBottom.getValue()));
|
||||||
|
|
||||||
// set 3D mode if applicable
|
// set 3D mode if applicable
|
||||||
if (arg3DSBS.isSet())
|
if (arg3DSBS.isSet())
|
||||||
{
|
{
|
||||||
grabber.set3D(VIDEO_3DSBS);
|
grabber.set3D(VIDEO_3DSBS);
|
||||||
}
|
}
|
||||||
else if (arg3DTAB.isSet())
|
else if (arg3DTAB.isSet())
|
||||||
{
|
{
|
||||||
grabber.set3D(VIDEO_3DTAB);
|
grabber.set3D(VIDEO_3DTAB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the grabber
|
// run the grabber
|
||||||
if (argScreenshot.isSet())
|
if (argScreenshot.isSet())
|
||||||
{
|
{
|
||||||
ScreenshotHandler handler("screenshot.png");
|
ScreenshotHandler handler("screenshot.png");
|
||||||
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
||||||
grabber.start();
|
grabber.start();
|
||||||
QCoreApplication::exec();
|
QCoreApplication::exec();
|
||||||
grabber.stop();
|
grabber.stop();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSkipReply.isSet());
|
ProtoConnectionWrapper handler(argAddress.getValue(), argPriority.getValue(), 1000, argSkipReply.isSet());
|
||||||
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
||||||
grabber.start();
|
grabber.start();
|
||||||
QCoreApplication::exec();
|
QCoreApplication::exec();
|
||||||
grabber.stop();
|
grabber.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::runtime_error & e)
|
catch (const std::runtime_error & e)
|
||||||
{
|
{
|
||||||
// An error occured. Display error and quit
|
// An error occured. Display error and quit
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
51
src/hyperion-x11/CMakeLists.txt
Normal file
51
src/hyperion-x11/CMakeLists.txt
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Configure minimum CMAKE version
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
# Set the project name
|
||||||
|
project(hyperion-x11)
|
||||||
|
|
||||||
|
# find Qt4
|
||||||
|
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork)
|
||||||
|
|
||||||
|
# Find X11
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/protoserver
|
||||||
|
${QT_INCLUDES}
|
||||||
|
${X11_INCLUDES}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Hyperion_X11_QT_HEADERS
|
||||||
|
X11Wrapper.h)
|
||||||
|
|
||||||
|
set(Hyperion_X11_HEADERS
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Hyperion_X11_SOURCES
|
||||||
|
hyperion-x11.cpp
|
||||||
|
X11Wrapper.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
QT4_WRAP_CPP(Hyperion_X11_HEADERS_MOC ${Hyperion_X11_QT_HEADERS})
|
||||||
|
|
||||||
|
add_executable(hyperion-x11
|
||||||
|
${Hyperion_X11_HEADERS}
|
||||||
|
${Hyperion_X11_SOURCES}
|
||||||
|
${Hyperion_X11_HEADERS_MOC}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(hyperion-x11
|
||||||
|
getoptPlusPlus
|
||||||
|
blackborder
|
||||||
|
hyperion-utils
|
||||||
|
protoserver
|
||||||
|
x11-grabber
|
||||||
|
${X11_LIBRARIES}
|
||||||
|
pthread
|
||||||
|
)
|
||||||
|
|
||||||
|
qt4_use_modules(hyperion-x11
|
||||||
|
Core
|
||||||
|
Gui
|
||||||
|
Network)
|
36
src/hyperion-x11/X11Wrapper.cpp
Normal file
36
src/hyperion-x11/X11Wrapper.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
// Hyperion-X11 includes
|
||||||
|
#include "X11Wrapper.h"
|
||||||
|
|
||||||
|
X11Wrapper::X11Wrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) :
|
||||||
|
_timer(this),
|
||||||
|
_grabber(cropLeft, cropRight, cropTop, cropBottom, horizontalPixelDecimation, verticalPixelDecimation)
|
||||||
|
{
|
||||||
|
_timer.setSingleShot(false);
|
||||||
|
_timer.setInterval(grabInterval);
|
||||||
|
|
||||||
|
// Connect capturing to the timeout signal of the timer
|
||||||
|
connect(&_timer, SIGNAL(timeout()), this, SLOT(capture()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Image<ColorRgb> & X11Wrapper::getScreenshot()
|
||||||
|
{
|
||||||
|
const Image<ColorRgb> & screenshot = _grabber.grab();
|
||||||
|
return screenshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
void X11Wrapper::start()
|
||||||
|
{
|
||||||
|
_timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void X11Wrapper::stop()
|
||||||
|
{
|
||||||
|
_timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void X11Wrapper::capture()
|
||||||
|
{
|
||||||
|
const Image<ColorRgb> & screenshot = _grabber.grab();
|
||||||
|
emit sig_screenshot(screenshot);
|
||||||
|
}
|
39
src/hyperion-x11/X11Wrapper.h
Normal file
39
src/hyperion-x11/X11Wrapper.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
// QT includes
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
// Hyperion-X11 includes
|
||||||
|
#include <grabber/X11Grabber.h>
|
||||||
|
|
||||||
|
class X11Wrapper : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
X11Wrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation);
|
||||||
|
|
||||||
|
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
|
||||||
|
X11Grabber _grabber;
|
||||||
|
};
|
110
src/hyperion-x11/hyperion-x11.cpp
Normal file
110
src/hyperion-x11/hyperion-x11.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
|
||||||
|
// QT includes
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
// getoptPlusPLus includes
|
||||||
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
|
|
||||||
|
#include "protoserver/ProtoConnectionWrapper.h"
|
||||||
|
#include "X11Wrapper.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("X11 capture application for Hyperion");
|
||||||
|
ParameterSet & parameters = optionParser.getParameters();
|
||||||
|
|
||||||
|
IntParameter & argFps = parameters.add<IntParameter> ('f', "framerate", "Capture frame rate [default=10]");
|
||||||
|
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides of 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 of the picture before decimation [default=0]");
|
||||||
|
IntParameter & argCropLeft = parameters.add<IntParameter> (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)");
|
||||||
|
IntParameter & argCropRight = parameters.add<IntParameter> (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)");
|
||||||
|
IntParameter & argCropTop = parameters.add<IntParameter> (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)");
|
||||||
|
IntParameter & argCropBottom = parameters.add<IntParameter> (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)");
|
||||||
|
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=8]");
|
||||||
|
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);
|
||||||
|
argCropWidth.setDefault(0);
|
||||||
|
argCropHeight.setDefault(0);
|
||||||
|
argSizeDecimation.setDefault(8);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cropping values if not defined
|
||||||
|
if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue());
|
||||||
|
if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue());
|
||||||
|
if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue());
|
||||||
|
if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue());
|
||||||
|
|
||||||
|
// Create the X11 grabbing stuff
|
||||||
|
int grabInterval = 1000 / argFps.getValue();
|
||||||
|
X11Wrapper x11Wrapper(
|
||||||
|
grabInterval,
|
||||||
|
argCropLeft.getValue(),
|
||||||
|
argCropRight.getValue(),
|
||||||
|
argCropTop.getValue(),
|
||||||
|
argCropBottom.getValue(),
|
||||||
|
argSizeDecimation.getValue(), // horizontal decimation
|
||||||
|
argSizeDecimation.getValue()); // vertical decimation
|
||||||
|
|
||||||
|
if (argScreenshot.isSet())
|
||||||
|
{
|
||||||
|
// Capture a single screenshot and finish
|
||||||
|
const Image<ColorRgb> & screenshot = x11Wrapper.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(&x11Wrapper, SIGNAL(sig_screenshot(const Image<ColorRgb> &)), &protoWrapper, SLOT(receiveImage(Image<ColorRgb>)));
|
||||||
|
|
||||||
|
// Start the capturing
|
||||||
|
x11Wrapper.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;
|
||||||
|
}
|
@ -52,3 +52,11 @@ target_link_libraries(test_qregexp
|
|||||||
add_executable(test_qtscreenshot TestQtScreenshot.cpp)
|
add_executable(test_qtscreenshot TestQtScreenshot.cpp)
|
||||||
target_link_libraries(test_qtscreenshot
|
target_link_libraries(test_qtscreenshot
|
||||||
${QT_LIBRARIES})
|
${QT_LIBRARIES})
|
||||||
|
|
||||||
|
if(ENABLE_X11)
|
||||||
|
# Find X11
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
|
||||||
|
add_executable(test_x11performance TestX11Performance.cpp)
|
||||||
|
target_link_libraries(test_x11performance ${X11_LIBRARIES} ${QT_LIBRARIES})
|
||||||
|
endif(ENABLE_X11)
|
||||||
|
@ -6,15 +6,67 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QRgb>
|
||||||
|
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
|
// Utils includes
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
void createScreenshot(const int cropHorizontal, const int cropVertical, const int decimation, Image<ColorRgb> & image)
|
||||||
|
{
|
||||||
|
// Create the full size screenshot
|
||||||
|
const QRect screenSize = QApplication::desktop()->screenGeometry();
|
||||||
|
const int croppedWidth = screenSize.width() - 2*cropVertical;
|
||||||
|
const int croppedHeight = screenSize.height() - 2*cropHorizontal;
|
||||||
|
const QPixmap fullSizeScreenshot = QPixmap::grabWindow(QApplication::desktop()->winId(), cropVertical, cropHorizontal, croppedWidth, croppedHeight);
|
||||||
|
|
||||||
|
// Scale the screenshot to the required size
|
||||||
|
const int width = fullSizeScreenshot.width()/decimation;
|
||||||
|
const int height = fullSizeScreenshot.height()/decimation;
|
||||||
|
const QPixmap scaledScreenshot = fullSizeScreenshot.scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation);
|
||||||
|
|
||||||
|
// Convert the QPixmap to QImage in order to get out RGB values
|
||||||
|
const QImage qImage = scaledScreenshot.toImage();
|
||||||
|
|
||||||
|
// Make sure that the output image has the right size
|
||||||
|
image.resize(width, height);
|
||||||
|
|
||||||
|
// Copy the data into the output image
|
||||||
|
for (int y=0; y<qImage.height(); ++y)
|
||||||
|
{
|
||||||
|
for (int x=0; x<qImage.width(); ++x)
|
||||||
|
{
|
||||||
|
// Get the pixel at [x;y] (format int32 #AARRGGBB)
|
||||||
|
const QRgb inPixel = qImage.pixel(x,y);
|
||||||
|
|
||||||
|
// Copy the color channels into the output pixel
|
||||||
|
ColorRgb & outPixel = image(x,y);
|
||||||
|
outPixel.red = (inPixel & 0x00ff0000) >> 16;
|
||||||
|
outPixel.green = (inPixel & 0x0000ff00) >> 8;
|
||||||
|
outPixel.blue = (inPixel & 0x000000ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
int decimation = 10;
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
QElapsedTimer timer;
|
||||||
|
|
||||||
QPixmap originalPixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
|
Image<ColorRgb> screenshot(64,64);
|
||||||
|
|
||||||
std::cout << "Grabbed image: [" << originalPixmap.width() << "; " << originalPixmap.height() << "]" << std::endl;
|
int loopCnt = 100;
|
||||||
|
timer.start();
|
||||||
|
for (int i=0; i<loopCnt; ++i)
|
||||||
|
{
|
||||||
|
createScreenshot(0,0, decimation, screenshot);
|
||||||
|
}
|
||||||
|
std::cout << "Time required for single screenshot: " << timer.elapsed()/loopCnt << "ms" << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
139
test/TestX11Performance.cpp
Normal file
139
test/TestX11Performance.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
|
||||||
|
// X11 includes
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
void foo_1(int pixelDecimation)
|
||||||
|
{
|
||||||
|
int cropWidth = 0;
|
||||||
|
int cropHeight = 0;
|
||||||
|
|
||||||
|
Image<ColorRgb> image(64, 64);
|
||||||
|
|
||||||
|
/// Reference to the X11 display (nullptr if not opened)
|
||||||
|
Display * x11Display;
|
||||||
|
|
||||||
|
const char * display_name = nullptr;
|
||||||
|
x11Display = XOpenDisplay(display_name);
|
||||||
|
|
||||||
|
std::cout << "Opened display: " << x11Display << std::endl;
|
||||||
|
|
||||||
|
XWindowAttributes window_attributes_return;
|
||||||
|
XGetWindowAttributes(x11Display, DefaultRootWindow(x11Display), &window_attributes_return);
|
||||||
|
|
||||||
|
int screenWidth = window_attributes_return.width;
|
||||||
|
int screenHeight = window_attributes_return.height;
|
||||||
|
std::cout << "[" << screenWidth << "x" << screenHeight <<"]" << std::endl;
|
||||||
|
|
||||||
|
// Update the size of the buffer used to transfer the screenshot
|
||||||
|
int width = (screenWidth - 2 * cropWidth + pixelDecimation/2) / pixelDecimation;
|
||||||
|
int height = (screenHeight - 2 * cropHeight + pixelDecimation/2) / pixelDecimation;
|
||||||
|
image.resize(width, height);
|
||||||
|
|
||||||
|
const int croppedWidth = screenWidth - 2*cropWidth;
|
||||||
|
const int croppedHeight = screenHeight - 2*cropHeight;
|
||||||
|
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
XImage * xImage = XGetImage(x11Display, DefaultRootWindow(x11Display), cropWidth, cropHeight, croppedWidth, croppedHeight, AllPlanes, ZPixmap);
|
||||||
|
|
||||||
|
std::cout << "Captured image: " << xImage << std::endl;
|
||||||
|
|
||||||
|
// Copy the capture XImage to the local image (and apply required decimation)
|
||||||
|
ColorRgb * outputPtr = image.memptr();
|
||||||
|
for (int iY=(pixelDecimation/2); iY<croppedHeight; iY+=pixelDecimation)
|
||||||
|
{
|
||||||
|
for (int iX=(pixelDecimation/2); iX<croppedWidth; iX+=pixelDecimation)
|
||||||
|
{
|
||||||
|
// Extract the pixel from the X11-image
|
||||||
|
const uint32_t pixel = uint32_t(XGetPixel(xImage, iX, iY));
|
||||||
|
|
||||||
|
// Assign the color value
|
||||||
|
outputPtr->red = uint8_t((pixel >> 16) & 0xff);
|
||||||
|
outputPtr->green = uint8_t((pixel >> 8) & 0xff);
|
||||||
|
outputPtr->blue = uint8_t((pixel >> 0) & 0xff);
|
||||||
|
|
||||||
|
// Move to the next output pixel
|
||||||
|
++outputPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup allocated resources of the X11 grab
|
||||||
|
XDestroyImage(xImage);
|
||||||
|
|
||||||
|
std::cout << "Time required: " << timer.elapsed() << " ms" << std::endl;
|
||||||
|
|
||||||
|
XCloseDisplay(x11Display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void foo_2(int pixelDecimation)
|
||||||
|
{
|
||||||
|
int cropWidth = 0;
|
||||||
|
int cropHeight = 0;
|
||||||
|
|
||||||
|
Image<ColorRgb> image(64, 64);
|
||||||
|
|
||||||
|
/// Reference to the X11 display (nullptr if not opened)
|
||||||
|
Display * x11Display;
|
||||||
|
|
||||||
|
const char * display_name = nullptr;
|
||||||
|
x11Display = XOpenDisplay(display_name);
|
||||||
|
|
||||||
|
XWindowAttributes window_attributes_return;
|
||||||
|
XGetWindowAttributes(x11Display, DefaultRootWindow(x11Display), &window_attributes_return);
|
||||||
|
|
||||||
|
int screenWidth = window_attributes_return.width;
|
||||||
|
int screenHeight = window_attributes_return.height;
|
||||||
|
std::cout << "[" << screenWidth << "x" << screenHeight <<"]" << std::endl;
|
||||||
|
|
||||||
|
// Update the size of the buffer used to transfer the screenshot
|
||||||
|
int width = (screenWidth - 2 * cropWidth + pixelDecimation/2) / pixelDecimation;
|
||||||
|
int height = (screenHeight - 2 * cropHeight + pixelDecimation/2) / pixelDecimation;
|
||||||
|
image.resize(width, height);
|
||||||
|
|
||||||
|
const int croppedWidth = screenWidth - 2*cropWidth;
|
||||||
|
const int croppedHeight = screenHeight - 2*cropHeight;
|
||||||
|
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
// Copy the capture XImage to the local image (and apply required decimation)
|
||||||
|
ColorRgb * outputPtr = image.memptr();
|
||||||
|
for (int iY=(pixelDecimation/2); iY<croppedHeight; iY+=pixelDecimation)
|
||||||
|
{
|
||||||
|
for (int iX=(pixelDecimation/2); iX<croppedWidth; iX+=pixelDecimation)
|
||||||
|
{
|
||||||
|
XImage * xImage = XGetImage(x11Display, DefaultRootWindow(x11Display), iX, iY, 1, 1, AllPlanes, ZPixmap);
|
||||||
|
// Extract the pixel from the X11-image
|
||||||
|
const uint32_t pixel = uint32_t(XGetPixel(xImage, 0, 0));
|
||||||
|
|
||||||
|
// Assign the color value
|
||||||
|
outputPtr->red = uint8_t((pixel >> 16) & 0xff);
|
||||||
|
outputPtr->green = uint8_t((pixel >> 8) & 0xff);
|
||||||
|
outputPtr->blue = uint8_t((pixel >> 0) & 0xff);
|
||||||
|
|
||||||
|
// Move to the next output pixel
|
||||||
|
++outputPtr;
|
||||||
|
|
||||||
|
// Cleanup allocated resources of the X11 grab
|
||||||
|
XDestroyImage(xImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "Time required: " << timer.elapsed() << " ms" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
XCloseDisplay(x11Display);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
foo_1(10);
|
||||||
|
foo_2(10);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user