diff --git a/dependencies/build/getoptPlusPlus/getoptpp.cc b/dependencies/build/getoptPlusPlus/getoptpp.cc index 4653ebb9..9c385585 100644 --- a/dependencies/build/getoptPlusPlus/getoptpp.cc +++ b/dependencies/build/getoptPlusPlus/getoptpp.cc @@ -120,7 +120,7 @@ void OptionsParser::usage() const { for(i = parameters.parameters.begin(); i != parameters.parameters.end(); i++) { - cerr.width(30); + cerr.width(31); cerr << std::left << " " + (*i)->usageLine(); cerr.width(40); diff --git a/libsrc/protoserver/CMakeLists.txt b/libsrc/protoserver/CMakeLists.txt index 58d67564..a66ef336 100644 --- a/libsrc/protoserver/CMakeLists.txt +++ b/libsrc/protoserver/CMakeLists.txt @@ -3,9 +3,6 @@ set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver) set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver) -# add protocol buffers -find_package(Protobuf REQUIRED) - include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${PROTOBUF_INCLUDE_DIRS}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d1f51d7d..b06b4ee7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,2 +1,5 @@ add_subdirectory(hyperiond) add_subdirectory(hyperion-remote) +if (ENABLE_V4L2) + add_subdirectory(hyperion-v4l2) +endif (ENABLE_V4L2) diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt new file mode 100644 index 00000000..603ce42e --- /dev/null +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 2.8) + +project(hyperion-v4l2) + +# add protocol buffers +find_package(Protobuf REQUIRED) + +# find Qt4 +find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${PROTOBUF_INCLUDE_DIRS} + ${QT_INCLUDES} +) + +set(Hyperion_V4L2_HEADERS + V4L2Grabber.h + ProtoConnection.h +) + +set(Hyperion_V4L2_SOURCES + hyperion-v4l2.cpp + V4L2Grabber.cpp + ProtoConnection.cpp +) + +set(Hyperion_V4L2_PROTOS + ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/protoserver/message.proto +) + +protobuf_generate_cpp(Hyperion_V4L2_PROTO_SRCS Hyperion_V4L2_PROTO_HDRS + ${Hyperion_V4L2_PROTOS} +) + +add_executable(hyperion-v4l2 + ${Hyperion_V4L2_HEADERS} + ${Hyperion_V4L2_SOURCES} + ${Hyperion_V4L2_PROTO_SRCS} + ${Hyperion_V4L2_PROTO_HDRS} +) + +target_link_libraries(hyperion-v4l2 + getoptPlusPlus + jsoncpp + hyperion-utils + ${PROTOBUF_LIBRARIES} + pthread +) + +qt4_use_modules(hyperion-v4l2 + Core + Gui + Network) diff --git a/src/hyperion-v4l2/ProtoConnection.cpp b/src/hyperion-v4l2/ProtoConnection.cpp new file mode 100644 index 00000000..f679bcbc --- /dev/null +++ b/src/hyperion-v4l2/ProtoConnection.cpp @@ -0,0 +1,181 @@ +// stl includes +#include + +// Qt includes +#include + +// hyperion-v4l2 includes +#include "ProtoConnection.h" + +ProtoConnection::ProtoConnection(const std::string & a) : + _socket() +{ + 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()); + } + + bool ok; + uint16_t 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()); + } + + _socket.connectToHost(parts[0], port); + if (!_socket.waitForConnected()) + { + throw std::runtime_error("Unable to connect to host"); + } + + std::cout << "Connected to " << a << std::endl; +} + +ProtoConnection::~ProtoConnection() +{ + _socket.close(); +} + +void ProtoConnection::setColor(std::vector colors, int priority, int duration) +{ + // create command + Json::Value command; + command["command"] = "color"; + command["priority"] = priority; + Json::Value & rgbValue = command["color"]; + for (const QColor & color : colors) + { + rgbValue.append(color.red()); + rgbValue.append(color.green()); + rgbValue.append(color.blue()); + } + if (duration > 0) + { + command["duration"] = duration; + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +void ProtoConnection::setImage(const Image &image, int priority, int duration) +{ + // ensure the image has RGB888 format + QByteArray binaryImage = QByteArray::fromRawData(reinterpret_cast(image.memptr()), image.width() * image.height() * 3); + const QByteArray base64Image = binaryImage.toBase64(); + + // create command + Json::Value command; + command["command"] = "image"; + command["priority"] = priority; + command["imagewidth"] = image.width(); + command["imageheight"] = image.height(); + command["imagedata"] = std::string(base64Image.data(), base64Image.size()); + if (duration > 0) + { + command["duration"] = duration; + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +void ProtoConnection::clear(int priority) +{ + std::cout << "Clear priority channel " << priority << std::endl; + + // create command + Json::Value command; + command["command"] = "clear"; + command["priority"] = priority; + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +void ProtoConnection::clearAll() +{ + std::cout << "Clear all priority channels" << std::endl; + + // create command + Json::Value command; + command["command"] = "clearall"; + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +Json::Value ProtoConnection::sendMessage(const Json::Value & message) +{ + // serialize message (FastWriter already appends a newline) + std::string serializedMessage = Json::FastWriter().write(message); + + // write message + _socket.write(serializedMessage.c_str()); + if (!_socket.waitForBytesWritten()) + { + throw std::runtime_error("Error while writing data to host"); + } + + // read reply data + QByteArray serializedReply; + while (!serializedReply.contains('\n')) + { + // receive reply + if (!_socket.waitForReadyRead()) + { + throw std::runtime_error("Error while reading data from host"); + } + + serializedReply += _socket.readAll(); + } + int bytes = serializedReply.indexOf('\n') + 1; // Find the end of message + + // parse reply data + Json::Reader jsonReader; + Json::Value reply; + if (!jsonReader.parse(serializedReply.constData(), serializedReply.constData() + bytes, reply)) + { + throw std::runtime_error("Error while parsing reply: invalid json"); + } + + return reply; +} + +bool ProtoConnection::parseReply(const Json::Value &reply) +{ + bool success = false; + std::string reason = "No error info"; + + try + { + success = reply.get("success", false).asBool(); + if (!success) + reason = reply.get("error", reason).asString(); + } + catch (const std::runtime_error &) + { + // Some json parsing error: ignore and set parsing error + } + + if (!success) + { + throw std::runtime_error("Error: " + reason); + } + + return success; +} diff --git a/src/hyperion-v4l2/ProtoConnection.h b/src/hyperion-v4l2/ProtoConnection.h new file mode 100644 index 00000000..cbff597d --- /dev/null +++ b/src/hyperion-v4l2/ProtoConnection.h @@ -0,0 +1,89 @@ +#pragma once + +// stl includes +#include + +// Qt includes +#include +#include +#include +#include + +// hyperion util +#include +#include + +// jsoncpp includes +#include + +/// +/// 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(); + + /// + /// Set all leds to the specified color + /// + /// @param color The color + /// @param priority The priority + /// @param duration The duration in milliseconds + /// + void setColor(std::vector color, int priority, int duration); + + /// + /// 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 & image, int priority, int duration); + + /// + /// Clear the given priority channel + /// + /// @param priority The priority + /// + void clear(int priority); + + /// + /// Clear all priority channels + /// + void clearAll(); + +private: + /// + /// Send a json command message and receive its reply + /// + /// @param message The message to send + /// + /// @return The returned reply + /// + Json::Value sendMessage(const Json::Value & message); + + /// + /// Parse a reply message + /// + /// @param reply The received reply + /// + /// @return true if the reply indicates success + /// + bool parseReply(const Json::Value & reply); + +private: + /// The TCP-Socket with the connection to the server + QTcpSocket _socket; +}; diff --git a/test/v4l2_to_png/V4L2Grabber.cpp b/src/hyperion-v4l2/V4L2Grabber.cpp similarity index 91% rename from test/v4l2_to_png/V4L2Grabber.cpp rename to src/hyperion-v4l2/V4L2Grabber.cpp index af1a3e9f..f971ad25 100644 --- a/test/v4l2_to_png/V4L2Grabber.cpp +++ b/src/hyperion-v4l2/V4L2Grabber.cpp @@ -12,9 +12,6 @@ #include #include -#include -#include - #include "V4L2Grabber.h" #define CLEAR(x) memset(&(x), 0, sizeof(x)) @@ -37,18 +34,20 @@ static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, u } -V4L2Grabber::V4L2Grabber(const std::string &device, int input, VideoStandard videoStandard, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation) : +V4L2Grabber::V4L2Grabber(const std::string &device, int input, VideoStandard videoStandard, int width, int height, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation) : _deviceName(device), _ioMethod(IO_METHOD_MMAP), _fileDescriptor(-1), _buffers(), - _width(0), - _height(0), + _width(width), + _height(height), _cropWidth(cropHorizontal), _cropHeight(cropVertical), _frameDecimation(std::max(1, frameDecimation)), _pixelDecimation(std::max(1, pixelDecimation)), - _currentFrame(0) + _currentFrame(0), + _callback(nullptr), + _callbackArg(nullptr) { open_device(); init_device(videoStandard, input); @@ -60,6 +59,12 @@ V4L2Grabber::~V4L2Grabber() close_device(); } +void V4L2Grabber::setCallback(V4L2Grabber::ImageCallback callback, void *arg) +{ + _callback = callback; + _callbackArg = arg; +} + void V4L2Grabber::start() { start_capturing(); @@ -343,6 +348,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) break; } + // get the current settings struct v4l2_format fmt; CLEAR(fmt); @@ -358,6 +364,32 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) exit(EXIT_FAILURE); } + if (_width > 0 || _height > 0) + { + if (_width > 0) + { + fmt.fmt.pix.width = _width; + } + + if (fmt.fmt.pix.height > 0) + { + fmt.fmt.pix.height = _height; + } + + // set the settings + if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) + { + errno_exit("VIDIOC_S_FMT"); + } + + // get the format settings again + // (the size may not have been accepted without an error) + if (-1 == xioctl(VIDIOC_G_FMT, &fmt)) + { + errno_exit("VIDIOC_G_FMT"); + } + } + // store width & height _width = fmt.fmt.pix.width; _height = fmt.fmt.pix.height; @@ -468,6 +500,8 @@ void V4L2Grabber::stop_capturing() int V4L2Grabber::read_frame() { + bool rc = false; + struct v4l2_buffer buf; switch (_ioMethod) { @@ -490,7 +524,7 @@ int V4L2Grabber::read_frame() } } - process_image(_buffers[0].start, size); + rc = process_image(_buffers[0].start, size); break; case IO_METHOD_MMAP: @@ -518,7 +552,7 @@ int V4L2Grabber::read_frame() assert(buf.index < _buffers.size()); - process_image(_buffers[buf.index].start, buf.bytesused); + rc = process_image(_buffers[buf.index].start, buf.bytesused); if (-1 == xioctl(VIDIOC_QBUF, &buf)) { @@ -558,7 +592,7 @@ int V4L2Grabber::read_frame() } } - process_image((void *)buf.m.userptr, buf.bytesused); + rc = process_image((void *)buf.m.userptr, buf.bytesused); if (-1 == xioctl(VIDIOC_QBUF, &buf)) { @@ -567,10 +601,10 @@ int V4L2Grabber::read_frame() break; } - return 1; + return rc ? 1 : 0; } -void V4L2Grabber::process_image(const void *p, int size) +bool V4L2Grabber::process_image(const void *p, int size) { if (++_currentFrame >= _frameDecimation) { @@ -584,14 +618,15 @@ void V4L2Grabber::process_image(const void *p, int size) { process_image(reinterpret_cast(p)); _currentFrame = 0; // restart counting + return true; } } + + return false; } void V4L2Grabber::process_image(const uint8_t * data) { - std::cout << "process image" << std::endl; - int width = (_width - 2 * _cropWidth + _pixelDecimation/2) / _pixelDecimation; int height = (_height - 2 * _cropHeight + _pixelDecimation/2) / _pixelDecimation; @@ -611,9 +646,10 @@ void V4L2Grabber::process_image(const uint8_t * data) } } - // store as PNG - QImage pngImage((const uint8_t *) image.memptr(), width, height, 3*width, QImage::Format_RGB888); - pngImage.save("screenshot.png"); + if (_callback != nullptr) + { + (*_callback)(_callbackArg, image); + } } int V4L2Grabber::xioctl(int request, void *arg) diff --git a/test/v4l2_to_png/V4L2Grabber.h b/src/hyperion-v4l2/V4L2Grabber.h similarity index 79% rename from test/v4l2_to_png/V4L2Grabber.h rename to src/hyperion-v4l2/V4L2Grabber.h index c028e745..2588e7ce 100644 --- a/test/v4l2_to_png/V4L2Grabber.h +++ b/src/hyperion-v4l2/V4L2Grabber.h @@ -14,14 +14,18 @@ class V4L2Grabber { public: + typedef void (*ImageCallback)(void * arg, const Image & image); + enum VideoStandard { PAL, NTSC, NO_CHANGE }; public: - V4L2Grabber(const std::string & device, int input, VideoStandard videoStandard, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation); + V4L2Grabber(const std::string & device, int input, VideoStandard videoStandard, int width, int height, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation); virtual ~V4L2Grabber(); + void setCallback(ImageCallback callback, void * arg); + void start(); void capture(int frameCount = -1); @@ -49,7 +53,7 @@ private: int read_frame(); - void process_image(const void *p, int size); + bool process_image(const void *p, int size); void process_image(const uint8_t *p); @@ -83,4 +87,7 @@ private: const int _pixelDecimation; int _currentFrame; + + ImageCallback _callback; + void * _callbackArg; }; diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp new file mode 100644 index 00000000..454832be --- /dev/null +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -0,0 +1,143 @@ + +// STL includes +#include +#include + +// QT includes +#include + +// getoptPlusPLus includes +#include + +#include "V4L2Grabber.h" +#include "ProtoConnection.h" + +using namespace vlofgren; + +/// Data parameter for the video standard +typedef vlofgren::PODParameter VideoStandardParameter; + +namespace vlofgren { + /// Translates a string (as passed on the commandline) to a color standard + /// + /// @param[in] s The string (as passed on the commandline) + /// @return The color standard + /// @throws Parameter::ParameterRejected If the string did not result in a video standard + template<> + V4L2Grabber::VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) + { + QString input = QString::fromStdString(s).toLower(); + + if (input == "pal") + { + return V4L2Grabber::PAL; + } + else if (input == "ntsc") + { + return V4L2Grabber::NTSC; + } + else if (input == "no-change") + { + return V4L2Grabber::NO_CHANGE; + } + + throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE"); + return V4L2Grabber::NO_CHANGE; + } +} + +// save the image as screenshot +void saveScreenshot(void *, const Image & image) +{ + // store as PNG + QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); + pngImage.save("screenshot.png"); +} + +// send the image to Hyperion +void sendImage(void * arg, const Image & image) +{ + ProtoConnection * connection = static_cast(arg); + connection->setImage(image, 50, 200); +} + +int main(int argc, char** argv) +{ + try + { + // create the option parser and initialize all parameters + OptionsParser optionParser("Simple application to send a command to hyperion using the Json interface"); + ParameterSet & parameters = optionParser.getParameters(); + + StringParameter & argDevice = parameters.add ('d', "device", "The device to use [default=/dev/video0]"); + VideoStandardParameter & argVideoStandard = parameters.add('v', "video-standard", "The used video standard. Valid values are PAL. NYSC, or NO-CHANGE [default=PAL]"); + IntParameter & argInput = parameters.add (0x0, "input", "Input channel (optional)"); + IntParameter & argWidth = parameters.add (0x0, "width", "Try to set the width of the video input (optional)"); + IntParameter & argHeight = parameters.add (0x0, "height", "Try to set the height of the video input (optional)"); + IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]"); + IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]"); + IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=1]"); + IntParameter & argFrameDecimation = parameters.add ('f', "frame-decimator","Decimation factor for the video frames [default=1]"); + SwitchParameter<> & argScreenshot = parameters.add> (0x0, "screenshot", "Take a single screenshot, save it to file and quit"); + StringParameter & argAddress = parameters.add ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]"); + IntParameter & argPriority = parameters.add ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]"); + SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); + + // set defaults + argDevice.setDefault("/dev/video0"); + argVideoStandard.setDefault(V4L2Grabber::PAL); + argInput.setDefault(-1); + argWidth.setDefault(-1); + argHeight.setDefault(-1); + argCropWidth.setDefault(0); + argCropHeight.setDefault(0); + argSizeDecimation.setDefault(1); + argFrameDecimation.setDefault(1); + argAddress.setDefault("127.0.0.1:19445"); + argPriority.setDefault(800); + + // parse all options + optionParser.parse(argc, const_cast(argv)); + + // check if we need to display the usage. exit if we do. + if (argHelp.isSet()) + { + optionParser.usage(); + return 0; + } + + V4L2Grabber grabber( + argDevice.getValue(), + argInput.getValue(), + argVideoStandard.getValue(), + argWidth.getValue(), + argHeight.getValue(), + std::max(0, argCropWidth.getValue()), + std::max(0, argCropHeight.getValue()), + std::max(1, argFrameDecimation.getValue()), + std::max(1, argSizeDecimation.getValue())); + + grabber.start(); + if (argScreenshot.isSet()) + { + grabber.setCallback(&saveScreenshot, nullptr); + grabber.capture(1); + } + else + { + ProtoConnection connection(argAddress.getValue()); + + grabber.setCallback(&sendImage, &connection); + grabber.capture(); + } + grabber.stop(); + } + catch (const std::runtime_error & e) + { + // An error occured. Display error and quit + std::cerr << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2ac4a4b3..bd46d059 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -35,10 +35,6 @@ if (ENABLE_DISPMANX) add_subdirectory(dispmanx2png) endif (ENABLE_DISPMANX) -if (ENABLE_V4L2) - add_subdirectory(v4l2_to_png) -endif (ENABLE_V4L2) - add_executable(test_blackborderdetector TestBlackBorderDetector.cpp) target_link_libraries(test_blackborderdetector diff --git a/test/v4l2_to_png/CMakeLists.txt b/test/v4l2_to_png/CMakeLists.txt deleted file mode 100644 index ab259048..00000000 --- a/test/v4l2_to_png/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ - -# find Qt4 -find_package(Qt4 REQUIRED QtCore QtGui) - -include_directories(${QT_INCLUDES}) - -add_executable(v4l2_to_png - v4l2_to_png.cpp - V4L2Grabber.h - V4L2Grabber.cpp) - -target_link_libraries(v4l2_to_png - getoptPlusPlus) - -qt4_use_modules(v4l2_to_png - Core - Gui) diff --git a/test/v4l2_to_png/v4l2_to_png.cpp b/test/v4l2_to_png/v4l2_to_png.cpp deleted file mode 100644 index 7f418018..00000000 --- a/test/v4l2_to_png/v4l2_to_png.cpp +++ /dev/null @@ -1,104 +0,0 @@ - -// STL includes -#include -#include - -// QT includes -#include - -// getoptPlusPLus includes -#include - -#include "V4L2Grabber.h" - -using namespace vlofgren; - -/// Data parameter for the video standard -typedef vlofgren::PODParameter VideoStandardParameter; - -namespace vlofgren { - /// Translates a string (as passed on the commandline) to a color standard - /// - /// @param[in] s The string (as passed on the commandline) - /// @return The color standard - /// @throws Parameter::ParameterRejected If the string did not result in a video standard - template<> - V4L2Grabber::VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) - { - QString input = QString::fromStdString(s).toLower(); - - if (input == "pal") - { - return V4L2Grabber::PAL; - } - else if (input == "ntsc") - { - return V4L2Grabber::NTSC; - } - else if (input == "no-change") - { - return V4L2Grabber::NO_CHANGE; - } - - throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE"); - return V4L2Grabber::NO_CHANGE; - } -} - - -int main(int argc, char** argv) -{ - try - { - // create the option parser and initialize all parameters - OptionsParser optionParser("Simple application to send a command to hyperion using the Json interface"); - ParameterSet & parameters = optionParser.getParameters(); - - StringParameter & argDevice = parameters.add ('d', "device", "The device to use [default=/dev/video0]"); - VideoStandardParameter & argVideoStandard = parameters.add('v', "video-standard", "The used video standard. Valid values are PAL. NYSC, or NO-CHANGE [default=PAL]"); - IntParameter & argInput = parameters.add ('i', "input", "Input channel [default=0]"); - IntParameter & argCropWidth = parameters.add (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]"); - IntParameter & argCropHeight = parameters.add (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]"); - IntParameter & argSizeDecimation = parameters.add ('s', "size-decimator", "Decimation factor for the output size [default=1]"); - SwitchParameter<> & argHelp = parameters.add > ('h', "help", "Show this help message and exit"); - - // set defaults - argDevice.setDefault("/dev/video0"); - argVideoStandard.setDefault(V4L2Grabber::PAL); - argInput.setDefault(0); - argCropWidth.setDefault(0); - argCropHeight.setDefault(0); - argSizeDecimation.setDefault(1); - - // parse all options - optionParser.parse(argc, const_cast(argv)); - - // check if we need to display the usage. exit if we do. - if (argHelp.isSet()) - { - optionParser.usage(); - return 0; - } - - V4L2Grabber grabber( - argDevice.getValue(), - argInput.getValue(), - argVideoStandard.getValue(), - std::max(0, argCropWidth.getValue()), - std::max(0, argCropHeight.getValue()), - 1, - argSizeDecimation.getValue()); - - grabber.start(); - grabber.capture(1); - grabber.stop(); - } - catch (const std::runtime_error & e) - { - // An error occured. Display error and quit - std::cerr << e.what() << std::endl; - return 1; - } - - return 0; -}