mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
feat: SchemaChecker & V4L2 enhancement (#734)
* libjpeg-turbo, QJsonSchemaChecker, V4L2 width/height/fps Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com> * Implement hyperion-v4l cli args * Apply v4l2 settings during runtime * feat: Provide minimum values for input restriction * fix: merge mess Co-authored-by: brindosch <edeltraud70@gmx.de>
This commit is contained in:
parent
20a5e5dc06
commit
662872dafe
@ -298,22 +298,31 @@ find_package(libusb-1.0 REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
|
||||
# Add jpeg library
|
||||
# Add JPEG library
|
||||
if (ENABLE_V4L2)
|
||||
# Turbo JPEG
|
||||
find_package(TurboJPEG)
|
||||
if (TURBOJPEG_FOUND)
|
||||
add_definitions(-DHAVE_TURBO_JPEG)
|
||||
message( STATUS "Using Turbo JPEG library: ${TurboJPEG_LIBRARY}")
|
||||
include_directories(${TurboJPEG_INCLUDE_DIRS})
|
||||
else()
|
||||
# System JPEG
|
||||
find_package(JPEG)
|
||||
if (JPEG_FOUND)
|
||||
add_definitions(-DHAVE_JPEG)
|
||||
message( STATUS "Using JPEG library: ${JPEG_LIBRARIES}")
|
||||
message( STATUS "Using system JPEG library: ${JPEG_LIBRARIES}")
|
||||
include_directories(${JPEG_INCLUDE_DIR})
|
||||
else()
|
||||
message( STATUS "JPEG library not found, MJPEG camera format won't work in V4L2 grabber.")
|
||||
endif()
|
||||
endif()
|
||||
endif (TurboJPEG_FOUND)
|
||||
|
||||
# TODO[TvdZ]: This linking directory should only be added if we are cross compiling
|
||||
#if(NOT APPLE)
|
||||
# link_directories(${CMAKE_FIND_ROOT_PATH}/lib/arm-linux-gnueabihf)
|
||||
#endif()
|
||||
|
||||
if (TURBOJPEG_FOUND OR JPEG_FOUND)
|
||||
add_definitions(-DHAVE_JPEG_DECODER)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-framework CoreGraphics")
|
||||
|
@ -40,7 +40,7 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libqt5sql5-sqlite libssl-dev
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libqt5sql5-sqlite libssl-dev
|
||||
```
|
||||
|
||||
**on RPI you need the videocore IV headers**
|
||||
|
@ -4,14 +4,14 @@ Use a clean Raspbian Stretch Lite (on target) and Ubuntu 18/19 (on host) to exec
|
||||
## On the Target system (here Raspberry Pi)
|
||||
Install required additional packages.
|
||||
```
|
||||
sudo apt-get install qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libqt5sql5-sqlite aptitude show qt5-default rsync
|
||||
sudo apt-get install qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libqt5sql5-sqlite aptitude show qt5-default rsync
|
||||
```
|
||||
## On the Host system (here Ubuntu)
|
||||
Update the Ubuntu environment to the latest stage and install required additional packages.
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
sudo apt-get -qq -y install git rsync cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libqt5sql5-sqlite
|
||||
sudo apt-get -qq -y install git rsync cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libqt5sql5-sqlite
|
||||
```
|
||||
|
||||
Refine the target IP or hostname, plus userID as required and set-up cross-compilation environment:
|
||||
|
@ -5,7 +5,7 @@ CFG="${2:-Release}"
|
||||
INST="$( [ "${3:-}" = "install" ] && echo true || echo false )"
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libssl-dev || exit 1
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev libturbojpeg0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libssl-dev || exit 1
|
||||
|
||||
if [ -e /dev/vc-cma -a -e /dev/vc-mem ]
|
||||
then
|
||||
|
33
cmake/FindTurboJPEG.cmake
Normal file
33
cmake/FindTurboJPEG.cmake
Normal file
@ -0,0 +1,33 @@
|
||||
# FindTurboJPEG.cmake
|
||||
# TURBOJPEG_FOUND
|
||||
# TurboJPEG_INCLUDE_DIRS
|
||||
# TurboJPEG_LIBRARY
|
||||
|
||||
find_path(TurboJPEG_INCLUDE_DIRS
|
||||
NAMES turbojpeg.h
|
||||
PATH_SUFFIXES include
|
||||
)
|
||||
|
||||
find_library(TurboJPEG_LIBRARY
|
||||
NAMES turbojpeg turbojpeg-static
|
||||
PATH_SUFFIXES bin lib
|
||||
)
|
||||
|
||||
if(TurboJPEG_INCLUDE_DIRS AND TurboJPEG_LIBRARY)
|
||||
include(CheckCSourceCompiles)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
cmake_push_check_state(RESET)
|
||||
list(APPEND CMAKE_REQUIRED_INCLUDES ${TurboJPEG_INCLUDE_DIRS})
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${TurboJPEG_LIBRARY})
|
||||
|
||||
check_c_source_compiles("#include <turbojpeg.h>\nint main(void) { tjhandle h=tjInitCompress(); return 0; }" TURBOJPEG_WORKS)
|
||||
cmake_pop_check_state()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(TurboJpeg
|
||||
FOUND_VAR TURBOJPEG_FOUND
|
||||
REQUIRED_VARS TurboJPEG_LIBRARY TurboJPEG_INCLUDE_DIRS TURBOJPEG_WORKS
|
||||
TurboJPEG_INCLUDE_DIRS TurboJPEG_LIBRARY
|
||||
)
|
@ -101,6 +101,8 @@
|
||||
|
||||
/// Configuration for the embedded V4L2 grabber
|
||||
/// * device : V4L2 Device to use [default="auto"] (Auto detection)
|
||||
/// * width : The width of the grabbed frames (pixels) [default=0]
|
||||
/// * height : The height of the grabbed frames (pixels) [default=0]
|
||||
/// * standard : Video standard (PAL/NTSC/SECAM/NO_CHANGE) [default="NO_CHANGE"]
|
||||
/// * sizeDecimation : Size decimation factor [default=8]
|
||||
/// * cropLeft : Cropping from the left [default=0]
|
||||
@ -118,6 +120,8 @@
|
||||
"grabberV4L2" :
|
||||
{
|
||||
"device" : "auto",
|
||||
"width" : 0,
|
||||
"height" : 0,
|
||||
"standard" : "NO_CHANGE",
|
||||
"sizeDecimation" : 8,
|
||||
"priority" : 240,
|
||||
|
@ -60,6 +60,9 @@
|
||||
"grabberV4L2" :
|
||||
{
|
||||
"device" : "auto",
|
||||
"width" : 0,
|
||||
"height" : 0,
|
||||
"fps" : 15,
|
||||
"standard" : "NO_CHANGE",
|
||||
"sizeDecimation" : 8,
|
||||
"cropLeft" : 0,
|
||||
|
@ -15,13 +15,23 @@
|
||||
#include <grabber/VideoStandard.h>
|
||||
#include <utils/Components.h>
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
// general JPEG decoder includes
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
#include <QImage>
|
||||
#include <QColor>
|
||||
#endif
|
||||
|
||||
// System JPEG decoder
|
||||
#ifdef HAVE_JPEG
|
||||
#include <jpeglib.h>
|
||||
#include <csetjmp>
|
||||
#endif
|
||||
|
||||
// TurboJPEG decoder
|
||||
#ifdef HAVE_TURBO_JPEG
|
||||
#include <turbojpeg.h>
|
||||
#endif
|
||||
|
||||
/// Capture class for V4L2 devices
|
||||
///
|
||||
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
|
||||
@ -31,6 +41,9 @@ class V4L2Grabber : public Grabber
|
||||
|
||||
public:
|
||||
V4L2Grabber(const QString & device,
|
||||
const unsigned width,
|
||||
const unsigned height,
|
||||
const unsigned fps,
|
||||
VideoStandard videoStandard,
|
||||
PixelFormat pixelFormat,
|
||||
int pixelDecimation
|
||||
@ -46,11 +59,6 @@ public:
|
||||
|
||||
int grabFrame(Image<ColorRgb> &);
|
||||
|
||||
///
|
||||
/// @brief overwrite Grabber.h implementation, as v4l doesn't use width/height
|
||||
///
|
||||
virtual void setWidthHeight(){};
|
||||
|
||||
///
|
||||
/// @brief set new PixelDecimation value to ImageResampler
|
||||
/// @param pixelDecimation The new pixelDecimation value
|
||||
@ -84,6 +92,16 @@ public:
|
||||
///
|
||||
virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard);
|
||||
|
||||
///
|
||||
/// @brief overwrite Grabber.h implementation
|
||||
///
|
||||
virtual bool setFramerate(int fps);
|
||||
|
||||
///
|
||||
/// @brief overwrite Grabber.h implementation
|
||||
///
|
||||
virtual bool setWidthHeight(int width, int height);
|
||||
|
||||
public slots:
|
||||
|
||||
bool start();
|
||||
@ -173,6 +191,11 @@ private:
|
||||
errorManager* _error;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TURBO_JPEG
|
||||
tjhandle _decompress = nullptr;
|
||||
int _subsamp;
|
||||
#endif
|
||||
|
||||
private:
|
||||
QString _deviceName;
|
||||
std::map<QString,QString> _v4lDevices;
|
||||
|
@ -9,6 +9,9 @@ class V4L2Wrapper : public GrabberWrapper
|
||||
|
||||
public:
|
||||
V4L2Wrapper(const QString & device,
|
||||
const unsigned grabWidth,
|
||||
const unsigned grabHeight,
|
||||
const unsigned fps,
|
||||
VideoStandard videoStandard,
|
||||
PixelFormat pixelFormat,
|
||||
int pixelDecimation );
|
||||
|
@ -40,6 +40,12 @@ public:
|
||||
///
|
||||
virtual bool setWidthHeight(int width, int height);
|
||||
|
||||
///
|
||||
/// @brief Apply new framerate (used from v4l)
|
||||
/// @param fps framesPerSecond
|
||||
///
|
||||
virtual bool setFramerate(int fps);
|
||||
|
||||
///
|
||||
/// @brief Apply new pixelDecimation (used from x11 and qt)
|
||||
///
|
||||
@ -111,6 +117,8 @@ protected:
|
||||
/// Height of the captured snapshot [pixels]
|
||||
int _height;
|
||||
|
||||
int _fps;
|
||||
|
||||
// number of pixels to crop after capturing
|
||||
int _cropLeft, _cropRight, _cropTop, _cropBottom;
|
||||
|
||||
|
@ -12,7 +12,7 @@ enum PixelFormat {
|
||||
PIXELFORMAT_BGR24,
|
||||
PIXELFORMAT_RGB32,
|
||||
PIXELFORMAT_BGR32,
|
||||
#ifdef HAVE_JPEG
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
PIXELFORMAT_MJPEG,
|
||||
#endif
|
||||
PIXELFORMAT_NO_CHANGE
|
||||
@ -47,7 +47,7 @@ inline PixelFormat parsePixelFormat(QString pixelFormat)
|
||||
{
|
||||
return PIXELFORMAT_BGR32;
|
||||
}
|
||||
#ifdef HAVE_JPEG
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
else if (pixelFormat == "mjpeg")
|
||||
{
|
||||
return PIXELFORMAT_MJPEG;
|
||||
|
@ -187,6 +187,14 @@ private:
|
||||
///
|
||||
void checkEnum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// @brief Return the "default" value as string. If not found, an empty string is output
|
||||
///
|
||||
/// @param value The JSON value to search
|
||||
/// @return The "default" value as string
|
||||
///
|
||||
QString getDefaultValue(const QJsonValue & value);
|
||||
|
||||
private:
|
||||
/// The schema of the entire json-configuration
|
||||
QJsonObject _qSchema;
|
||||
|
@ -41,6 +41,37 @@ public:
|
||||
return createValue(schema, ignoreRequired);
|
||||
}
|
||||
|
||||
static QString getDefaultValue(const QJsonValue & value)
|
||||
{
|
||||
QString ret;
|
||||
switch (value.type())
|
||||
{
|
||||
case QJsonValue::Array:
|
||||
{
|
||||
for (const QJsonValue &v : value.toArray())
|
||||
{
|
||||
ret = getDefaultValue(v);
|
||||
if (!ret.isEmpty())
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QJsonValue::Object:
|
||||
ret = getDefaultValue(value.toObject().find("default").value());
|
||||
break;
|
||||
case QJsonValue::Bool:
|
||||
return value.toBool() ? "True" : "False";
|
||||
case QJsonValue::Double:
|
||||
return QString::number(value.toDouble());
|
||||
case QJsonValue::String:
|
||||
return value.toString();
|
||||
case QJsonValue::Null:
|
||||
case QJsonValue::Undefined:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static QJsonValue createValue(QJsonValue schema, bool ignoreRequired)
|
||||
|
@ -11,6 +11,8 @@ target_link_libraries(v4l2-grabber
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
if (JPEG_FOUND)
|
||||
if(TURBOJPEG_FOUND)
|
||||
target_link_libraries(v4l2-grabber ${TurboJPEG_LIBRARY})
|
||||
elseif (JPEG_FOUND)
|
||||
target_link_libraries(v4l2-grabber ${JPEG_LIBRARY})
|
||||
endif()
|
||||
endif(TURBOJPEG_FOUND)
|
||||
|
@ -27,6 +27,9 @@
|
||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
V4L2Grabber::V4L2Grabber(const QString & device
|
||||
, const unsigned width
|
||||
, const unsigned height
|
||||
, const unsigned fps
|
||||
, VideoStandard videoStandard
|
||||
, PixelFormat pixelFormat
|
||||
, int pixelDecimation
|
||||
@ -59,6 +62,8 @@ V4L2Grabber::V4L2Grabber(const QString & device
|
||||
getV4Ldevices();
|
||||
|
||||
// init
|
||||
setWidthHeight(width, height);
|
||||
setFramerate(fps);
|
||||
setDeviceVideoStandard(device, videoStandard);
|
||||
}
|
||||
|
||||
@ -561,7 +566,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
|
||||
break;
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
case PIXELFORMAT_MJPEG:
|
||||
{
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
|
||||
@ -576,53 +581,41 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
break;
|
||||
}
|
||||
|
||||
// get maximum video devices resolution
|
||||
__u32 max_width = 0, max_height = 0;
|
||||
struct v4l2_fmtdesc fmtdesc;
|
||||
CLEAR(fmtdesc);
|
||||
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmtdesc.index = 0;
|
||||
while (xioctl(VIDIOC_ENUM_FMT, &fmtdesc) >= 0)
|
||||
{
|
||||
// collect available device resolutions
|
||||
QString v4lDevice_res;
|
||||
v4l2_frmsizeenum frmsizeenum;
|
||||
CLEAR(frmsizeenum);
|
||||
frmsizeenum.pixel_format = fmtdesc.pixelformat;
|
||||
frmsizeenum.index = 0;
|
||||
frmsizeenum.pixel_format = fmt.fmt.pix.pixelformat;
|
||||
while (xioctl(VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) >= 0)
|
||||
{
|
||||
switch (frmsizeenum.type)
|
||||
{
|
||||
case V4L2_FRMSIZE_TYPE_DISCRETE:
|
||||
{
|
||||
max_width = std::max(max_width, frmsizeenum.discrete.width);
|
||||
max_height = std::max(max_height, frmsizeenum.discrete.height);
|
||||
}
|
||||
v4lDevice_res += "\t"+ QString::number(frmsizeenum.discrete.width) + "x" + QString::number(frmsizeenum.discrete.height) + "\n";
|
||||
break;
|
||||
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
|
||||
case V4L2_FRMSIZE_TYPE_STEPWISE:
|
||||
{
|
||||
max_width = std::max(max_width, frmsizeenum.stepwise.max_width);
|
||||
max_height = std::max(max_height, frmsizeenum.stepwise.max_height);
|
||||
for(unsigned int y = frmsizeenum.stepwise.min_height; y <= frmsizeenum.stepwise.max_height; y += frmsizeenum.stepwise.step_height)
|
||||
{
|
||||
for(unsigned int x = frmsizeenum.stepwise.min_width; x <= frmsizeenum.stepwise.max_width; x += frmsizeenum.stepwise.step_width)
|
||||
{
|
||||
v4lDevice_res += "\t"+ QString::number(x) + "x" + QString::number(y) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frmsizeenum.index++;
|
||||
}
|
||||
|
||||
fmtdesc.index++;
|
||||
}
|
||||
// print available device resolutions in debug mode
|
||||
if (!v4lDevice_res.isEmpty())
|
||||
Debug(_log, "available V4L2 resolutions:\n%s", QSTRING_CSTR(v4lDevice_res));
|
||||
|
||||
// set the settings
|
||||
if (max_width != 0 || max_height != 0)
|
||||
{
|
||||
fmt.fmt.pix.width = max_width;
|
||||
fmt.fmt.pix.height = max_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt.fmt.pix.width = _width;
|
||||
fmt.fmt.pix.height = _height;
|
||||
}
|
||||
|
||||
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
|
||||
{
|
||||
@ -652,14 +645,18 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (streamparms.parm.capture.capability == V4L2_CAP_TIMEPERFRAME)
|
||||
{
|
||||
// Driver supports the feature. Set required framerate
|
||||
streamparms.parm.capture.capturemode = V4L2_MODE_HIGHQUALITY;
|
||||
CLEAR(streamparms);
|
||||
streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
streamparms.parm.capture.timeperframe.numerator = 1;
|
||||
streamparms.parm.capture.timeperframe.denominator = 30;
|
||||
streamparms.parm.capture.timeperframe.denominator = _fps;
|
||||
if(-1 == xioctl(VIDIOC_S_PARM, &streamparms))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_PARM");
|
||||
// continue
|
||||
}
|
||||
else
|
||||
// display the used framerate
|
||||
Debug(_log, "Set framerate to %d fps", _fps);
|
||||
}
|
||||
}
|
||||
|
||||
@ -693,7 +690,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
{
|
||||
_pixelFormat = PIXELFORMAT_MJPEG;
|
||||
@ -703,7 +700,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
#endif
|
||||
|
||||
default:
|
||||
#ifdef HAVE_JPEG
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
throw_exception("Only pixel formats UYVY, YUYV, RGB32 and MJPEG are supported");
|
||||
#else
|
||||
throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported");
|
||||
@ -955,7 +952,7 @@ int V4L2Grabber::read_frame()
|
||||
bool V4L2Grabber::process_image(const void *p, int size)
|
||||
{
|
||||
// We do want a new frame...
|
||||
#ifdef HAVE_JPEG
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
if (size != _frameByteSize && _pixelFormat != PIXELFORMAT_MJPEG)
|
||||
#else
|
||||
if (size != _frameByteSize)
|
||||
@ -976,9 +973,15 @@ void V4L2Grabber::process_image(const uint8_t * data, int size)
|
||||
{
|
||||
Image<ColorRgb> image(_width, _height);
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
/* ----------------------------------------------------------
|
||||
* ----------- BEGIN of JPEG decoder related code -----------
|
||||
* --------------------------------------------------------*/
|
||||
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
if (_pixelFormat == PIXELFORMAT_MJPEG)
|
||||
{
|
||||
#endif
|
||||
#ifdef HAVE_JPEG
|
||||
_decompress = new jpeg_decompress_struct;
|
||||
_error = new errorManager;
|
||||
|
||||
@ -1048,7 +1051,31 @@ void V4L2Grabber::process_image(const uint8_t * data, int size)
|
||||
|
||||
if (imageFrame.isNull() || _error->pub.num_warnings > 0)
|
||||
return;
|
||||
#endif
|
||||
#ifdef HAVE_TURBO_JPEG
|
||||
_decompress = tjInitDecompress();
|
||||
if (_decompress == nullptr)
|
||||
return;
|
||||
|
||||
if (tjDecompressHeader2(_decompress, const_cast<uint8_t*>(data), size, &_width, &_height, &_subsamp) != 0)
|
||||
{
|
||||
tjDestroy(_decompress);
|
||||
return;
|
||||
}
|
||||
|
||||
QImage imageFrame = QImage(_width, _height, QImage::Format_RGB888);
|
||||
if (tjDecompress2(_decompress, const_cast<uint8_t*>(data), size, imageFrame.bits(), _width, 0, _height, TJPF_RGB, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0)
|
||||
{
|
||||
tjDestroy(_decompress);
|
||||
return;
|
||||
}
|
||||
|
||||
tjDestroy(_decompress);
|
||||
|
||||
if (imageFrame.isNull())
|
||||
return;
|
||||
#endif
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
QRect rect(_cropLeft, _cropTop, imageFrame.width() - _cropLeft - _cropRight, imageFrame.height() - _cropTop - _cropBottom);
|
||||
imageFrame = imageFrame.copy(rect);
|
||||
imageFrame = imageFrame.scaled(imageFrame.width() / _pixelDecimation, imageFrame.height() / _pixelDecimation,Qt::KeepAspectRatio);
|
||||
@ -1068,6 +1095,11 @@ void V4L2Grabber::process_image(const uint8_t * data, int size)
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
* ------------ END of JPEG decoder related code ------------
|
||||
* --------------------------------------------------------*/
|
||||
|
||||
_imageResampler.processImage(data, _width, _height, _lineLength, _pixelFormat, image);
|
||||
|
||||
if (_signalDetectionEnabled)
|
||||
@ -1171,3 +1203,27 @@ void V4L2Grabber::setDeviceVideoStandard(QString device, VideoStandard videoStan
|
||||
if(started) start();
|
||||
}
|
||||
}
|
||||
|
||||
bool V4L2Grabber::setFramerate(int fps)
|
||||
{
|
||||
if(Grabber::setFramerate(fps))
|
||||
{
|
||||
bool started = _initialized;
|
||||
uninit();
|
||||
if(started) start();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool V4L2Grabber::setWidthHeight(int width, int height)
|
||||
{
|
||||
if(Grabber::setWidthHeight(width,height))
|
||||
{
|
||||
bool started = _initialized;
|
||||
uninit();
|
||||
if(started) start();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -6,11 +6,17 @@
|
||||
#include <QTimer>
|
||||
|
||||
V4L2Wrapper::V4L2Wrapper(const QString &device,
|
||||
const unsigned grabWidth,
|
||||
const unsigned grabHeight,
|
||||
const unsigned fps,
|
||||
VideoStandard videoStandard,
|
||||
PixelFormat pixelFormat,
|
||||
int pixelDecimation )
|
||||
: GrabberWrapper("V4L2:"+device, &_grabber, 0, 0, 10)
|
||||
: GrabberWrapper("V4L2:"+device, &_grabber, grabWidth, grabHeight, 10)
|
||||
, _grabber(device,
|
||||
grabWidth,
|
||||
grabHeight,
|
||||
fps,
|
||||
videoStandard,
|
||||
pixelFormat,
|
||||
pixelDecimation)
|
||||
|
@ -7,6 +7,7 @@ Grabber::Grabber(QString grabberName, int width, int height, int cropLeft, int c
|
||||
, _videoMode(VIDEO_2D)
|
||||
, _width(width)
|
||||
, _height(height)
|
||||
, _fps(15)
|
||||
, _cropLeft(0)
|
||||
, _cropRight(0)
|
||||
, _cropTop(0)
|
||||
@ -86,3 +87,11 @@ bool Grabber::setWidthHeight(int width, int height)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Grabber::setFramerate(int fps)
|
||||
{
|
||||
if(fps > 0)
|
||||
_fps = fps;
|
||||
|
||||
return fps > 0;
|
||||
}
|
@ -157,6 +157,12 @@ void GrabberWrapper::handleSettingsUpdate(const settings::type& type, const QJso
|
||||
obj["cropTop"].toInt(0),
|
||||
obj["cropBottom"].toInt(0));
|
||||
|
||||
// device resolution
|
||||
_ggrabber->setWidthHeight(obj["width"].toInt(0), obj["height"].toInt(0));
|
||||
|
||||
// device framerate
|
||||
_ggrabber->setFramerate(obj["fps"].toInt(15));
|
||||
|
||||
_ggrabber->setSignalDetectionEnable(obj["signalDetection"].toBool(true));
|
||||
_ggrabber->setSignalDetectionOffset(
|
||||
obj["sDHOffsetMin"].toDouble(0.25),
|
||||
@ -170,7 +176,6 @@ void GrabberWrapper::handleSettingsUpdate(const settings::type& type, const QJso
|
||||
_ggrabber->setDeviceVideoStandard(
|
||||
obj["device"].toString("auto"),
|
||||
parseVideoStandard(obj["standard"].toString("no-change")));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,8 @@
|
||||
"items" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"maximum" : 255
|
||||
"maximum" : 255,
|
||||
"default" : 0
|
||||
},
|
||||
"minItems" : 3,
|
||||
"maxItems" : 3,
|
||||
|
@ -30,7 +30,8 @@
|
||||
"items" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"maximum" : 255
|
||||
"maximum" : 255,
|
||||
"default" : 0
|
||||
},
|
||||
"minItems" : 3,
|
||||
"maxItems" : 3,
|
||||
|
@ -9,7 +9,6 @@
|
||||
"type" : "string",
|
||||
"title" : "edt_conf_v4l2_device_title",
|
||||
"default" : "auto",
|
||||
"minLength" : 4,
|
||||
"required" : true,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
@ -17,14 +16,44 @@
|
||||
{
|
||||
"type" : "string",
|
||||
"title" : "edt_conf_v4l2_standard_title",
|
||||
"enum" : ["PAL","NTSC","SECAM","NO_CHANGE"],
|
||||
"enum" : ["NO_CHANGE", "PAL","NTSC","SECAM"],
|
||||
"default" : "NO_CHANGE",
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM", "edt_conf_enum_NO_CHANGE"]
|
||||
"enum_titles" : ["edt_conf_enum_NO_CHANGE", "edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM"]
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"width" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_fg_width_title",
|
||||
"default" : 0,
|
||||
"minimum" : 0,
|
||||
"required" : true,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"height" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_fg_height_title",
|
||||
"default" : 0,
|
||||
"minimum" : 0,
|
||||
"required" : true,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"fps" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "Framerate",
|
||||
"default" : 15,
|
||||
"minimum" : 1,
|
||||
"required" : true,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"sizeDecimation" :
|
||||
{
|
||||
"type" : "integer",
|
||||
@ -33,7 +62,7 @@
|
||||
"maximum" : 30,
|
||||
"default" : 6,
|
||||
"required" : true,
|
||||
"propertyOrder" : 3
|
||||
"propertyOrder" : 6
|
||||
},
|
||||
"cropLeft" :
|
||||
{
|
||||
@ -43,7 +72,7 @@
|
||||
"default" : 0,
|
||||
"append" : "edt_append_pixel",
|
||||
"required" : true,
|
||||
"propertyOrder" : 4
|
||||
"propertyOrder" : 7
|
||||
},
|
||||
"cropRight" :
|
||||
{
|
||||
@ -53,7 +82,7 @@
|
||||
"default" : 0,
|
||||
"append" : "edt_append_pixel",
|
||||
"required" : true,
|
||||
"propertyOrder" : 5
|
||||
"propertyOrder" : 8
|
||||
},
|
||||
"cropTop" :
|
||||
{
|
||||
@ -63,7 +92,7 @@
|
||||
"default" : 0,
|
||||
"append" : "edt_append_pixel",
|
||||
"required" : true,
|
||||
"propertyOrder" : 6
|
||||
"propertyOrder" : 9
|
||||
},
|
||||
"cropBottom" :
|
||||
{
|
||||
@ -73,7 +102,7 @@
|
||||
"default" : 0,
|
||||
"append" : "edt_append_pixel",
|
||||
"required" : true,
|
||||
"propertyOrder" : 7
|
||||
"propertyOrder" : 10
|
||||
},
|
||||
"signalDetection" :
|
||||
{
|
||||
@ -81,7 +110,7 @@
|
||||
"title" : "edt_conf_v4l2_signalDetection_title",
|
||||
"default" : false,
|
||||
"required" : true,
|
||||
"propertyOrder" : 8
|
||||
"propertyOrder" : 11
|
||||
},
|
||||
"redSignalThreshold" :
|
||||
{
|
||||
@ -97,7 +126,7 @@
|
||||
}
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 9
|
||||
"propertyOrder" : 12
|
||||
},
|
||||
"greenSignalThreshold" :
|
||||
{
|
||||
@ -113,7 +142,7 @@
|
||||
}
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 10
|
||||
"propertyOrder" : 13
|
||||
},
|
||||
"blueSignalThreshold" :
|
||||
{
|
||||
@ -129,7 +158,7 @@
|
||||
}
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 11
|
||||
"propertyOrder" : 14
|
||||
},
|
||||
"sDVOffsetMin" :
|
||||
{
|
||||
@ -145,7 +174,7 @@
|
||||
}
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 12
|
||||
"propertyOrder" : 15
|
||||
},
|
||||
"sDVOffsetMax" :
|
||||
{
|
||||
@ -161,7 +190,7 @@
|
||||
}
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 13
|
||||
"propertyOrder" : 16
|
||||
},
|
||||
"sDHOffsetMin" :
|
||||
{
|
||||
@ -177,7 +206,7 @@
|
||||
}
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 14
|
||||
"propertyOrder" : 17
|
||||
},
|
||||
"sDHOffsetMax" :
|
||||
{
|
||||
@ -193,8 +222,8 @@
|
||||
}
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 15
|
||||
"propertyOrder" : 18
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
"additionalProperties" : true
|
||||
}
|
||||
|
@ -45,7 +45,8 @@
|
||||
"required" : true,
|
||||
"items" : {
|
||||
"type": "string",
|
||||
"title" : "edt_conf_net_ip_itemtitle"
|
||||
"title" : "edt_conf_net_ip_itemtitle",
|
||||
"allowEmptyArray" : true
|
||||
},
|
||||
"options": {
|
||||
"dependencies": {
|
||||
|
@ -121,7 +121,7 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i
|
||||
rgb.red = data[index+2];
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_JPEG
|
||||
#ifdef HAVE_JPEG_DECODER
|
||||
case PIXELFORMAT_MJPEG:
|
||||
break;
|
||||
#endif
|
||||
|
@ -73,7 +73,7 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
|
||||
QJsonObject::const_iterator defaultValue = schema.find("default");
|
||||
|
||||
if (attribute == "type")
|
||||
checkType(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkType(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "properties")
|
||||
{
|
||||
if (value.isObject())
|
||||
@ -106,13 +106,13 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
|
||||
}
|
||||
}
|
||||
else if (attribute == "minimum")
|
||||
checkMinimum(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkMinimum(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "maximum")
|
||||
checkMaximum(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkMaximum(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "minLength")
|
||||
checkMinLength(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkMinLength(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "maxLength")
|
||||
checkMaxLength(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkMaxLength(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "items")
|
||||
{
|
||||
if (value.isArray())
|
||||
@ -125,19 +125,20 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
|
||||
}
|
||||
}
|
||||
else if (attribute == "minItems")
|
||||
checkMinItems(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkMinItems(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "maxItems")
|
||||
checkMaxItems(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkMaxItems(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "uniqueItems")
|
||||
checkUniqueItems(value, attributeValue);
|
||||
else if (attribute == "enum")
|
||||
checkEnum(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
|
||||
checkEnum(value, attributeValue, (defaultValue != schema.end() ? *defaultValue : QJsonValue::Null));
|
||||
else if (attribute == "required")
|
||||
; // nothing to do. value is present so always oke
|
||||
else if (attribute == "id")
|
||||
; // references have already been collected
|
||||
else if (attribute == "title" || attribute == "description" || attribute == "default" || attribute == "format"
|
||||
|| attribute == "defaultProperties" || attribute == "propertyOrder" || attribute == "append" || attribute == "step" || attribute == "access" || attribute == "options" || attribute == "script")
|
||||
|| attribute == "defaultProperties" || attribute == "propertyOrder" || attribute == "append" || attribute == "step"
|
||||
|| attribute == "access" || attribute == "options" || attribute == "script" || attribute == "allowEmptyArray")
|
||||
; // nothing to do.
|
||||
else
|
||||
{
|
||||
@ -225,7 +226,7 @@ void QJsonSchemaChecker::checkProperties(const QJsonObject & value, const QJsonO
|
||||
if (_correct == "create")
|
||||
{
|
||||
QJsonUtils::modify(_autoCorrected, _currentPath, QJsonUtils::create(propertyValue, _ignoreRequired), property);
|
||||
setMessage("Create property: "+property+" with value: "+propertyValue.toObject().find("default").value().toString());
|
||||
setMessage("Create property: "+property+" with value: "+QJsonUtils::getDefaultValue(propertyValue));
|
||||
}
|
||||
|
||||
if (_correct == "")
|
||||
@ -391,7 +392,7 @@ void QJsonSchemaChecker::checkItems(const QJsonValue & value, const QJsonObject
|
||||
QJsonArray jArray = value.toArray();
|
||||
|
||||
if (_correct == "remove")
|
||||
if (jArray.isEmpty())
|
||||
if (jArray.isEmpty() && !schema.contains("allowEmptyArray"))
|
||||
{
|
||||
QJsonUtils::modify(_autoCorrected, _currentPath);
|
||||
setMessage("Remove empty array");
|
||||
|
@ -53,6 +53,9 @@ int main(int argc, char** argv)
|
||||
Option & argDevice = parser.add<Option> ('d', "device", "The device to use, can be /dev/video0 [default: %1 (auto detected)]", "auto");
|
||||
SwitchOption<VideoStandard> & argVideoStandard= parser.add<SwitchOption<VideoStandard>>('v', "video-standard", "The used video standard. Valid values are PAL, NTSC, SECAM or no-change. [default: %1]", "no-change");
|
||||
SwitchOption<PixelFormat> & argPixelFormat = parser.add<SwitchOption<PixelFormat>> (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, RGB32, MJPEG or no-change. [default: %1]", "no-change");
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "15", 1, 25);
|
||||
IntOption & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "160", 160);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "160", 160);
|
||||
IntOption & argCropWidth = parser.add<IntOption> (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default: %1]", "0");
|
||||
IntOption & argCropHeight = parser.add<IntOption> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default: %1]", "0");
|
||||
IntOption & argCropLeft = parser.add<IntOption> (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)");
|
||||
@ -105,6 +108,9 @@ int main(int argc, char** argv)
|
||||
// initialize the grabber
|
||||
V4L2Grabber grabber(
|
||||
argDevice.value(parser),
|
||||
argWidth.getInt(parser),
|
||||
argHeight.getInt(parser),
|
||||
1000 / argFps.getInt(parser),
|
||||
argVideoStandard.switchValue(parser),
|
||||
argPixelFormat.switchValue(parser),
|
||||
std::max(1, argSizeDecimation.getInt(parser)));
|
||||
|
@ -443,9 +443,12 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& settingsType, co
|
||||
|
||||
_v4l2Grabber = new V4L2Wrapper(
|
||||
grabberConfig["device"].toString("auto"),
|
||||
grabberConfig["width"].toInt(0),
|
||||
grabberConfig["height"].toInt(0),
|
||||
grabberConfig["fps"].toInt(15),
|
||||
parseVideoStandard(grabberConfig["standard"].toString("no-change")),
|
||||
parsePixelFormat(grabberConfig["pixelFormat"].toString("no-change")),
|
||||
grabberConfig["sizeDecimation"].toInt(8) );
|
||||
grabberConfig["sizeDecimation"].toInt(8));
|
||||
_v4l2Grabber->setSignalThreshold(
|
||||
grabberConfig["redSignalThreshold"].toDouble(0.0)/100.0,
|
||||
grabberConfig["greenSignalThreshold"].toDouble(0.0)/100.0,
|
||||
|
Loading…
Reference in New Issue
Block a user