V4L2 enhanced (#766)

* fix v4l2 standard
* ignore v4l2 meta devices
* added resolution, framerate and device dropdown list to WebUI (thx to @Lord-Grey & @b1rdhous3)

* Fix for kernels prior to v4.16
* Device names added & WebUI adapted
This commit is contained in:
Paulchen Panther 2020-04-17 16:59:20 +02:00 committed by GitHub
parent b92af63cef
commit 10f11c2d89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 717 additions and 175 deletions

View File

@ -431,6 +431,7 @@
"edt_conf_enum_bbclassic": "Klasický",
"edt_conf_enum_bbosd": "OSD",
"edt_conf_enum_automatic": "",
"edt_conf_enum_custom": "",
"edt_conf_gen_heading_title": "Obecné nastavení",
"edt_conf_gen_name_title": "Název konfigurace",
"edt_conf_gen_name_expl": "Uživatelské jméno, které se používá k detekci Hyperionu. (Užitečné s více než jednou instancí Hyperion)",
@ -492,6 +493,10 @@
"edt_conf_v4l2_device_expl": "Cesta k Zachycení/Graber usb.",
"edt_conf_v4l2_standard_title": "Video standard",
"edt_conf_v4l2_standard_expl": "Vyberte standard videa pro vaši oblast.",
"edt_conf_v4l2_resolution_title": "",
"edt_conf_v4l2_resolution_expl": "",
"edt_conf_v4l2_framerate_title": "",
"edt_conf_v4l2_framerate_expl": "",
"edt_conf_v4l2_sizeDecimation_title": "Velikost decimace",
"edt_conf_v4l2_sizeDecimation_expl": "Faktor velikosti decimace",
"edt_conf_v4l2_cropLeft_title": "Ořezat zleva",
@ -809,4 +814,4 @@
"edt_eff_ledtest_header_desc": "",
"update_no_updates_for_branch": "",
"general_speech_sv": ""
}
}

View File

@ -470,7 +470,7 @@
"edt_conf_enum_PAL": "PAL",
"edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_SECAM": "SECAM",
"edt_conf_enum_NO_CHANGE": "Auto",
"edt_conf_enum_NO_CHANGE": "Automatisch",
"edt_conf_enum_logsilent": "Stille",
"edt_conf_enum_logwarn": "Warnung",
"edt_conf_enum_logverbose": "Ausführlich",
@ -479,6 +479,7 @@
"edt_conf_enum_bbclassic": "Klassisch",
"edt_conf_enum_bbosd": "OSD",
"edt_conf_enum_automatic": "Automatisch",
"edt_conf_enum_custom": "Benutzerdefiniert",
"edt_conf_gen_heading_title": "Allgemeine Einstellungen",
"edt_conf_gen_name_title": "Name der Konfiguration",
"edt_conf_gen_name_expl": "Der Name wird verwendet, um Hyperion besser zu identifizieren. (Hilfreich bei mehreren Instanzen)",
@ -539,9 +540,13 @@
"edt_conf_smooth_continuousOutput_expl": "Aktualisiere die LEDs, auch wenn das Bild sich nicht geändert hat.",
"edt_conf_v4l2_heading_title": "USB Aufnahme",
"edt_conf_v4l2_device_title": "Gerät",
"edt_conf_v4l2_device_expl": "Der Pfad zum USB (v4l) Aufnahmegerät. Wähle 'auto' für automatische Erkennung. Beispiel: '/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_standard_title": "Videoformat",
"edt_conf_v4l2_standard_expl": "Wähle das passende Videoformat deiner Region. Auf 'Auto' 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_expl": "Eine Liste von unterstützten Auflösungen des aktiven Gerätes. Auf 'Automatisch' wird der gewählte Modus vom v4l interface beibehalten.",
"edt_conf_v4l2_framerate_title": "Bilder pro Sekunde",
"edt_conf_v4l2_framerate_expl": "Die unterstützten Bilder pro Sekunde des aktiven Gerätes. Auf 'Automatisch' wird der gewählte Modus vom v4l interface beibehalten.",
"edt_conf_v4l2_sizeDecimation_title": "Bildverkleinerung Faktor",
"edt_conf_v4l2_sizeDecimation_expl": "Der Faktor der Bildverkleinerung ausgehend von der ursprünglichen Größe, 1 bedeutet keine Änderung (originales Bild).",
"edt_conf_v4l2_cropLeft_title": "Entferne links",
@ -570,9 +575,9 @@
"edt_conf_v4l2_sDHOffsetMax_expl": "Signal Erkennungs-Bereich horizontal maximum (0.0-1.0)",
"edt_conf_instCapture_heading_title": "Instance Aufnahme",
"edt_conf_instC_systemEnable_title": "Aktiviere Plattform Aufnahme",
"edt_conf_instC_systemEnable_expl": "Aktiviert die Plattform Aufnahme für diese LED Hardware Instanz",
"edt_conf_instC_systemEnable_expl": "Aktiviert die Plattform Aufnahme für diese LED Hardware Instanz.",
"edt_conf_instC_v4lEnable_title": "Aktiviere USB Aufnahme",
"edt_conf_instC_v4lEnable_expl": "Aktiviert die USB Aufnahme für diese LED Hardware Instanz",
"edt_conf_instC_v4lEnable_expl": "Aktiviert die USB Aufnahme für diese LED Hardware Instanz.",
"edt_conf_fg_heading_title": "Plattform Aufnahme",
"edt_conf_fg_type_title": "Typ",
"edt_conf_fg_type_expl": "Art der Plattform Aufnahme, standard ist 'auto'",

View File

@ -469,7 +469,7 @@
"edt_conf_enum_PAL" : "PAL",
"edt_conf_enum_NTSC" : "NTSC",
"edt_conf_enum_SECAM" : "SECAM",
"edt_conf_enum_NO_CHANGE" : "Auto",
"edt_conf_enum_NO_CHANGE" : "Automatic",
"edt_conf_enum_logsilent" : "Silent",
"edt_conf_enum_logwarn" : "Warning",
"edt_conf_enum_logverbose" : "Verbose",
@ -478,6 +478,7 @@
"edt_conf_enum_bbclassic" : "Classic",
"edt_conf_enum_bbosd" : "OSD",
"edt_conf_enum_automatic" : "Automatic",
"edt_conf_enum_custom": "Custom",
"edt_conf_gen_heading_title" : "General Settings",
"edt_conf_gen_name_title" : "Configuration name",
"edt_conf_gen_name_expl" : "A user defined name which is used to identify Hyperion. (Helpful if you have more than one Hyperion instance)",
@ -538,9 +539,13 @@
"edt_conf_smooth_continuousOutput_expl" : "Update the leds even if there is no change in the picture.",
"edt_conf_v4l2_heading_title" : "USB Capture",
"edt_conf_v4l2_device_title" : "Device",
"edt_conf_v4l2_device_expl" : "The path to the USB capture interface. Set to 'auto' 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_standard_title" : "Video standard",
"edt_conf_v4l2_standard_expl" : "Select the video standard for your region. 'Auto' 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_expl" : "A list of supported resolutions of the active device",
"edt_conf_v4l2_framerate_title": "Frames per second",
"edt_conf_v4l2_framerate_expl": "The supported frames per second of the active device",
"edt_conf_v4l2_sizeDecimation_title" : "Size decimation",
"edt_conf_v4l2_sizeDecimation_expl" : "The factor of size decimation. 1 means no decimation (keep original size)",
"edt_conf_v4l2_cropLeft_title" : "Crop left",

View File

@ -431,6 +431,7 @@
"edt_conf_enum_bbclassic": "Clásico",
"edt_conf_enum_bbosd": "OSD",
"edt_conf_enum_automatic": "Automático",
"edt_conf_enum_custom": "",
"edt_conf_gen_heading_title": "Configuración General",
"edt_conf_gen_name_title": "Nombre de configuración",
"edt_conf_gen_name_expl": "Un nombre definido por el usuario que se utiliza para detectar Hyperion. (Útil con más de una instancia de Hyperion)",
@ -492,6 +493,10 @@
"edt_conf_v4l2_device_expl": "La ruta a la captura USB.",
"edt_conf_v4l2_standard_title": "Estándar de vídeo",
"edt_conf_v4l2_standard_expl": "Selecciona el estándar de vídeo para tu región.",
"edt_conf_v4l2_resolution_title": "",
"edt_conf_v4l2_resolution_expl": "",
"edt_conf_v4l2_framerate_title": "",
"edt_conf_v4l2_framerate_expl": "",
"edt_conf_v4l2_sizeDecimation_title": "Diezmación de tamaño",
"edt_conf_v4l2_sizeDecimation_expl": "El factor de diezmación del tamaño",
"edt_conf_v4l2_cropLeft_title": "Cortar izquierda",
@ -809,4 +814,4 @@
"edt_eff_ledtest_header_desc": "Salida giratoria: Rojo, Azul, Verde, Blanco, Negro",
"update_no_updates_for_branch": "",
"general_speech_sv": ""
}
}

View File

@ -431,6 +431,7 @@
"edt_conf_enum_bbclassic": "Classico",
"edt_conf_enum_bbosd": "OSD",
"edt_conf_enum_automatic": "Automatico",
"edt_conf_enum_custom": "",
"edt_conf_gen_heading_title": "impostazioni Generali",
"edt_conf_gen_name_title": "Nome configurazione",
"edt_conf_gen_name_expl": "Un nome definito dall'utente che viene utilizzato per riconoscere Hyperion. (Utile con più di un'istanza di Hyperion)",
@ -492,6 +493,10 @@
"edt_conf_v4l2_device_expl": "Percorso del dispositivo di cattura usb.",
"edt_conf_v4l2_standard_title": "Standard video",
"edt_conf_v4l2_standard_expl": "Seleziona lo standard video per la tua regione",
"edt_conf_v4l2_resolution_title": "",
"edt_conf_v4l2_resolution_expl": "",
"edt_conf_v4l2_framerate_title": "",
"edt_conf_v4l2_framerate_expl": "",
"edt_conf_v4l2_sizeDecimation_title": "Decimazione dimensione",
"edt_conf_v4l2_sizeDecimation_expl": "Il fattore di decimazione della dimensione",
"edt_conf_v4l2_cropLeft_title": "Ritaglia sinistra",
@ -809,4 +814,4 @@
"edt_eff_ledtest_header_desc": "",
"update_no_updates_for_branch": "",
"general_speech_sv": ""
}
}

View File

@ -431,6 +431,7 @@
"edt_conf_enum_bbclassic": "",
"edt_conf_enum_bbosd": "",
"edt_conf_enum_automatic": "",
"edt_conf_enum_custom": "",
"edt_conf_gen_heading_title": "",
"edt_conf_gen_name_title": "",
"edt_conf_gen_name_expl": "",
@ -492,6 +493,10 @@
"edt_conf_v4l2_device_expl": "",
"edt_conf_v4l2_standard_title": "",
"edt_conf_v4l2_standard_expl": "",
"edt_conf_v4l2_resolution_title": "",
"edt_conf_v4l2_resolution_expl": "",
"edt_conf_v4l2_framerate_title": "",
"edt_conf_v4l2_framerate_expl": "",
"edt_conf_v4l2_sizeDecimation_title": "",
"edt_conf_v4l2_sizeDecimation_expl": "",
"edt_conf_v4l2_cropLeft_title": "",
@ -809,4 +814,4 @@
"edt_eff_ledtest_header_desc": "",
"update_no_updates_for_branch": "",
"general_speech_sv": ""
}
}

View File

@ -431,6 +431,7 @@
"edt_conf_enum_bbclassic": "",
"edt_conf_enum_bbosd": "",
"edt_conf_enum_automatic": "",
"edt_conf_enum_custom": "",
"edt_conf_gen_heading_title": "",
"edt_conf_gen_name_title": "",
"edt_conf_gen_name_expl": "",
@ -492,6 +493,10 @@
"edt_conf_v4l2_device_expl": "",
"edt_conf_v4l2_standard_title": "",
"edt_conf_v4l2_standard_expl": "",
"edt_conf_v4l2_resolution_title": "",
"edt_conf_v4l2_resolution_expl": "",
"edt_conf_v4l2_framerate_title": "",
"edt_conf_v4l2_framerate_expl": "",
"edt_conf_v4l2_sizeDecimation_title": "",
"edt_conf_v4l2_sizeDecimation_expl": "",
"edt_conf_v4l2_cropLeft_title": "",
@ -809,4 +814,4 @@
"edt_eff_ledtest_header_desc": "",
"update_no_updates_for_branch": "",
"general_speech_sv": ""
}
}

View File

@ -431,6 +431,7 @@
"edt_conf_enum_bbclassic": "",
"edt_conf_enum_bbosd": "",
"edt_conf_enum_automatic": "",
"edt_conf_enum_custom": "",
"edt_conf_gen_heading_title": "",
"edt_conf_gen_name_title": "",
"edt_conf_gen_name_expl": "",
@ -492,6 +493,10 @@
"edt_conf_v4l2_device_expl": "",
"edt_conf_v4l2_standard_title": "",
"edt_conf_v4l2_standard_expl": "",
"edt_conf_v4l2_resolution_title": "",
"edt_conf_v4l2_resolution_expl": "",
"edt_conf_v4l2_framerate_title": "",
"edt_conf_v4l2_framerate_expl": "",
"edt_conf_v4l2_sizeDecimation_title": "",
"edt_conf_v4l2_sizeDecimation_expl": "",
"edt_conf_v4l2_cropLeft_title": "",
@ -809,4 +814,4 @@
"edt_eff_ledtest_header_desc": "",
"update_no_updates_for_branch": "",
"general_speech_sv": ""
}
}

View File

@ -431,6 +431,7 @@
"edt_conf_enum_bbclassic": "Klassisk",
"edt_conf_enum_bbosd": "OSD",
"edt_conf_enum_automatic": "Automatisk",
"edt_conf_enum_custom": "",
"edt_conf_gen_heading_title": "Allmänna Inställningar",
"edt_conf_gen_name_title": "Konfigurationsnamn",
"edt_conf_gen_name_expl": "Ett användardefinierat namn som används för att identifiera Hyperion. (Hjälpsamt om du har mer än en Hyperion-instans)",
@ -492,6 +493,10 @@
"edt_conf_v4l2_device_expl": "Sökvägen till USB-fångstgränssnittet. Ställ in på 'auto' för automatisk upptäckt. Exempel: '/dev/video0'",
"edt_conf_v4l2_standard_title": "Videostandard",
"edt_conf_v4l2_standard_expl": "Välj videostandard för din region. 'Auto' behåller det värde som väljs av v4l2-gränssnittet",
"edt_conf_v4l2_resolution_title": "",
"edt_conf_v4l2_resolution_expl": "",
"edt_conf_v4l2_framerate_title": "",
"edt_conf_v4l2_framerate_expl": "",
"edt_conf_v4l2_sizeDecimation_title": "Bildreduktionsfaktor",
"edt_conf_v4l2_sizeDecimation_expl": "Faktorn för bildminskning baserad på originalstorleken, 1 betyder ingen förändring (originalbild).",
"edt_conf_v4l2_cropLeft_title": "Beskär vänster",

View File

@ -4,39 +4,171 @@ $(document).ready( function() {
var conf_editor_fg = null;
var conf_editor_instCapt = null;
function hideEl(el)
{
for(var i = 0; i<el.length; i++)
// Dynamic v4l2 enum schema
var v4l2_dynamic_enum_schema = {
"available_devices":
{
$('[data-schemapath*="root.framegrabber.'+el[i]+'"]').toggle(false);
"type": "string",
"title": "edt_conf_v4l2_device_title",
"propertyOrder" : 1,
"required" : true
},
"resolutions":
{
"type": "string",
"title": "edt_conf_v4l2_resolution_title",
"propertyOrder" : 4,
"required" : true
},
"framerates":
{
"type": "string",
"title": "edt_conf_v4l2_framerate_title",
"propertyOrder" : 7,
"required" : true
}
};
// Build dynamic v4l2 enum schema parts
var buildSchemaPart = function(key, schema, device) {
if (schema[key]) {
var enumVals = [];
var enumTitelVals = [];
var v4l2_properties = JSON.parse(JSON.stringify(window.serverInfo.grabbers.v4l2_properties));
if (key === 'available_devices') {
for (var i = 0; i < v4l2_properties.length; i++) {
enumVals.push(v4l2_properties[i]['device']);
v4l2_properties[i].hasOwnProperty('name')
? enumTitelVals.push(v4l2_properties[i]['name'])
: enumTitelVals.push(v4l2_properties[i]['device']);
}
} else if (key == 'resolutions' || key == 'framerates') {
for (var i = 0; i < v4l2_properties.length; i++) {
if (v4l2_properties[i]['device'] == device) {
enumVals = enumTitelVals = v4l2_properties[i][key];
break;
}
}
}
window.schema.grabberV4L2.properties[key] = {
"type": schema[key].type,
"title": schema[key].title,
"enum": [].concat(["auto"], enumVals, ["custom"]),
"options" :
{
"enum_titles" : [].concat(["edt_conf_enum_automatic"], enumTitelVals, ["edt_conf_enum_custom"]),
},
"propertyOrder" : schema[key].propertyOrder,
"required" : schema[key].required
};
}
};
// Switch between visible states
function toggleOption(option, state) {
$('[data-schemapath*="root.grabberV4L2.'+option+'"]').toggle(state);
if (state) (
$('[data-schemapath*="root.grabberV4L2.'+option+'"]').addClass('col-md-12'),
$('label[for="root_grabberV4L2_'+option+'"]').css('left','10px'),
$('[id="root_grabberV4L2_'+option+'"]').css('left','10px')
);
}
if(window.showOptHelp)
{
//fg
// Watch all v4l2 dynamic fields
var setWatchers = function(schema) {
var path = 'root.grabberV4L2.';
Object.keys(schema).forEach(function(key) {
conf_editor_v4l2.watch(path + key, function() {
var ed = conf_editor_v4l2.getEditor(path + key);
var val = ed.getValue();
if (key == 'available_devices') {
var V4L2properties = ['resolutions', 'framerates'];
if (val == 'custom') {
var grabberV4L2 = ed.parent;
V4L2properties.forEach(function(item) {
buildSchemaPart(item, v4l2_dynamic_enum_schema, 'none');
grabberV4L2.original_schema.properties[item] = window.schema.grabberV4L2.properties[item];
grabberV4L2.schema.properties[item] = window.schema.grabberV4L2.properties[item];
grabberV4L2.removeObjectProperty(item);
delete grabberV4L2.cached_editors[item];
grabberV4L2.addObjectProperty(item);
conf_editor_v4l2.getEditor(path + item).enable();
});
toggleOption('device', true);
} else if (val == 'auto') {
V4L2properties.forEach(function(item) {
conf_editor_v4l2.getEditor(path + item).setValue('auto');
conf_editor_v4l2.getEditor(path + item).disable();
});
(toggleOption('device', false), toggleOption('width', false),
toggleOption('height', false), toggleOption('fps', false));
} else {
var grabberV4L2 = ed.parent;
V4L2properties.forEach(function(item) {
buildSchemaPart(item, v4l2_dynamic_enum_schema, val);
grabberV4L2.original_schema.properties[item] = window.schema.grabberV4L2.properties[item];
grabberV4L2.schema.properties[item] = window.schema.grabberV4L2.properties[item];
grabberV4L2.removeObjectProperty(item);
delete grabberV4L2.cached_editors[item];
grabberV4L2.addObjectProperty(item);
conf_editor_v4l2.getEditor(path + item).enable();
});
toggleOption('device', false);
}
}
if (key == 'resolutions')
val != 'custom'
? (toggleOption('width', false), toggleOption('height', false))
: (toggleOption('width', true), toggleOption('height', true));
if (key == 'framerates')
val != 'custom'
? toggleOption('fps', false)
: toggleOption('fps', true);
});
});
};
// Insert dynamic v4l2 enum schema parts
Object.keys(v4l2_dynamic_enum_schema).forEach(function(key) {
buildSchemaPart(key, v4l2_dynamic_enum_schema, window.serverConfig.grabberV4L2.device);
});
if(window.showOptHelp) {
// Instance Capture
$('#conf_cont').append(createRow('conf_cont_instCapt'));
$('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt'));
$('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title")));
//fg
// Framegrabber
$('#conf_cont').append(createRow('conf_cont_fg'));
$('#conf_cont_fg').append(createOptPanel('fa-camera', $.i18n("edt_conf_fg_heading_title"), 'editor_container_fg', 'btn_submit_fg'));
$('#conf_cont_fg').append(createHelpTable(window.schema.framegrabber.properties, $.i18n("edt_conf_fg_heading_title")));
//v4l
// V4L2
$('#conf_cont').append(createRow('conf_cont_v4l'));
$('#conf_cont_v4l').append(createOptPanel('fa-camera', $.i18n("edt_conf_v4l2_heading_title"), 'editor_container_v4l2', 'btn_submit_v4l2'));
$('#conf_cont_v4l').append(createHelpTable(window.schema.grabberV4L2.properties, $.i18n("edt_conf_v4l2_heading_title")));
}
else
{
} else {
$('#conf_cont').addClass('row');
$('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt'));
$('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_fg_heading_title"), 'editor_container_fg', 'btn_submit_fg'));
$('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_v4l2_heading_title"), 'editor_container_v4l2', 'btn_submit_v4l2'));
}
//instCapt
// Instance Capture
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
instCapture: window.schema.instCapture
}, true, true);
@ -49,8 +181,7 @@ $(document).ready( function() {
requestWriteConfig(conf_editor_instCapt.getValue());
});
//fg
// Framegrabber
conf_editor_fg = createJsonEditor('editor_container_fg', {
framegrabber: window.schema.framegrabber
}, true, true);
@ -63,7 +194,6 @@ $(document).ready( function() {
requestWriteConfig(conf_editor_fg.getValue());
});
//vl4
conf_editor_v4l2 = createJsonEditor('editor_container_v4l2', {
grabberV4L2 : window.schema.grabberV4L2
}, true, true);
@ -72,28 +202,80 @@ $(document).ready( function() {
conf_editor_v4l2.validate().length ? $('#btn_submit_v4l2').attr('disabled', true) : $('#btn_submit_v4l2').attr('disabled', false);
});
$('#btn_submit_v4l2').off().on('click',function() {
requestWriteConfig(conf_editor_v4l2.getValue());
conf_editor_v4l2.on('ready', function() {
setWatchers(v4l2_dynamic_enum_schema);
if (window.serverConfig.grabberV4L2.available_devices == 'custom' && window.serverConfig.grabberV4L2.device != 'auto')
toggleOption('device', true);
if (window.serverConfig.grabberV4L2.device == 'auto')
conf_editor_v4l2.getEditor('root.grabberV4L2.available_devices').setValue('auto');
if (window.serverConfig.grabberV4L2.available_devices == 'auto') {
['resolutions', 'framerates'].forEach(function(item) {
conf_editor_v4l2.getEditor('root.grabberV4L2.' + item).setValue('auto');
conf_editor_v4l2.getEditor('root.grabberV4L2.' + item).disable();
});
}
if (window.serverConfig.grabberV4L2.resolutions == 'custom' && window.serverConfig.grabberV4L2.device != 'auto')
(toggleOption('width', true), toggleOption('height', true));
if (window.serverConfig.grabberV4L2.framerates == 'custom' && window.serverConfig.grabberV4L2.device != 'auto')
toggleOption('fps', true);
});
$('#btn_submit_v4l2').off().on('click',function() {
var v4l2Options = conf_editor_v4l2.getValue()
if (v4l2Options.grabberV4L2.available_devices != 'custom' && v4l2Options.grabberV4L2.available_devices != 'auto')
v4l2Options.grabberV4L2.device = v4l2Options.grabberV4L2.available_devices;
if (v4l2Options.grabberV4L2.available_devices == 'auto')
v4l2Options.grabberV4L2.device = '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.height = parseInt(v4l2Options.grabberV4L2.resolutions.split('x')[1]));
if (v4l2Options.grabberV4L2.resolutions == 'auto')
(v4l2Options.grabberV4L2.width = 0, v4l2Options.grabberV4L2.height = 0);
if (v4l2Options.grabberV4L2.framerates != 'custom' && v4l2Options.grabberV4L2.framerates != 'auto' && v4l2Options.grabberV4L2.available_devices != 'auto')
v4l2Options.grabberV4L2.fps = parseInt(v4l2Options.grabberV4L2.framerates);
if (v4l2Options.grabberV4L2.framerates == 'auto')
v4l2Options.grabberV4L2.fps = 15;
requestWriteConfig(v4l2Options);
});
//////////////////////////////////////////////////
//create introduction
if(window.showOptHelp)
{
if(window.showOptHelp) {
createHint("intro", $.i18n('conf_grabber_fg_intro'), "editor_container_fg");
createHint("intro", $.i18n('conf_grabber_v4l_intro'), "editor_container_v4l2");
}
function hideEl(el) {
for(var i = 0; i<el.length; i++) {
$('[data-schemapath*="root.framegrabber.'+el[i]+'"]').toggle(false);
}
}
//hide specific options
conf_editor_fg.on('ready',function() {
var grabbers = window.serverInfo.grabbers.available;
if(grabbers.indexOf('dispmanx') > -1)
if (grabbers.indexOf('dispmanx') > -1)
hideEl(["device","pixelDecimation"]);
else if(grabbers.indexOf('x11') > -1)
else if (grabbers.indexOf('x11') > -1)
hideEl(["device","width","height"]);
else if(grabbers.indexOf('osx') > -1 )
else if (grabbers.indexOf('osx') > -1 )
hideEl(["device","pixelDecimation"]);
else if(grabbers.indexOf('amlogic') > -1)
else if (grabbers.indexOf('amlogic') > -1)
hideEl(["pixelDecimation"]);
});

View File

@ -216,7 +216,7 @@ private:
///
void handleConfigCommand(const QJsonObject &message, const QString &command, const int tan);
/// Handle an incoming JSON GetConfig message from handleConfigCommand()
/// Handle an incoming JSON GetSchema message from handleConfigCommand()
///
/// @param message the incoming message
///
@ -297,4 +297,4 @@ private:
/// @brief Kill all signal/slot connections to stop possible data emitter
///
void stopDataConnections(void);
};
};

View File

@ -8,6 +8,7 @@
#include <QObject>
#include <QSocketNotifier>
#include <QRectF>
#include <QMap>
// util includes
#include <utils/PixelFormat.h>
@ -40,6 +41,13 @@ class V4L2Grabber : public Grabber
Q_OBJECT
public:
struct DeviceProperties
{
QString name = QString();
QStringList resolutions = QStringList();
QStringList framerates = QStringList();
};
V4L2Grabber(const QString & device,
const unsigned width,
const unsigned height,
@ -48,7 +56,7 @@ public:
PixelFormat pixelFormat,
int pixelDecimation
);
virtual ~V4L2Grabber();
~V4L2Grabber() override;
QRectF getSignalDetectionOffset()
{
@ -63,44 +71,64 @@ public:
/// @brief set new PixelDecimation value to ImageResampler
/// @param pixelDecimation The new pixelDecimation value
///
virtual void setPixelDecimation(int pixelDecimation);
void setPixelDecimation(int pixelDecimation) override;
///
/// @brief overwrite Grabber.h implementation
///
virtual void setSignalThreshold(
void setSignalThreshold(
double redSignalThreshold,
double greenSignalThreshold,
double blueSignalThreshold,
int noSignalCounterThreshold = 50);
int noSignalCounterThreshold = 50) override;
///
/// @brief overwrite Grabber.h implementation
///
virtual void setSignalDetectionOffset(
void setSignalDetectionOffset(
double verticalMin,
double horizontalMin,
double verticalMax,
double horizontalMax);
double horizontalMax) override;
///
/// @brief overwrite Grabber.h implementation
///
virtual void setSignalDetectionEnable(bool enable);
///
/// @brief overwrite Grabber.h implementation
///
virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard);
///
/// @brief overwrite Grabber.h implementation
///
virtual bool setFramerate(int fps);
void setSignalDetectionEnable(bool enable) override;
///
/// @brief overwrite Grabber.h implementation
///
virtual bool setWidthHeight(int width, int height);
void setDeviceVideoStandard(QString device, VideoStandard videoStandard) override;
///
/// @brief overwrite Grabber.h implementation
///
bool setFramerate(int fps) override;
///
/// @brief overwrite Grabber.h implementation
///
bool setWidthHeight(int width, int height) override;
///
/// @brief overwrite Grabber.h implementation
///
QStringList getV4L2devices() override;
///
/// @brief overwrite Grabber.h implementation
///
QString getV4L2deviceName(QString devicePath) override;
///
/// @brief overwrite Grabber.h implementation
///
QStringList getResolutions(QString devicePath) override;
///
/// @brief overwrite Grabber.h implementation
///
QStringList getFramerates(QString devicePath) override;
public slots:
@ -145,6 +173,8 @@ private:
int xioctl(int request, void *arg);
int xioctl(int fileDescriptor, int request, void *arg);
void throw_exception(const QString & error)
{
Error(_log, "Throws error: %s", QSTRING_CSTR(error));
@ -198,12 +228,13 @@ private:
private:
QString _deviceName;
std::map<QString,QString> _v4lDevices;
int _input;
VideoStandard _videoStandard;
io_method _ioMethod;
int _fileDescriptor;
std::vector<buffer> _buffers;
std::map<QString, QString> _v4lDevices;
QMap<QString, V4L2Grabber::DeviceProperties> _deviceProperties;
int _input;
VideoStandard _videoStandard;
io_method _ioMethod;
int _fileDescriptor;
std::vector<buffer> _buffers;
PixelFormat _pixelFormat;
int _pixelDecimation;
@ -225,4 +256,7 @@ private:
bool _initialized;
bool _deviceAutoDiscoverEnabled;
protected:
void enumFrameIntervals(QStringList &framerates, int fileDescriptor, int pixelformat, int width, int height);
};

View File

@ -20,7 +20,7 @@ class Grabber : public QObject
Q_OBJECT
public:
Grabber(QString grabberName, int width=0, int height=0, int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
Grabber(QString grabberName = "", int width=0, int height=0, int cropLeft=0, int cropRight=0, int cropTop=0, int cropBottom=0);
virtual ~Grabber();
///
@ -42,14 +42,14 @@ public:
///
/// @brief Apply new framerate (used from v4l)
/// @param fps framesPerSecond
/// @param fps framesPerSecond
///
virtual bool setFramerate(int fps);
///
/// @brief Apply new pixelDecimation (used from x11 and qt)
///
virtual void setPixelDecimation(int pixelDecimation) {};
virtual void setPixelDecimation(int pixelDecimation) {}
///
/// @brief Apply new signalThreshold (used from v4l)
@ -58,7 +58,7 @@ public:
double redSignalThreshold,
double greenSignalThreshold,
double blueSignalThreshold,
int noSignalCounterThreshold = 50) {};
int noSignalCounterThreshold = 50) {}
///
/// @brief Apply new SignalDetectionOffset (used from v4l)
///
@ -66,43 +66,70 @@ public:
double verticalMin,
double horizontalMin,
double verticalMax,
double horizontalMax) {};
double horizontalMax) {}
///
/// @brief Apply SignalDetectionEnable (used from v4l)
///
virtual void setSignalDetectionEnable(bool enable) {};
virtual void setSignalDetectionEnable(bool enable) {}
///
/// @brief Apply device and videoStanded (used from v4l)
///
virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard) {};
virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard) {}
///
/// @brief Apply display index (used from qt)
///
virtual void setDisplayIndex(int index) {};
virtual void setDisplayIndex(int index) {}
///
/// @brief Apply path for device (used from framebuffer)
///
virtual void setDevicePath(const QString& path) {};
virtual void setDevicePath(const QString& path) {}
///
/// @brief get current resulting height of image (after crop)
///
virtual int getImageWidth() { return _width; };
virtual int getImageWidth() { return _width; }
///
/// @brief get current resulting width of image (after crop)
///
virtual int getImageHeight() { return _height; };
virtual int getImageHeight() { return _height; }
///
/// @brief Prevent the real capture implementation from capturing if disabled
///
void setEnabled(bool enable);
///
/// @brief Get a list of all available V4L devices
/// @return List of all available V4L devices on success else empty List
///
virtual QStringList getV4L2devices() { return QStringList(); }
///
/// @brief Get the V4L device name
/// @param devicePath The device path
/// @return The name of the V4L device on success else empty String
///
virtual QString getV4L2deviceName(QString devicePath) { return QString(); }
///
/// @brief Get a list of supported device resolutions
/// @param devicePath The device path
/// @return List of resolutions on success else empty List
///
virtual QStringList getResolutions(QString devicePath) { return QStringList(); }
///
/// @brief Get a list of supported device framerates
/// @param devicePath The device path
/// @return List of framerates on success else empty List
///
virtual QStringList getFramerates(QString devicePath) { return QStringList(); }
protected:
ImageResampler _imageResampler;

View File

@ -32,6 +32,9 @@ public:
virtual ~GrabberWrapper();
static GrabberWrapper* instance;
static GrabberWrapper* getInstance(){ return instance; }
///
/// Starts the grabber wich produces led values with the specified update rate
///
@ -47,6 +50,33 @@ public:
///
virtual void stop();
///
/// @brief Get a list of all available V4L devices
/// @return List of all available V4L devices on success else empty List
///
virtual QStringList getV4L2devices();
///
/// @brief Get the V4L device name
/// @param devicePath The device path
/// @return The name of the V4L device on success else empty String
///
virtual QString getV4L2deviceName(QString devicePath);
///
/// @brief Get a list of supported device resolutions
/// @param devicePath The device path
/// @return List of resolutions on success else empty List
///
virtual QStringList getResolutions(QString devicePath);
///
/// @brief Get a list of supported device framerates
/// @param devicePath The device path
/// @return List of framerates on success else empty List
///
virtual QStringList getFramerates(QString devicePath);
static QStringList availableGrabbers();
public:

View File

@ -469,14 +469,51 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
QJsonObject grabbers;
QJsonArray availableGrabbers;
QJsonObject availableProperties;
#if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11)
// get available grabbers
//grabbers["active"] = ????;
for (auto grabber : GrabberWrapper::availableGrabbers())
{
availableGrabbers.append(grabber);
}
#endif
#if defined(ENABLE_V4L2)
QJsonArray availableV4L2devices;
for (auto devicePath : GrabberWrapper::getInstance()->getV4L2devices())
{
QJsonObject device;
device["device"] = devicePath;
device["name"] = GrabberWrapper::getInstance()->getV4L2deviceName(devicePath);
QJsonArray availableResolutions;
for (auto resolution : GrabberWrapper::getInstance()->getResolutions(devicePath))
{
availableResolutions.append(resolution);
}
device.insert("resolutions", availableResolutions);
QJsonArray availableFramerates;
for (auto framerate : GrabberWrapper::getInstance()->getFramerates(devicePath))
{
availableFramerates.append(framerate);
}
device.insert("framerates", availableFramerates);
availableV4L2devices.append(device);
}
grabbers["v4l2_properties"] = availableV4L2devices;
#endif
grabbers["available"] = availableGrabbers;
info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode()));
info["grabbers"] = grabbers;

View File

@ -26,6 +26,10 @@
#define CLEAR(x) memset(&(x), 0, sizeof(x))
#ifndef V4L2_CAP_META_CAPTURE
#define V4L2_CAP_META_CAPTURE 0x00800000 // Specified in kernel header v4.16. Required for backward compatibility.
#endif
V4L2Grabber::V4L2Grabber(const QString & device
, const unsigned width
, const unsigned height
@ -84,13 +88,13 @@ void V4L2Grabber::uninit()
bool V4L2Grabber::init()
{
if (! _initialized)
if (!_initialized)
{
getV4Ldevices();
QString v4lDevices_str;
// show list only once
if ( ! QString(QSTRING_CSTR(_deviceName)).startsWith("/dev/") )
if (!QString(QSTRING_CSTR(_deviceName)).startsWith("/dev/"))
{
for (auto& dev: _v4lDevices)
{
@ -100,7 +104,7 @@ bool V4L2Grabber::init()
Info(_log, "available V4L2 devices:\n%s", QSTRING_CSTR(v4lDevices_str));
}
if ( _deviceName == "auto" )
if (_deviceName == "auto")
{
_deviceAutoDiscoverEnabled = true;
_deviceName = "unknown";
@ -108,20 +112,20 @@ bool V4L2Grabber::init()
for (auto& dev: _v4lDevices)
{
_deviceName = dev.first;
if ( init() )
if (init())
{
Info(_log, "found usable v4l2 device: %s (%s)",QSTRING_CSTR(dev.first), QSTRING_CSTR(dev.second));
_deviceAutoDiscoverEnabled = false;
return _initialized;
}
}
Info( _log, "no usable device found" );
Info(_log, "no usable device found");
}
else if ( ! _deviceName.startsWith("/dev/") )
else if (!_deviceName.startsWith("/dev/"))
{
for (auto& dev: _v4lDevices)
{
if ( _deviceName.toLower() == dev.second.toLower() )
if (_deviceName.toLower() == dev.second.toLower())
{
_deviceName = dev.first;
Info(_log, "found v4l2 device with configured name: %s (%s)", QSTRING_CSTR(dev.second), QSTRING_CSTR(dev.first) );
@ -138,7 +142,7 @@ bool V4L2Grabber::init()
try
{
// do not init with unknown device
if(_deviceName != "unknown")
if (_deviceName != "unknown")
{
if (open_device())
{
@ -165,22 +169,96 @@ bool V4L2Grabber::init()
void V4L2Grabber::getV4Ldevices()
{
QDirIterator it("/sys/class/video4linux/", QDirIterator::NoIteratorFlags);
_deviceProperties.clear();
while(it.hasNext())
{
//_v4lDevices
QString dev = it.next();
if (it.fileName().startsWith("video"))
{
QString devName = "/dev/" + it.fileName();
int fd = open(QSTRING_CSTR(devName), O_RDWR | O_NONBLOCK, 0);
if (fd < 0)
{
throw_errno_exception("Cannot open '" + devName + "'");
continue;
}
struct v4l2_capability cap;
CLEAR(cap);
if (xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
{
throw_errno_exception("'" + devName + "' is no V4L2 device");
close(fd);
continue;
}
if (cap.device_caps & V4L2_CAP_META_CAPTURE) // this device has bit 23 set (and bit 1 reset), so it doesn't have capture.
{
close(fd);
continue;
}
// get the current settings
struct v4l2_format fmt;
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(fd, VIDIOC_G_FMT, &fmt) < 0)
{
close(fd);
continue;
}
V4L2Grabber::DeviceProperties properties;
// collect available device resolutions & frame rates
struct v4l2_frmsizeenum frmsizeenum;
CLEAR(frmsizeenum);
frmsizeenum.index = 0;
frmsizeenum.pixel_format = fmt.fmt.pix.pixelformat;
while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) >= 0)
{
switch (frmsizeenum.type)
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
{
properties.resolutions << QString::number(frmsizeenum.discrete.width) + "x" + QString::number(frmsizeenum.discrete.height);
enumFrameIntervals(properties.framerates, fd, fmt.fmt.pix.pixelformat, frmsizeenum.discrete.width, frmsizeenum.discrete.height);
}
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE:
{
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)
{
properties.resolutions << QString::number(x) + "x" + QString::number(y);
enumFrameIntervals(properties.framerates, fd, fmt.fmt.pix.pixelformat, x, y);
}
}
}
}
frmsizeenum.index++;
}
if (close(fd) < 0) continue;
QFile devNameFile(dev+"/name");
QString devName;
if ( devNameFile.exists())
if (devNameFile.exists())
{
devNameFile.open(QFile::ReadOnly);
devName = devNameFile.readLine();
devName = devName.trimmed();
properties.name = devName;
devNameFile.close();
}
_v4lDevices.emplace("/dev/"+it.fileName(), devName);
_deviceProperties.insert("/dev/"+it.fileName(), properties);
}
}
}
@ -237,6 +315,7 @@ void V4L2Grabber::stop()
uninit_device();
close_device();
_initialized = false;
_deviceProperties.clear();
Info(_log, "Stopped");
}
}
@ -409,6 +488,8 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
{
struct v4l2_capability cap;
CLEAR(cap);
if (-1 == xioctl(VIDIOC_QUERYCAP, &cap))
{
if (EINVAL == errno)
@ -484,6 +565,8 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
// set input if needed and supported
struct v4l2_input v4l2Input;
CLEAR(v4l2Input);
v4l2Input.index = input;
if (input >= 0 && 0 == xioctl(VIDIOC_ENUMINPUT,&v4l2Input))
@ -496,41 +579,46 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
}
// set the video standard if needed and supported
v4l2_std_id std_id;
if (-1 != xioctl(VIDIOC_ENUMSTD, &std_id))
struct v4l2_standard standard;
CLEAR(standard);
if (-1 != xioctl(VIDIOC_ENUMSTD, &standard))
{
switch (videoStandard)
{
case VIDEOSTANDARD_PAL:
{
std_id = V4L2_STD_PAL;
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
standard.id = V4L2_STD_PAL;
if (-1 == xioctl(VIDIOC_S_STD, &standard.id))
{
throw_errno_exception("VIDIOC_S_STD");
break;
}
Debug(_log, "Video standard=PAL");
}
break;
case VIDEOSTANDARD_NTSC:
{
std_id = V4L2_STD_NTSC;
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
standard.id = V4L2_STD_NTSC;
if (-1 == xioctl(VIDIOC_S_STD, &standard.id))
{
throw_errno_exception("VIDIOC_S_STD");
break;
}
Debug(_log, "Video standard=NTSC");
}
break;
case VIDEOSTANDARD_SECAM:
{
std_id = V4L2_STD_SECAM;
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
standard.id = V4L2_STD_SECAM;
if (-1 == xioctl(VIDIOC_S_STD, &standard.id))
{
throw_errno_exception("VIDIOC_S_STD");
break;
}
Debug(_log, "Video standard=SECAM");
}
break;
@ -544,6 +632,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
// get the current settings
struct v4l2_format fmt;
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
{
@ -581,42 +670,14 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
break;
}
// collect available device resolutions
QString v4lDevice_res;
v4l2_frmsizeenum frmsizeenum;
CLEAR(frmsizeenum);
frmsizeenum.index = 0;
frmsizeenum.pixel_format = fmt.fmt.pix.pixelformat;
while (xioctl(VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) >= 0)
// set custom resolution for width and height if they are not zero
if(_width && _height)
{
switch (frmsizeenum.type)
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
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:
{
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++;
fmt.fmt.pix.width = _width;
fmt.fmt.pix.height = _height;
}
// 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
fmt.fmt.pix.width = _width;
fmt.fmt.pix.height = _height;
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
{
throw_errno_exception("VIDIOC_S_FMT");
@ -633,30 +694,19 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
// Trying to set frame rate
struct v4l2_streamparm streamparms;
CLEAR(streamparms);
streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(VIDIOC_G_PARM, &streamparms))
// Check that the driver knows about framerate get/set
if (xioctl(VIDIOC_G_PARM, &streamparms) >= 0)
{
Debug(_log, "Frame rate settings not supported");
// continue
}
else
{
// Check the capability flag is set to V4L2_CAP_TIMEPERFRAME
// Check if the device is able to accept a capture framerate set.
if (streamparms.parm.capture.capability == V4L2_CAP_TIMEPERFRAME)
{
// Driver supports the feature. Set required framerate
CLEAR(streamparms);
streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
streamparms.parm.capture.timeperframe.numerator = 1;
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);
(-1 == xioctl(VIDIOC_S_PARM, &streamparms))
? Debug(_log, "Frame rate settings not supported.")
: Debug(_log, "Set framerate to %d fps", streamparms.parm.capture.timeperframe.denominator);
}
}
@ -883,7 +933,11 @@ int V4L2Grabber::read_frame()
case EIO: /* Could ignore EIO, see spec. */
default:
{
throw_errno_exception("VIDIOC_DQBUF");
stop();
getV4Ldevices();
}
return 0;
}
}
@ -916,7 +970,11 @@ int V4L2Grabber::read_frame()
case EIO: /* Could ignore EIO, see spec. */
default:
{
throw_errno_exception("VIDIOC_DQBUF");
stop();
getV4Ldevices();
}
return 0;
}
}
@ -953,9 +1011,9 @@ bool V4L2Grabber::process_image(const void *p, int size)
{
// We do want a new frame...
#ifdef HAVE_JPEG_DECODER
if (size != _frameByteSize && _pixelFormat != PIXELFORMAT_MJPEG)
if (size < _frameByteSize && _pixelFormat != PIXELFORMAT_MJPEG)
#else
if (size != _frameByteSize)
if (size < _frameByteSize)
#endif
{
Error(_log, "Frame too small: %d != %d", size, _frameByteSize);
@ -1100,7 +1158,7 @@ void V4L2Grabber::process_image(const uint8_t * data, int size)
* ------------ END of JPEG decoder related code ------------
* --------------------------------------------------------*/
_imageResampler.processImage(data, _width, _height, _lineLength, _pixelFormat, image);
_imageResampler.processImage(data, _width, _height, _lineLength, _pixelFormat, image);
if (_signalDetectionEnabled)
{
@ -1168,6 +1226,60 @@ int V4L2Grabber::xioctl(int request, void *arg)
return r;
}
int V4L2Grabber::xioctl(int fileDescriptor, int request, void *arg)
{
int r;
do
{
r = ioctl(fileDescriptor, request, arg);
}
while (r < 0 && errno == EINTR );
return r;
}
void V4L2Grabber::enumFrameIntervals(QStringList &framerates, int fileDescriptor, int pixelformat, int width, int height)
{
// collect available frame rates
struct v4l2_frmivalenum frmivalenum;
CLEAR(frmivalenum);
frmivalenum.index = 0;
frmivalenum.pixel_format = pixelformat;
frmivalenum.width = width;
frmivalenum.height = height;
while (xioctl(fileDescriptor, VIDIOC_ENUM_FRAMEINTERVALS, &frmivalenum) >= 0)
{
int rate;
switch (frmivalenum.type)
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
{
if (frmivalenum.discrete.numerator != 0)
{
rate = frmivalenum.discrete.denominator / frmivalenum.discrete.numerator;
if (!framerates.contains(QString::number(rate)))
framerates.append(QString::number(rate));
}
}
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE:
{
if (frmivalenum.stepwise.min.denominator != 0)
{
rate = frmivalenum.stepwise.min.denominator / frmivalenum.stepwise.min.numerator;
if (!framerates.contains(QString::number(rate)))
framerates.append(QString::number(rate));
}
}
}
frmivalenum.index++;
}
}
void V4L2Grabber::setSignalDetectionEnable(bool enable)
{
if (_signalDetectionEnabled != enable)
@ -1227,3 +1339,28 @@ bool V4L2Grabber::setWidthHeight(int width, int height)
}
return false;
}
QStringList V4L2Grabber::getV4L2devices()
{
QStringList result = QStringList();
for (auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it)
{
result << it.key();
}
return result;
}
QString V4L2Grabber::getV4L2deviceName(QString devicePath)
{
return _deviceProperties.value(devicePath).name;
}
QStringList V4L2Grabber::getResolutions(QString devicePath)
{
return _deviceProperties.value(devicePath).resolutions;
}
QStringList V4L2Grabber::getFramerates(QString devicePath)
{
return _deviceProperties.value(devicePath).framerates;
}

View File

@ -90,8 +90,8 @@ bool Grabber::setWidthHeight(int width, int height)
bool Grabber::setFramerate(int fps)
{
if(fps > 0)
if((fps > 0) && (_fps != fps))
_fps = fps;
return fps > 0;
}
return (fps > 0) && (_fps != fps);
}

View File

@ -9,6 +9,8 @@
// qt
#include <QTimer>
GrabberWrapper* GrabberWrapper::instance = nullptr;
GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned width, unsigned height, const unsigned updateRate_Hz)
: _grabberName(grabberName)
, _timer(new QTimer(this))
@ -17,6 +19,8 @@ GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned
, _ggrabber(ggrabber)
, _image(0,0)
{
GrabberWrapper::instance = this;
// Configure the timer to generate events every n milliseconds
_timer->setInterval(_updateInterval_ms);
@ -89,7 +93,6 @@ QStringList GrabberWrapper::availableGrabbers()
return grabbers;
}
void GrabberWrapper::setVideoMode(const VideoMode& mode)
{
if (_ggrabber != nullptr)
@ -216,3 +219,35 @@ void GrabberWrapper::tryStart()
start();
}
}
QStringList GrabberWrapper::getV4L2devices()
{
if(_grabberName.startsWith("V4L"))
return _ggrabber->getV4L2devices();
return QStringList();
}
QString GrabberWrapper::getV4L2deviceName(QString devicePath)
{
if(_grabberName.startsWith("V4L"))
return _ggrabber->getV4L2deviceName(devicePath);
return QString();
}
QStringList GrabberWrapper::getResolutions(QString devicePath)
{
if(_grabberName.startsWith("V4L"))
return _ggrabber->getResolutions(devicePath);
return QStringList();
}
QStringList GrabberWrapper::getFramerates(QString devicePath)
{
if(_grabberName.startsWith("V4L"))
return _ggrabber->getFramerates(devicePath);
return QStringList();
}

View File

@ -7,10 +7,14 @@
"device" :
{
"type" : "string",
"title" : "edt_conf_v4l2_device_title",
"title" : "edt_conf_enum_custom",
"default" : "auto",
"options" : {
"hidden":true
},
"required" : true,
"propertyOrder" : 1
"propertyOrder" : 2,
"comment" : "The 'available_device' settings are dynamically inserted into the WebUI under PropertyOrder '1'."
},
"standard" :
{
@ -22,7 +26,7 @@
"enum_titles" : ["edt_conf_enum_NO_CHANGE", "edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM"]
},
"required" : true,
"propertyOrder" : 2
"propertyOrder" : 3
},
"width" :
{
@ -30,9 +34,13 @@
"title" : "edt_conf_fg_width_title",
"default" : 0,
"minimum" : 0,
"append" : "edt_append_pixel",
"options" : {
"hidden":true
},
"required" : true,
"access" : "expert",
"propertyOrder" : 3
"propertyOrder" : 5,
"comment" : "The 'resolution' settings are dynamically inserted into the WebUI under PropertyOrder '4'."
},
"height" :
{
@ -40,19 +48,26 @@
"title" : "edt_conf_fg_height_title",
"default" : 0,
"minimum" : 0,
"append" : "edt_append_pixel",
"options" : {
"hidden":true
},
"required" : true,
"access" : "expert",
"propertyOrder" : 4
"propertyOrder" : 6
},
"fps" :
{
"type" : "integer",
"title" : "Framerate",
"title" : "edt_conf_enum_custom",
"default" : 15,
"minimum" : 1,
"append" : "fps",
"options" : {
"hidden":true
},
"required" : true,
"access" : "expert",
"propertyOrder" : 5
"propertyOrder" : 8,
"comment" : "The 'frames per second' setting is dynamically inserted into the WebUI under PropertyOrder '7'."
},
"sizeDecimation" :
{
@ -62,7 +77,7 @@
"maximum" : 30,
"default" : 6,
"required" : true,
"propertyOrder" : 6
"propertyOrder" : 9
},
"cropLeft" :
{
@ -72,7 +87,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 7
"propertyOrder" : 10
},
"cropRight" :
{
@ -82,7 +97,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 8
"propertyOrder" : 11
},
"cropTop" :
{
@ -92,7 +107,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 9
"propertyOrder" : 12
},
"cropBottom" :
{
@ -102,7 +117,7 @@
"default" : 0,
"append" : "edt_append_pixel",
"required" : true,
"propertyOrder" : 10
"propertyOrder" : 13
},
"signalDetection" :
{
@ -110,7 +125,7 @@
"title" : "edt_conf_v4l2_signalDetection_title",
"default" : false,
"required" : true,
"propertyOrder" : 11
"propertyOrder" : 14
},
"redSignalThreshold" :
{
@ -126,7 +141,7 @@
}
},
"required" : true,
"propertyOrder" : 12
"propertyOrder" : 15
},
"greenSignalThreshold" :
{
@ -142,7 +157,7 @@
}
},
"required" : true,
"propertyOrder" : 13
"propertyOrder" : 16
},
"blueSignalThreshold" :
{
@ -158,7 +173,7 @@
}
},
"required" : true,
"propertyOrder" : 14
"propertyOrder" : 17
},
"sDVOffsetMin" :
{
@ -174,7 +189,7 @@
}
},
"required" : true,
"propertyOrder" : 15
"propertyOrder" : 18
},
"sDVOffsetMax" :
{
@ -190,7 +205,7 @@
}
},
"required" : true,
"propertyOrder" : 16
"propertyOrder" : 19
},
"sDHOffsetMin" :
{
@ -206,7 +221,7 @@
}
},
"required" : true,
"propertyOrder" : 17
"propertyOrder" : 20
},
"sDHOffsetMax" :
{
@ -222,7 +237,7 @@
}
},
"required" : true,
"propertyOrder" : 18
"propertyOrder" : 21
}
},
"additionalProperties" : true

View File

@ -138,7 +138,7 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
; // 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 == "allowEmptyArray")
|| attribute == "access" || attribute == "options" || attribute == "script" || attribute == "allowEmptyArray" || attribute == "comment")
; // nothing to do.
else
{