V4l2 input (#825)

This commit is contained in:
Paulchen Panther 2020-06-17 20:55:57 +02:00 committed by GitHub
parent 423a59fa34
commit 756247aa1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 252 additions and 77 deletions

View File

@ -583,6 +583,8 @@
"edt_conf_v4l2_heading_title": "USB Aufnahme", "edt_conf_v4l2_heading_title": "USB Aufnahme",
"edt_conf_v4l2_device_title": "Gerät", "edt_conf_v4l2_device_title": "Gerät",
"edt_conf_v4l2_device_expl": "Eine Liste von USB (v4l) Aufnahmegeräten. Wähle 'Automatisch' für automatische Erkennung. Benutzerdefiniert z.b.: '/dev/video0'", "edt_conf_v4l2_device_expl": "Eine Liste von USB (v4l) Aufnahmegeräten. Wähle 'Automatisch' für automatische Erkennung. Benutzerdefiniert z.b.: '/dev/video0'",
"edt_conf_v4l2_input_title": "Eingang",
"edt_conf_v4l2_input_expl": "Wähle den Videoeingang deines Gerätes. Auf 'Automatisch' wird der Videoeingang vom v4l interface beibehalten.",
"edt_conf_v4l2_standard_title": "Videoformat", "edt_conf_v4l2_standard_title": "Videoformat",
"edt_conf_v4l2_standard_expl": "Wähle das passende Videoformat deiner Region. Auf 'Automatisch' wird der gewählte Modus vom v4l interface beibehalten.", "edt_conf_v4l2_standard_expl": "Wähle das passende Videoformat deiner Region. Auf 'Automatisch' wird der gewählte Modus vom v4l interface beibehalten.",
"edt_conf_v4l2_resolution_title": "Auflösung", "edt_conf_v4l2_resolution_title": "Auflösung",

View File

@ -41,7 +41,7 @@
"general_btn_rename" : "Rename", "general_btn_rename" : "Rename",
"general_btn_continue" : "Continue", "general_btn_continue" : "Continue",
"general_btn_save" : "Save", "general_btn_save" : "Save",
"general_btn_saverestart" : "Save and restart", "general_btn_saverestart" : "Save and restart",
"general_btn_saveandreload" : "Save and reload", "general_btn_saveandreload" : "Save and reload",
"general_btn_restarthyperion" : "Restart Hyperion", "general_btn_restarthyperion" : "Restart Hyperion",
"general_btn_off" : "Off", "general_btn_off" : "Off",
@ -584,8 +584,10 @@
"edt_conf_v4l2_heading_title" : "USB Capture", "edt_conf_v4l2_heading_title" : "USB Capture",
"edt_conf_v4l2_device_title" : "Device", "edt_conf_v4l2_device_title" : "Device",
"edt_conf_v4l2_device_expl" : "The path to the USB capture interface. Set to 'Automatic' for automatic detection. Example: '/dev/video0'", "edt_conf_v4l2_device_expl" : "The path to the USB capture interface. Set to 'Automatic' for automatic detection. Example: '/dev/video0'",
"edt_conf_v4l2_input_title": "Input",
"edt_conf_v4l2_input_expl": "Select the video input of your device. 'Automatic' keeps the value chosen by the v4l2 interface.",
"edt_conf_v4l2_standard_title" : "Video standard", "edt_conf_v4l2_standard_title" : "Video standard",
"edt_conf_v4l2_standard_expl" : "Select the video standard for your region. 'Automatic' keeps the value chosen by the v4l2 interface", "edt_conf_v4l2_standard_expl" : "Select the video standard for your region. 'Automatic' keeps the value chosen by the v4l2 interface.",
"edt_conf_v4l2_resolution_title" : "Device Resolution", "edt_conf_v4l2_resolution_title" : "Device Resolution",
"edt_conf_v4l2_resolution_expl" : "A list of supported resolutions of the active device", "edt_conf_v4l2_resolution_expl" : "A list of supported resolutions of the active device",
"edt_conf_v4l2_framerate_title": "Frames per second", "edt_conf_v4l2_framerate_title": "Frames per second",

View File

@ -15,18 +15,25 @@ $(document).ready( function() {
"propertyOrder" : 1, "propertyOrder" : 1,
"required" : true "required" : true
}, },
"device_inputs":
{
"type": "string",
"title": "edt_conf_v4l2_input_title",
"propertyOrder" : 3,
"required" : true
},
"resolutions": "resolutions":
{ {
"type": "string", "type": "string",
"title": "edt_conf_v4l2_resolution_title", "title": "edt_conf_v4l2_resolution_title",
"propertyOrder" : 4, "propertyOrder" : 6,
"required" : true "required" : true
}, },
"framerates": "framerates":
{ {
"type": "string", "type": "string",
"title": "edt_conf_v4l2_framerate_title", "title": "edt_conf_v4l2_framerate_title",
"propertyOrder" : 7, "propertyOrder" : 9,
"required" : true "required" : true
} }
}; };
@ -53,6 +60,16 @@ $(document).ready( function() {
break; break;
} }
} }
} else if (key == 'device_inputs') {
for (var i = 0; i < v4l2_properties.length; i++) {
if (v4l2_properties[i]['device'] == device) {
for (var index = 0; index < v4l2_properties[i]['inputs'].length; index++) {
enumVals.push(v4l2_properties[i]['inputs'][index]['inputIndex'].toString());
enumTitelVals.push(v4l2_properties[i]['inputs'][index]['inputName']);
}
break;
}
}
} }
window.schema.grabberV4L2.properties[key] = { window.schema.grabberV4L2.properties[key] = {
@ -71,9 +88,9 @@ $(document).ready( function() {
// Switch between visible states // Switch between visible states
function toggleOption(option, state) { function toggleOption(option, state) {
$('[data-schemapath*="root.grabberV4L2.'+option+'"]').toggle(state); $('[data-schemapath="root.grabberV4L2.'+option+'"]').toggle(state);
if (state) ( if (state) (
$('[data-schemapath*="root.grabberV4L2.'+option+'"]').addClass('col-md-12'), $('[data-schemapath="root.grabberV4L2.'+option+'"]').addClass('col-md-12'),
$('label[for="root_grabberV4L2_'+option+'"]').css('left','10px'), $('label[for="root_grabberV4L2_'+option+'"]').css('left','10px'),
$('[id="root_grabberV4L2_'+option+'"]').css('left','10px') $('[id="root_grabberV4L2_'+option+'"]').css('left','10px')
); );
@ -88,13 +105,14 @@ $(document).ready( function() {
var val = ed.getValue(); var val = ed.getValue();
if (key == 'available_devices') { if (key == 'available_devices') {
var V4L2properties = ['resolutions', 'framerates']; var V4L2properties = ['device_inputs', 'resolutions', 'framerates'];
if (val == 'custom') { if (val == 'custom') {
var grabberV4L2 = ed.parent; var grabberV4L2 = ed.parent;
V4L2properties.forEach(function(item) { V4L2properties.forEach(function(item) {
buildSchemaPart(item, v4l2_dynamic_enum_schema, 'none'); buildSchemaPart(item, v4l2_dynamic_enum_schema, 'none');
grabberV4L2.original_schema.properties[item] = window.schema.grabberV4L2.properties[item]; grabberV4L2.original_schema.properties[item] = window.schema.grabberV4L2.properties[item];
grabberV4L2.schema.properties[item] = window.schema.grabberV4L2.properties[item]; grabberV4L2.schema.properties[item] = window.schema.grabberV4L2.properties[item];
conf_editor_v4l2.validator.schema.properties.grabberV4L2.properties[item] = window.schema.grabberV4L2.properties[item];
grabberV4L2.removeObjectProperty(item); grabberV4L2.removeObjectProperty(item);
delete grabberV4L2.cached_editors[item]; delete grabberV4L2.cached_editors[item];
@ -103,15 +121,22 @@ $(document).ready( function() {
conf_editor_v4l2.getEditor(path + item).enable(); conf_editor_v4l2.getEditor(path + item).enable();
}); });
conf_editor_v4l2.getEditor(path + 'standard').enable();
toggleOption('device', true); toggleOption('device', true);
} else if (val == 'auto') { } else if (val == 'auto') {
V4L2properties.forEach(function(item) { V4L2properties.forEach(function(item) {
conf_editor_v4l2.getEditor(path + item).setValue('auto'); conf_editor_v4l2.getEditor(path + item).setValue('auto');
conf_editor_v4l2.getEditor(path + item).disable(); conf_editor_v4l2.getEditor(path + item).disable();
}); });
(toggleOption('device', false), toggleOption('width', false), conf_editor_v4l2.getEditor(path + 'standard').setValue('auto');
toggleOption('height', false), toggleOption('fps', false)); conf_editor_v4l2.getEditor(path + 'standard').disable();
(toggleOption('device', false), toggleOption('input', false),
toggleOption('width', false), toggleOption('height', false),
toggleOption('fps', false));
} else { } else {
var grabberV4L2 = ed.parent; var grabberV4L2 = ed.parent;
V4L2properties.forEach(function(item) { V4L2properties.forEach(function(item) {
@ -127,6 +152,7 @@ $(document).ready( function() {
conf_editor_v4l2.getEditor(path + item).enable(); conf_editor_v4l2.getEditor(path + item).enable();
}); });
conf_editor_v4l2.getEditor(path + 'standard').enable();
toggleOption('device', false); toggleOption('device', false);
} }
} }
@ -140,6 +166,11 @@ $(document).ready( function() {
val != 'custom' val != 'custom'
? toggleOption('fps', false) ? toggleOption('fps', false)
: toggleOption('fps', true); : toggleOption('fps', true);
if (key == 'device_inputs')
val != 'custom'
? toggleOption('input', false)
: toggleOption('input', true);
}); });
}); });
}; };
@ -221,12 +252,15 @@ $(document).ready( function() {
conf_editor_v4l2.getEditor('root.grabberV4L2.available_devices').setValue('auto'); conf_editor_v4l2.getEditor('root.grabberV4L2.available_devices').setValue('auto');
if (window.serverConfig.grabberV4L2.available_devices == 'auto') { if (window.serverConfig.grabberV4L2.available_devices == 'auto') {
['resolutions', 'framerates'].forEach(function(item) { ['device_inputs', 'standard', 'resolutions', 'framerates'].forEach(function(item) {
conf_editor_v4l2.getEditor('root.grabberV4L2.' + item).setValue('auto'); conf_editor_v4l2.getEditor('root.grabberV4L2.' + item).setValue('auto');
conf_editor_v4l2.getEditor('root.grabberV4L2.' + item).disable(); conf_editor_v4l2.getEditor('root.grabberV4L2.' + item).disable();
}); });
} }
if (window.serverConfig.grabberV4L2.device_inputs == 'custom' && window.serverConfig.grabberV4L2.device != 'auto')
toggleOption('input', true);
if (window.serverConfig.grabberV4L2.resolutions == 'custom' && window.serverConfig.grabberV4L2.device != 'auto') if (window.serverConfig.grabberV4L2.resolutions == 'custom' && window.serverConfig.grabberV4L2.device != 'auto')
(toggleOption('width', true), toggleOption('height', true)); (toggleOption('width', true), toggleOption('height', true));
@ -244,6 +278,12 @@ $(document).ready( function() {
if (v4l2Options.grabberV4L2.available_devices == 'auto') if (v4l2Options.grabberV4L2.available_devices == 'auto')
v4l2Options.grabberV4L2.device = 'auto'; v4l2Options.grabberV4L2.device = 'auto';
if (v4l2Options.grabberV4L2.device_inputs != 'custom' && v4l2Options.grabberV4L2.device_inputs != 'auto' && v4l2Options.grabberV4L2.available_devices != 'auto')
v4l2Options.grabberV4L2.input = parseInt(v4l2Options.grabberV4L2.device_inputs);
if (v4l2Options.grabberV4L2.device_inputs == 'auto')
v4l2Options.grabberV4L2.input = -1;
if (v4l2Options.grabberV4L2.resolutions != 'custom' && v4l2Options.grabberV4L2.resolutions != 'auto' && v4l2Options.grabberV4L2.available_devices != 'auto') if (v4l2Options.grabberV4L2.resolutions != 'custom' && v4l2Options.grabberV4L2.resolutions != 'auto' && v4l2Options.grabberV4L2.available_devices != 'auto')
(v4l2Options.grabberV4L2.width = parseInt(v4l2Options.grabberV4L2.resolutions.split('x')[0]), (v4l2Options.grabberV4L2.width = parseInt(v4l2Options.grabberV4L2.resolutions.split('x')[0]),
v4l2Options.grabberV4L2.height = parseInt(v4l2Options.grabberV4L2.resolutions.split('x')[1])); v4l2Options.grabberV4L2.height = parseInt(v4l2Options.grabberV4L2.resolutions.split('x')[1]));

View File

@ -58,24 +58,25 @@
"grabberV4L2" : "grabberV4L2" :
{ {
"device" : "auto", "device" : "auto",
"width" : 0, "input" : 0,
"height" : 0, "width" : 0,
"fps" : 15, "height" : 0,
"standard" : "NO_CHANGE", "fps" : 15,
"sizeDecimation" : 8, "standard" : "NO_CHANGE",
"cropLeft" : 0, "sizeDecimation" : 8,
"cropRight" : 0, "cropLeft" : 0,
"cropTop" : 0, "cropRight" : 0,
"cropBottom" : 0, "cropTop" : 0,
"cropBottom" : 0,
"redSignalThreshold" : 5, "redSignalThreshold" : 5,
"greenSignalThreshold" : 5, "greenSignalThreshold" : 5,
"blueSignalThreshold" : 5, "blueSignalThreshold" : 5,
"signalDetection" : false, "signalDetection" : false,
"sDVOffsetMin" : 0.25, "sDVOffsetMin" : 0.25,
"sDHOffsetMin" : 0.25, "sDHOffsetMin" : 0.25,
"sDVOffsetMax" : 0.75, "sDVOffsetMax" : 0.75,
"sDHOffsetMax" : 0.75 "sDHOffsetMax" : 0.75
}, },
"framegrabber" : "framegrabber" :
@ -203,14 +204,14 @@
"vdepth" : 5, "vdepth" : 5,
"overlap" : 0, "overlap" : 0,
"edgegap" : 0, "edgegap" : 0,
"ptlh" : 0, "ptlh" : 0,
"ptlv" : 0, "ptlv" : 0,
"ptrh" : 100, "ptrh" : 100,
"ptrv" : 0, "ptrv" : 0,
"pblh" : 0, "pblh" : 0,
"pblv" : 100, "pblv" : 100,
"pbrh" : 100, "pbrh" : 100,
"pbrv" : 100 "pbrv" : 100
}, },
"matrix": "matrix":

View File

@ -9,6 +9,7 @@
#include <QSocketNotifier> #include <QSocketNotifier>
#include <QRectF> #include <QRectF>
#include <QMap> #include <QMap>
#include <QMultiMap>
// util includes // util includes
#include <utils/PixelFormat.h> #include <utils/PixelFormat.h>
@ -43,15 +44,17 @@ class V4L2Grabber : public Grabber
public: public:
struct DeviceProperties struct DeviceProperties
{ {
QString name = QString(); QString name = QString();
QStringList resolutions = QStringList(); QMultiMap<QString, int> inputs = QMultiMap<QString, int>();
QStringList framerates = QStringList(); QStringList resolutions = QStringList();
QStringList framerates = QStringList();
}; };
V4L2Grabber(const QString & device, V4L2Grabber(const QString & device,
const unsigned width, const unsigned width,
const unsigned height, const unsigned height,
const unsigned fps, const unsigned fps,
const unsigned input,
VideoStandard videoStandard, VideoStandard videoStandard,
PixelFormat pixelFormat, PixelFormat pixelFormat,
int pixelDecimation int pixelDecimation
@ -103,13 +106,18 @@ public:
/// ///
/// @brief overwrite Grabber.h implementation /// @brief overwrite Grabber.h implementation
/// ///
bool setFramerate(int fps) override; bool setInput(int input) override;
/// ///
/// @brief overwrite Grabber.h implementation /// @brief overwrite Grabber.h implementation
/// ///
bool setWidthHeight(int width, int height) override; bool setWidthHeight(int width, int height) override;
///
/// @brief overwrite Grabber.h implementation
///
bool setFramerate(int fps) override;
/// ///
/// @brief overwrite Grabber.h implementation /// @brief overwrite Grabber.h implementation
/// ///
@ -120,6 +128,11 @@ public:
/// ///
QString getV4L2deviceName(QString devicePath) override; QString getV4L2deviceName(QString devicePath) override;
///
/// @brief overwrite Grabber.h implementation
///
QMultiMap<QString, int> getV4L2deviceInputs(QString devicePath) override;
/// ///
/// @brief overwrite Grabber.h implementation /// @brief overwrite Grabber.h implementation
/// ///
@ -159,7 +172,7 @@ private:
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);
void uninit_device(); void uninit_device();
@ -230,7 +243,6 @@ private:
QString _deviceName; QString _deviceName;
std::map<QString, QString> _v4lDevices; std::map<QString, QString> _v4lDevices;
QMap<QString, V4L2Grabber::DeviceProperties> _deviceProperties; QMap<QString, V4L2Grabber::DeviceProperties> _deviceProperties;
int _input;
VideoStandard _videoStandard; VideoStandard _videoStandard;
io_method _ioMethod; io_method _ioMethod;
int _fileDescriptor; int _fileDescriptor;

View File

@ -12,6 +12,7 @@ public:
const unsigned grabWidth, const unsigned grabWidth,
const unsigned grabHeight, const unsigned grabHeight,
const unsigned fps, const unsigned fps,
const unsigned input,
VideoStandard videoStandard, VideoStandard videoStandard,
PixelFormat pixelFormat, PixelFormat pixelFormat,
int pixelDecimation ); int pixelDecimation );

View File

@ -11,6 +11,8 @@
#include <utils/Logger.h> #include <utils/Logger.h>
#include <utils/Components.h> #include <utils/Components.h>
#include <QMultiMap>
/// ///
/// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler) /// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler)
/// Overwrite the videoMode with setVideoMode() /// Overwrite the videoMode with setVideoMode()
@ -34,6 +36,12 @@ public:
/// ///
virtual void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom); virtual void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom);
///
/// @brief Apply new video input (used from v4l)
/// @param input device input
///
virtual bool setInput(int input);
/// ///
/// @brief Apply new width/height values, on errors (collide with cropping) reject the values /// @brief Apply new width/height values, on errors (collide with cropping) reject the values
/// @return True on success else false /// @return True on success else false
@ -116,6 +124,13 @@ public:
/// ///
virtual QString getV4L2deviceName(QString devicePath) { return QString(); } virtual QString getV4L2deviceName(QString devicePath) { return QString(); }
///
/// @brief Get a name/index pair of supported device inputs
/// @param devicePath The device path
/// @return multi pair of name/index on success else empty pair
///
virtual QMultiMap<QString, int> getV4L2deviceInputs(QString devicePath) { return QMultiMap<QString, int>(); }
/// ///
/// @brief Get a list of supported device resolutions /// @brief Get a list of supported device resolutions
/// @param devicePath The device path /// @param devicePath The device path
@ -144,9 +159,13 @@ protected:
/// Height of the captured snapshot [pixels] /// Height of the captured snapshot [pixels]
int _height; int _height;
/// frame per second
int _fps; int _fps;
// number of pixels to crop after capturing /// device input
int _input;
/// number of pixels to crop after capturing
int _cropLeft, _cropRight, _cropTop, _cropBottom; int _cropLeft, _cropRight, _cropTop, _cropBottom;
bool _enabled; bool _enabled;

View File

@ -5,6 +5,7 @@
#include <QJsonArray> #include <QJsonArray>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QMultiMap>
#include <utils/Logger.h> #include <utils/Logger.h>
#include <utils/Components.h> #include <utils/Components.h>
@ -63,6 +64,13 @@ public:
/// ///
virtual QString getV4L2deviceName(QString devicePath); virtual QString getV4L2deviceName(QString devicePath);
///
/// @brief Get a name/index pair of supported device inputs
/// @param devicePath The device path
/// @return multi pair of name/index on success else empty pair
///
virtual QMultiMap<QString, int> getV4L2deviceInputs(QString devicePath);
/// ///
/// @brief Get a list of supported device resolutions /// @brief Get a list of supported device resolutions
/// @param devicePath The device path /// @param devicePath The device path

View File

@ -14,6 +14,7 @@
#include <QByteArray> #include <QByteArray>
#include <QTimer> #include <QTimer>
#include <QHostInfo> #include <QHostInfo>
#include <QMultiMap>
// hyperion includes // hyperion includes
#include <leddevice/LedDeviceWrapper.h> #include <leddevice/LedDeviceWrapper.h>
@ -489,21 +490,32 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
QJsonArray availableV4L2devices; QJsonArray availableV4L2devices;
for (auto devicePath : GrabberWrapper::getInstance()->getV4L2devices()) for (auto devicePath : GrabberWrapper::getInstance()->getV4L2devices())
{ {
QJsonObject device; QJsonObject device;
device["device"] = devicePath; device["device"] = devicePath;
device["name"] = GrabberWrapper::getInstance()->getV4L2deviceName(devicePath); device["name"] = GrabberWrapper::getInstance()->getV4L2deviceName(devicePath);
QJsonArray availableInputs;
QMultiMap<QString, int> inputs = GrabberWrapper::getInstance()->getV4L2deviceInputs(devicePath);
for (auto input = inputs.begin(); input != inputs.end(); input++)
{
QJsonObject availableInput;
availableInput["inputName"] = input.key();
availableInput["inputIndex"] = input.value();
availableInputs.append(availableInput);
}
device.insert("inputs", availableInputs);
QJsonArray availableResolutions; QJsonArray availableResolutions;
for (auto resolution : GrabberWrapper::getInstance()->getResolutions(devicePath)) QStringList resolutions = GrabberWrapper::getInstance()->getResolutions(devicePath);
for (auto resolution : resolutions)
{ {
availableResolutions.append(resolution); availableResolutions.append(resolution);
} }
device.insert("resolutions", availableResolutions); device.insert("resolutions", availableResolutions);
QJsonArray availableFramerates; QJsonArray availableFramerates;
for (auto framerate : GrabberWrapper::getInstance()->getFramerates(devicePath)) QStringList framerates = GrabberWrapper::getInstance()->getFramerates(devicePath);
for (auto framerate : framerates)
{ {
availableFramerates.append(framerate); availableFramerates.append(framerate);
} }

View File

@ -8,6 +8,10 @@
#include <QUuid> #include <QUuid>
#include <QDir> #include <QDir>
#ifdef _WIN32
#include <stdexcept>
#endif
// not in header because of linking // not in header because of linking
static QString _rootPath; static QString _rootPath;
static QThreadStorage<QSqlDatabase> _databasePool; static QThreadStorage<QSqlDatabase> _databasePool;

View File

@ -34,13 +34,13 @@ V4L2Grabber::V4L2Grabber(const QString & device
, const unsigned width , const unsigned width
, const unsigned height , const unsigned height
, const unsigned fps , const unsigned fps
, const unsigned input
, VideoStandard videoStandard , VideoStandard videoStandard
, PixelFormat pixelFormat , PixelFormat pixelFormat
, int pixelDecimation , int pixelDecimation
) )
: Grabber("V4L2:"+device) : Grabber("V4L2:"+device)
, _deviceName() , _deviceName()
, _input(-1)
, _videoStandard(videoStandard) , _videoStandard(videoStandard)
, _ioMethod(IO_METHOD_MMAP) , _ioMethod(IO_METHOD_MMAP)
, _fileDescriptor(-1) , _fileDescriptor(-1)
@ -66,6 +66,7 @@ V4L2Grabber::V4L2Grabber(const QString & device
getV4Ldevices(); getV4Ldevices();
// init // init
setInput(input);
setWidthHeight(width, height); setWidthHeight(width, height);
setFramerate(fps); setFramerate(fps);
setDeviceVideoStandard(device, videoStandard); setDeviceVideoStandard(device, videoStandard);
@ -147,7 +148,7 @@ bool V4L2Grabber::init()
if (open_device()) if (open_device())
{ {
opened = true; opened = true;
init_device(_videoStandard, _input); init_device(_videoStandard);
_initialized = true; _initialized = true;
} }
} }
@ -214,6 +215,21 @@ void V4L2Grabber::getV4Ldevices()
V4L2Grabber::DeviceProperties properties; V4L2Grabber::DeviceProperties properties;
// collect available device inputs (index & name)
int inputIndex;
if (xioctl(fd, VIDIOC_G_INPUT, &inputIndex) == 0)
{
struct v4l2_input input;
CLEAR(input);
input.index = 0;
while (xioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0)
{
properties.inputs.insert(QString((char*)input.name), input.index);
input.index++;
}
}
// collect available device resolutions & frame rates // collect available device resolutions & frame rates
struct v4l2_frmsizeenum frmsizeenum; struct v4l2_frmsizeenum frmsizeenum;
CLEAR(frmsizeenum); CLEAR(frmsizeenum);
@ -485,7 +501,7 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
} }
} }
void V4L2Grabber::init_device(VideoStandard videoStandard, int input) void V4L2Grabber::init_device(VideoStandard videoStandard)
{ {
struct v4l2_capability cap; struct v4l2_capability cap;
CLEAR(cap); CLEAR(cap);
@ -566,16 +582,13 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
// set input if needed and supported // set input if needed and supported
struct v4l2_input v4l2Input; struct v4l2_input v4l2Input;
CLEAR(v4l2Input); CLEAR(v4l2Input);
v4l2Input.index = _input;
v4l2Input.index = input; if (_input >= 0 && 0 == xioctl(VIDIOC_ENUMINPUT, &v4l2Input))
if (input >= 0 && 0 == xioctl(VIDIOC_ENUMINPUT,&v4l2Input))
{ {
if (-1 == xioctl(VIDIOC_S_INPUT, &input)) (-1 == xioctl(VIDIOC_S_INPUT, &_input))
{ ? Debug(_log, "Input settings not supported.")
throw_errno_exception("VIDIOC_S_INPUT"); : Debug(_log, "Set device input to: %s", v4l2Input.name);
return;
}
} }
// set the video standard if needed and supported // set the video standard if needed and supported
@ -1316,9 +1329,9 @@ void V4L2Grabber::setDeviceVideoStandard(QString device, VideoStandard videoStan
} }
} }
bool V4L2Grabber::setFramerate(int fps) bool V4L2Grabber::setInput(int input)
{ {
if(Grabber::setFramerate(fps)) if(Grabber::setInput(input))
{ {
bool started = _initialized; bool started = _initialized;
uninit(); uninit();
@ -1340,6 +1353,18 @@ bool V4L2Grabber::setWidthHeight(int width, int height)
return false; return false;
} }
bool V4L2Grabber::setFramerate(int fps)
{
if(Grabber::setFramerate(fps))
{
bool started = _initialized;
uninit();
if(started) start();
return true;
}
return false;
}
QStringList V4L2Grabber::getV4L2devices() QStringList V4L2Grabber::getV4L2devices()
{ {
QStringList result = QStringList(); QStringList result = QStringList();
@ -1355,6 +1380,11 @@ QString V4L2Grabber::getV4L2deviceName(QString devicePath)
return _deviceProperties.value(devicePath).name; return _deviceProperties.value(devicePath).name;
} }
QMultiMap<QString, int> V4L2Grabber::getV4L2deviceInputs(QString devicePath)
{
return _deviceProperties.value(devicePath).inputs;
}
QStringList V4L2Grabber::getResolutions(QString devicePath) QStringList V4L2Grabber::getResolutions(QString devicePath)
{ {
return _deviceProperties.value(devicePath).resolutions; return _deviceProperties.value(devicePath).resolutions;

View File

@ -9,6 +9,7 @@ V4L2Wrapper::V4L2Wrapper(const QString &device,
const unsigned grabWidth, const unsigned grabWidth,
const unsigned grabHeight, const unsigned grabHeight,
const unsigned fps, const unsigned fps,
const unsigned input,
VideoStandard videoStandard, VideoStandard videoStandard,
PixelFormat pixelFormat, PixelFormat pixelFormat,
int pixelDecimation ) int pixelDecimation )
@ -17,6 +18,7 @@ V4L2Wrapper::V4L2Wrapper(const QString &device,
grabWidth, grabWidth,
grabHeight, grabHeight,
fps, fps,
input,
videoStandard, videoStandard,
pixelFormat, pixelFormat,
pixelDecimation) pixelDecimation)

View File

@ -8,6 +8,7 @@ Grabber::Grabber(QString grabberName, int width, int height, int cropLeft, int c
, _width(width) , _width(width)
, _height(height) , _height(height)
, _fps(15) , _fps(15)
, _input(-1)
, _cropLeft(0) , _cropLeft(0)
, _cropRight(0) , _cropRight(0)
, _cropTop(0) , _cropTop(0)
@ -70,6 +71,17 @@ void Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTo
} }
} }
bool Grabber::setInput(int input)
{
if((input >= 0) && (_input != input))
{
_input = input;
return true;
}
return false;
}
bool Grabber::setWidthHeight(int width, int height) bool Grabber::setWidthHeight(int width, int height)
{ {
// eval changes with crop // eval changes with crop
@ -91,7 +103,10 @@ bool Grabber::setWidthHeight(int width, int height)
bool Grabber::setFramerate(int fps) bool Grabber::setFramerate(int fps)
{ {
if((fps > 0) && (_fps != fps)) if((fps > 0) && (_fps != fps))
{
_fps = fps; _fps = fps;
return true;
}
return (fps > 0) && (_fps != fps); return false;
} }

View File

@ -160,6 +160,9 @@ void GrabberWrapper::handleSettingsUpdate(const settings::type& type, const QJso
obj["cropTop"].toInt(0), obj["cropTop"].toInt(0),
obj["cropBottom"].toInt(0)); obj["cropBottom"].toInt(0));
// device input
_ggrabber->setInput(obj["input"].toInt(-1));
// device resolution // device resolution
_ggrabber->setWidthHeight(obj["width"].toInt(0), obj["height"].toInt(0)); _ggrabber->setWidthHeight(obj["width"].toInt(0), obj["height"].toInt(0));
@ -236,6 +239,14 @@ QString GrabberWrapper::getV4L2deviceName(QString devicePath)
return QString(); return QString();
} }
QMultiMap<QString, int> GrabberWrapper::getV4L2deviceInputs(QString devicePath)
{
if(_grabberName.startsWith("V4L"))
return _ggrabber->getV4L2deviceInputs(devicePath);
return QMultiMap<QString, int>();
}
QStringList GrabberWrapper::getResolutions(QString devicePath) QStringList GrabberWrapper::getResolutions(QString devicePath)
{ {
if(_grabberName.startsWith("V4L")) if(_grabberName.startsWith("V4L"))

View File

@ -14,7 +14,19 @@
}, },
"required" : true, "required" : true,
"propertyOrder" : 2, "propertyOrder" : 2,
"comment" : "The 'available_device' settings are dynamically inserted into the WebUI under PropertyOrder '1'." "comment" : "The 'available_devices' settings are dynamically inserted into the WebUI under PropertyOrder '1'."
},
"input" :
{
"type" : "integer",
"title" : "edt_conf_enum_custom",
"default" : 0,
"options" : {
"hidden":true
},
"required" : true,
"propertyOrder" : 4,
"comment" : "The 'device_inputs' settings are dynamically inserted into the WebUI under PropertyOrder '3'."
}, },
"standard" : "standard" :
{ {
@ -26,7 +38,7 @@
"enum_titles" : ["edt_conf_enum_NO_CHANGE", "edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM"] "enum_titles" : ["edt_conf_enum_NO_CHANGE", "edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM"]
}, },
"required" : true, "required" : true,
"propertyOrder" : 3 "propertyOrder" : 5
}, },
"width" : "width" :
{ {
@ -39,8 +51,8 @@
"hidden":true "hidden":true
}, },
"required" : true, "required" : true,
"propertyOrder" : 5, "propertyOrder" : 7,
"comment" : "The 'resolution' settings are dynamically inserted into the WebUI under PropertyOrder '4'." "comment" : "The 'resolutions' settings are dynamically inserted into the WebUI under PropertyOrder '6'."
}, },
"height" : "height" :
{ {
@ -53,7 +65,7 @@
"hidden":true "hidden":true
}, },
"required" : true, "required" : true,
"propertyOrder" : 6 "propertyOrder" : 8
}, },
"fps" : "fps" :
{ {
@ -66,8 +78,8 @@
"hidden":true "hidden":true
}, },
"required" : true, "required" : true,
"propertyOrder" : 8, "propertyOrder" : 10,
"comment" : "The 'frames per second' setting is dynamically inserted into the WebUI under PropertyOrder '7'." "comment" : "The 'framerates' setting is dynamically inserted into the WebUI under PropertyOrder '9'."
}, },
"sizeDecimation" : "sizeDecimation" :
{ {
@ -77,7 +89,7 @@
"maximum" : 30, "maximum" : 30,
"default" : 6, "default" : 6,
"required" : true, "required" : true,
"propertyOrder" : 9 "propertyOrder" : 11
}, },
"cropLeft" : "cropLeft" :
{ {
@ -87,7 +99,7 @@
"default" : 0, "default" : 0,
"append" : "edt_append_pixel", "append" : "edt_append_pixel",
"required" : true, "required" : true,
"propertyOrder" : 10 "propertyOrder" : 12
}, },
"cropRight" : "cropRight" :
{ {
@ -97,7 +109,7 @@
"default" : 0, "default" : 0,
"append" : "edt_append_pixel", "append" : "edt_append_pixel",
"required" : true, "required" : true,
"propertyOrder" : 11 "propertyOrder" : 13
}, },
"cropTop" : "cropTop" :
{ {
@ -107,7 +119,7 @@
"default" : 0, "default" : 0,
"append" : "edt_append_pixel", "append" : "edt_append_pixel",
"required" : true, "required" : true,
"propertyOrder" : 12 "propertyOrder" : 14
}, },
"cropBottom" : "cropBottom" :
{ {
@ -117,7 +129,7 @@
"default" : 0, "default" : 0,
"append" : "edt_append_pixel", "append" : "edt_append_pixel",
"required" : true, "required" : true,
"propertyOrder" : 13 "propertyOrder" : 15
}, },
"signalDetection" : "signalDetection" :
{ {
@ -125,7 +137,7 @@
"title" : "edt_conf_v4l2_signalDetection_title", "title" : "edt_conf_v4l2_signalDetection_title",
"default" : false, "default" : false,
"required" : true, "required" : true,
"propertyOrder" : 14 "propertyOrder" : 16
}, },
"redSignalThreshold" : "redSignalThreshold" :
{ {
@ -141,7 +153,7 @@
} }
}, },
"required" : true, "required" : true,
"propertyOrder" : 15 "propertyOrder" : 17
}, },
"greenSignalThreshold" : "greenSignalThreshold" :
{ {
@ -157,7 +169,7 @@
} }
}, },
"required" : true, "required" : true,
"propertyOrder" : 16 "propertyOrder" : 18
}, },
"blueSignalThreshold" : "blueSignalThreshold" :
{ {
@ -173,7 +185,7 @@
} }
}, },
"required" : true, "required" : true,
"propertyOrder" : 17 "propertyOrder" : 19
}, },
"sDVOffsetMin" : "sDVOffsetMin" :
{ {
@ -189,7 +201,7 @@
} }
}, },
"required" : true, "required" : true,
"propertyOrder" : 18 "propertyOrder" : 20
}, },
"sDVOffsetMax" : "sDVOffsetMax" :
{ {
@ -205,7 +217,7 @@
} }
}, },
"required" : true, "required" : true,
"propertyOrder" : 19 "propertyOrder" : 21
}, },
"sDHOffsetMin" : "sDHOffsetMin" :
{ {
@ -221,7 +233,7 @@
} }
}, },
"required" : true, "required" : true,
"propertyOrder" : 20 "propertyOrder" : 22
}, },
"sDHOffsetMax" : "sDHOffsetMax" :
{ {
@ -237,7 +249,7 @@
} }
}, },
"required" : true, "required" : true,
"propertyOrder" : 21 "propertyOrder" : 23
} }
}, },
"additionalProperties" : true "additionalProperties" : true

View File

@ -16,6 +16,7 @@
#include <effectengine/EffectModule.h> #include <effectengine/EffectModule.h>
#ifdef _WIN32 #ifdef _WIN32
#include <stdexcept>
#define STRINGIFY2(x) #x #define STRINGIFY2(x) #x
#define STRINGIFY(x) STRINGIFY2(x) #define STRINGIFY(x) STRINGIFY2(x)
#endif #endif

View File

@ -51,6 +51,7 @@ int main(int argc, char** argv)
Parser parser("V4L capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used."); Parser parser("V4L capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used.");
Option & argDevice = parser.add<Option> ('d', "device", "The device to use, can be /dev/video0 [default: %1 (auto detected)]", "auto"); Option & argDevice = parser.add<Option> ('d', "device", "The device to use, can be /dev/video0 [default: %1 (auto detected)]", "auto");
IntOption & argInput = parser.add<IntOption> ('i', "input", "The device input [default: %1]", "0");
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<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"); 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 & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "15", 1, 25);
@ -111,6 +112,7 @@ int main(int argc, char** argv)
argWidth.getInt(parser), argWidth.getInt(parser),
argHeight.getInt(parser), argHeight.getInt(parser),
1000 / argFps.getInt(parser), 1000 / argFps.getInt(parser),
argInput.getInt(parser),
argVideoStandard.switchValue(parser), argVideoStandard.switchValue(parser),
argPixelFormat.switchValue(parser), argPixelFormat.switchValue(parser),
std::max(1, argSizeDecimation.getInt(parser))); std::max(1, argSizeDecimation.getInt(parser)));

View File

@ -462,6 +462,7 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type &settingsType, co
grabberConfig["width"].toInt(0), grabberConfig["width"].toInt(0),
grabberConfig["height"].toInt(0), grabberConfig["height"].toInt(0),
grabberConfig["fps"].toInt(15), grabberConfig["fps"].toInt(15),
grabberConfig["input"].toInt(-1),
parseVideoStandard(grabberConfig["standard"].toString("no-change")), parseVideoStandard(grabberConfig["standard"].toString("no-change")),
parsePixelFormat(grabberConfig["pixelFormat"].toString("no-change")), parsePixelFormat(grabberConfig["pixelFormat"].toString("no-change")),
grabberConfig["sizeDecimation"].toInt(8)); grabberConfig["sizeDecimation"].toInt(8));