mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Moved test/v4l2_to_png to src/hyperion-v4l2; Added json backend temporarily
Former-commit-id: 8c4e0ef7add8016c522d0b4c6f4df8886b905e36
This commit is contained in:
parent
1981f6e307
commit
6723c7bf2b
@ -120,7 +120,7 @@ void OptionsParser::usage() const {
|
|||||||
for(i = parameters.parameters.begin();
|
for(i = parameters.parameters.begin();
|
||||||
i != parameters.parameters.end(); i++)
|
i != parameters.parameters.end(); i++)
|
||||||
{
|
{
|
||||||
cerr.width(30);
|
cerr.width(31);
|
||||||
cerr << std::left << " " + (*i)->usageLine();
|
cerr << std::left << " " + (*i)->usageLine();
|
||||||
|
|
||||||
cerr.width(40);
|
cerr.width(40);
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver)
|
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver)
|
||||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver)
|
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver)
|
||||||
|
|
||||||
# add protocol buffers
|
|
||||||
find_package(Protobuf REQUIRED)
|
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
${PROTOBUF_INCLUDE_DIRS})
|
${PROTOBUF_INCLUDE_DIRS})
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
add_subdirectory(hyperiond)
|
add_subdirectory(hyperiond)
|
||||||
add_subdirectory(hyperion-remote)
|
add_subdirectory(hyperion-remote)
|
||||||
|
if (ENABLE_V4L2)
|
||||||
|
add_subdirectory(hyperion-v4l2)
|
||||||
|
endif (ENABLE_V4L2)
|
||||||
|
54
src/hyperion-v4l2/CMakeLists.txt
Normal file
54
src/hyperion-v4l2/CMakeLists.txt
Normal file
@ -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)
|
181
src/hyperion-v4l2/ProtoConnection.cpp
Normal file
181
src/hyperion-v4l2/ProtoConnection.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
// stl includes
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QRgb>
|
||||||
|
|
||||||
|
// 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<QColor> 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<ColorRgb> &image, int priority, int duration)
|
||||||
|
{
|
||||||
|
// ensure the image has RGB888 format
|
||||||
|
QByteArray binaryImage = QByteArray::fromRawData(reinterpret_cast<const char *>(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;
|
||||||
|
}
|
89
src/hyperion-v4l2/ProtoConnection.h
Normal file
89
src/hyperion-v4l2/ProtoConnection.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#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 <json/json.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();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 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<QColor> 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<ColorRgb> & 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;
|
||||||
|
};
|
@ -12,9 +12,6 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
|
||||||
#include <QImage>
|
|
||||||
#include <QRgb>
|
|
||||||
|
|
||||||
#include "V4L2Grabber.h"
|
#include "V4L2Grabber.h"
|
||||||
|
|
||||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
#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),
|
_deviceName(device),
|
||||||
_ioMethod(IO_METHOD_MMAP),
|
_ioMethod(IO_METHOD_MMAP),
|
||||||
_fileDescriptor(-1),
|
_fileDescriptor(-1),
|
||||||
_buffers(),
|
_buffers(),
|
||||||
_width(0),
|
_width(width),
|
||||||
_height(0),
|
_height(height),
|
||||||
_cropWidth(cropHorizontal),
|
_cropWidth(cropHorizontal),
|
||||||
_cropHeight(cropVertical),
|
_cropHeight(cropVertical),
|
||||||
_frameDecimation(std::max(1, frameDecimation)),
|
_frameDecimation(std::max(1, frameDecimation)),
|
||||||
_pixelDecimation(std::max(1, pixelDecimation)),
|
_pixelDecimation(std::max(1, pixelDecimation)),
|
||||||
_currentFrame(0)
|
_currentFrame(0),
|
||||||
|
_callback(nullptr),
|
||||||
|
_callbackArg(nullptr)
|
||||||
{
|
{
|
||||||
open_device();
|
open_device();
|
||||||
init_device(videoStandard, input);
|
init_device(videoStandard, input);
|
||||||
@ -60,6 +59,12 @@ V4L2Grabber::~V4L2Grabber()
|
|||||||
close_device();
|
close_device();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void V4L2Grabber::setCallback(V4L2Grabber::ImageCallback callback, void *arg)
|
||||||
|
{
|
||||||
|
_callback = callback;
|
||||||
|
_callbackArg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
void V4L2Grabber::start()
|
void V4L2Grabber::start()
|
||||||
{
|
{
|
||||||
start_capturing();
|
start_capturing();
|
||||||
@ -343,6 +348,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get the current settings
|
// get the current settings
|
||||||
struct v4l2_format fmt;
|
struct v4l2_format fmt;
|
||||||
CLEAR(fmt);
|
CLEAR(fmt);
|
||||||
@ -358,6 +364,32 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|||||||
exit(EXIT_FAILURE);
|
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
|
// store width & height
|
||||||
_width = fmt.fmt.pix.width;
|
_width = fmt.fmt.pix.width;
|
||||||
_height = fmt.fmt.pix.height;
|
_height = fmt.fmt.pix.height;
|
||||||
@ -468,6 +500,8 @@ void V4L2Grabber::stop_capturing()
|
|||||||
|
|
||||||
int V4L2Grabber::read_frame()
|
int V4L2Grabber::read_frame()
|
||||||
{
|
{
|
||||||
|
bool rc = false;
|
||||||
|
|
||||||
struct v4l2_buffer buf;
|
struct v4l2_buffer buf;
|
||||||
|
|
||||||
switch (_ioMethod) {
|
switch (_ioMethod) {
|
||||||
@ -490,7 +524,7 @@ int V4L2Grabber::read_frame()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
process_image(_buffers[0].start, size);
|
rc = process_image(_buffers[0].start, size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_METHOD_MMAP:
|
case IO_METHOD_MMAP:
|
||||||
@ -518,7 +552,7 @@ int V4L2Grabber::read_frame()
|
|||||||
|
|
||||||
assert(buf.index < _buffers.size());
|
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))
|
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))
|
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||||
{
|
{
|
||||||
@ -567,10 +601,10 @@ int V4L2Grabber::read_frame()
|
|||||||
break;
|
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)
|
if (++_currentFrame >= _frameDecimation)
|
||||||
{
|
{
|
||||||
@ -584,14 +618,15 @@ void V4L2Grabber::process_image(const void *p, int size)
|
|||||||
{
|
{
|
||||||
process_image(reinterpret_cast<const uint8_t *>(p));
|
process_image(reinterpret_cast<const uint8_t *>(p));
|
||||||
_currentFrame = 0; // restart counting
|
_currentFrame = 0; // restart counting
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void V4L2Grabber::process_image(const uint8_t * data)
|
void V4L2Grabber::process_image(const uint8_t * data)
|
||||||
{
|
{
|
||||||
std::cout << "process image" << std::endl;
|
|
||||||
|
|
||||||
int width = (_width - 2 * _cropWidth + _pixelDecimation/2) / _pixelDecimation;
|
int width = (_width - 2 * _cropWidth + _pixelDecimation/2) / _pixelDecimation;
|
||||||
int height = (_height - 2 * _cropHeight + _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
|
if (_callback != nullptr)
|
||||||
QImage pngImage((const uint8_t *) image.memptr(), width, height, 3*width, QImage::Format_RGB888);
|
{
|
||||||
pngImage.save("screenshot.png");
|
(*_callback)(_callbackArg, image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int V4L2Grabber::xioctl(int request, void *arg)
|
int V4L2Grabber::xioctl(int request, void *arg)
|
@ -14,14 +14,18 @@
|
|||||||
class V4L2Grabber
|
class V4L2Grabber
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
typedef void (*ImageCallback)(void * arg, const Image<ColorRgb> & image);
|
||||||
|
|
||||||
enum VideoStandard {
|
enum VideoStandard {
|
||||||
PAL, NTSC, NO_CHANGE
|
PAL, NTSC, NO_CHANGE
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
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();
|
virtual ~V4L2Grabber();
|
||||||
|
|
||||||
|
void setCallback(ImageCallback callback, void * arg);
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
void capture(int frameCount = -1);
|
void capture(int frameCount = -1);
|
||||||
@ -49,7 +53,7 @@ private:
|
|||||||
|
|
||||||
int read_frame();
|
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);
|
void process_image(const uint8_t *p);
|
||||||
|
|
||||||
@ -83,4 +87,7 @@ private:
|
|||||||
const int _pixelDecimation;
|
const int _pixelDecimation;
|
||||||
|
|
||||||
int _currentFrame;
|
int _currentFrame;
|
||||||
|
|
||||||
|
ImageCallback _callback;
|
||||||
|
void * _callbackArg;
|
||||||
};
|
};
|
143
src/hyperion-v4l2/hyperion-v4l2.cpp
Normal file
143
src/hyperion-v4l2/hyperion-v4l2.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
|
||||||
|
// STL includes
|
||||||
|
#include <csignal>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
// QT includes
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
// getoptPlusPLus includes
|
||||||
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
|
|
||||||
|
#include "V4L2Grabber.h"
|
||||||
|
#include "ProtoConnection.h"
|
||||||
|
|
||||||
|
using namespace vlofgren;
|
||||||
|
|
||||||
|
/// Data parameter for the video standard
|
||||||
|
typedef vlofgren::PODParameter<V4L2Grabber::VideoStandard> VideoStandardParameter;
|
||||||
|
|
||||||
|
namespace vlofgren {
|
||||||
|
/// Translates a string (as passed on the commandline) to a color standard
|
||||||
|
///
|
||||||
|
/// @param[in] s The string (as passed on the commandline)
|
||||||
|
/// @return The color standard
|
||||||
|
/// @throws Parameter::ParameterRejected If the string did not result in a video standard
|
||||||
|
template<>
|
||||||
|
V4L2Grabber::VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
||||||
|
{
|
||||||
|
QString input = QString::fromStdString(s).toLower();
|
||||||
|
|
||||||
|
if (input == "pal")
|
||||||
|
{
|
||||||
|
return V4L2Grabber::PAL;
|
||||||
|
}
|
||||||
|
else if (input == "ntsc")
|
||||||
|
{
|
||||||
|
return V4L2Grabber::NTSC;
|
||||||
|
}
|
||||||
|
else if (input == "no-change")
|
||||||
|
{
|
||||||
|
return V4L2Grabber::NO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE");
|
||||||
|
return V4L2Grabber::NO_CHANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the image as screenshot
|
||||||
|
void saveScreenshot(void *, const Image<ColorRgb> & image)
|
||||||
|
{
|
||||||
|
// store as PNG
|
||||||
|
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
|
||||||
|
pngImage.save("screenshot.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the image to Hyperion
|
||||||
|
void sendImage(void * arg, const Image<ColorRgb> & image)
|
||||||
|
{
|
||||||
|
ProtoConnection * connection = static_cast<ProtoConnection *>(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<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. NYSC, or NO-CHANGE [default=PAL]");
|
||||||
|
IntParameter & argInput = parameters.add<IntParameter> (0x0, "input", "Input channel (optional)");
|
||||||
|
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Try to set the width of the video input (optional)");
|
||||||
|
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Try to set the height of the video input (optional)");
|
||||||
|
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]");
|
||||||
|
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]");
|
||||||
|
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=1]");
|
||||||
|
IntParameter & argFrameDecimation = parameters.add<IntParameter> ('f', "frame-decimator","Decimation factor for the video frames [default=1]");
|
||||||
|
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
|
||||||
|
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<> & argHelp = parameters.add<SwitchParameter<>> ('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<const char **>(argv));
|
||||||
|
|
||||||
|
// check if we need to display the usage. exit if we do.
|
||||||
|
if (argHelp.isSet())
|
||||||
|
{
|
||||||
|
optionParser.usage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
V4L2Grabber grabber(
|
||||||
|
argDevice.getValue(),
|
||||||
|
argInput.getValue(),
|
||||||
|
argVideoStandard.getValue(),
|
||||||
|
argWidth.getValue(),
|
||||||
|
argHeight.getValue(),
|
||||||
|
std::max(0, argCropWidth.getValue()),
|
||||||
|
std::max(0, argCropHeight.getValue()),
|
||||||
|
std::max(1, argFrameDecimation.getValue()),
|
||||||
|
std::max(1, argSizeDecimation.getValue()));
|
||||||
|
|
||||||
|
grabber.start();
|
||||||
|
if (argScreenshot.isSet())
|
||||||
|
{
|
||||||
|
grabber.setCallback(&saveScreenshot, nullptr);
|
||||||
|
grabber.capture(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
@ -35,10 +35,6 @@ if (ENABLE_DISPMANX)
|
|||||||
add_subdirectory(dispmanx2png)
|
add_subdirectory(dispmanx2png)
|
||||||
endif (ENABLE_DISPMANX)
|
endif (ENABLE_DISPMANX)
|
||||||
|
|
||||||
if (ENABLE_V4L2)
|
|
||||||
add_subdirectory(v4l2_to_png)
|
|
||||||
endif (ENABLE_V4L2)
|
|
||||||
|
|
||||||
add_executable(test_blackborderdetector
|
add_executable(test_blackborderdetector
|
||||||
TestBlackBorderDetector.cpp)
|
TestBlackBorderDetector.cpp)
|
||||||
target_link_libraries(test_blackborderdetector
|
target_link_libraries(test_blackborderdetector
|
||||||
|
@ -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)
|
|
@ -1,104 +0,0 @@
|
|||||||
|
|
||||||
// STL includes
|
|
||||||
#include <csignal>
|
|
||||||
#include <iomanip>
|
|
||||||
|
|
||||||
// QT includes
|
|
||||||
#include <QImage>
|
|
||||||
|
|
||||||
// getoptPlusPLus includes
|
|
||||||
#include <getoptPlusPlus/getoptpp.h>
|
|
||||||
|
|
||||||
#include "V4L2Grabber.h"
|
|
||||||
|
|
||||||
using namespace vlofgren;
|
|
||||||
|
|
||||||
/// Data parameter for the video standard
|
|
||||||
typedef vlofgren::PODParameter<V4L2Grabber::VideoStandard> VideoStandardParameter;
|
|
||||||
|
|
||||||
namespace vlofgren {
|
|
||||||
/// Translates a string (as passed on the commandline) to a color standard
|
|
||||||
///
|
|
||||||
/// @param[in] s The string (as passed on the commandline)
|
|
||||||
/// @return The color standard
|
|
||||||
/// @throws Parameter::ParameterRejected If the string did not result in a video standard
|
|
||||||
template<>
|
|
||||||
V4L2Grabber::VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
|
||||||
{
|
|
||||||
QString input = QString::fromStdString(s).toLower();
|
|
||||||
|
|
||||||
if (input == "pal")
|
|
||||||
{
|
|
||||||
return V4L2Grabber::PAL;
|
|
||||||
}
|
|
||||||
else if (input == "ntsc")
|
|
||||||
{
|
|
||||||
return V4L2Grabber::NTSC;
|
|
||||||
}
|
|
||||||
else if (input == "no-change")
|
|
||||||
{
|
|
||||||
return V4L2Grabber::NO_CHANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE");
|
|
||||||
return V4L2Grabber::NO_CHANGE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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<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. NYSC, or NO-CHANGE [default=PAL]");
|
|
||||||
IntParameter & argInput = parameters.add<IntParameter> ('i', "input", "Input channel [default=0]");
|
|
||||||
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]");
|
|
||||||
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]");
|
|
||||||
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=1]");
|
|
||||||
SwitchParameter<> & argHelp = parameters.add<SwitchParameter<> > ('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<const char **>(argv));
|
|
||||||
|
|
||||||
// check if we need to display the usage. exit if we do.
|
|
||||||
if (argHelp.isSet())
|
|
||||||
{
|
|
||||||
optionParser.usage();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
V4L2Grabber grabber(
|
|
||||||
argDevice.getValue(),
|
|
||||||
argInput.getValue(),
|
|
||||||
argVideoStandard.getValue(),
|
|
||||||
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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user