From 10f11c2d893fff56f717757ee275ac842d21a468 Mon Sep 17 00:00:00 2001 From: Paulchen Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Fri, 17 Apr 2020 16:59:20 +0200 Subject: [PATCH] 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 --- assets/webconfig/i18n/cs.json | 7 +- assets/webconfig/i18n/de.json | 15 +- assets/webconfig/i18n/en.json | 11 +- assets/webconfig/i18n/es.json | 7 +- assets/webconfig/i18n/it.json | 7 +- assets/webconfig/i18n/nl.json | 7 +- assets/webconfig/i18n/pl.json | 7 +- assets/webconfig/i18n/ro.json | 7 +- assets/webconfig/i18n/sv.json | 5 + assets/webconfig/js/content_grabber.js | 230 +++++++++++++-- include/api/JsonAPI.h | 4 +- include/grabber/V4L2Grabber.h | 82 +++-- include/hyperion/Grabber.h | 49 ++- include/hyperion/GrabberWrapper.h | 30 ++ libsrc/api/JsonAPI.cpp | 37 +++ libsrc/grabber/v4l2/V4L2Grabber.cpp | 279 +++++++++++++----- libsrc/hyperion/Grabber.cpp | 8 +- libsrc/hyperion/GrabberWrapper.cpp | 37 ++- .../hyperion/schema/schema-grabberV4L2.json | 61 ++-- .../utils/jsonschema/QJsonSchemaChecker.cpp | 2 +- 20 files changed, 717 insertions(+), 175 deletions(-) diff --git a/assets/webconfig/i18n/cs.json b/assets/webconfig/i18n/cs.json index 39e2ae1a..732d3c2a 100644 --- a/assets/webconfig/i18n/cs.json +++ b/assets/webconfig/i18n/cs.json @@ -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": "" -} \ No newline at end of file +} diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json index 644049ff..e24e974c 100644 --- a/assets/webconfig/i18n/de.json +++ b/assets/webconfig/i18n/de.json @@ -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'", diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 07d9aaea..21a444c8 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -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", diff --git a/assets/webconfig/i18n/es.json b/assets/webconfig/i18n/es.json index ef6a05ec..5e5bf826 100644 --- a/assets/webconfig/i18n/es.json +++ b/assets/webconfig/i18n/es.json @@ -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": "" -} \ No newline at end of file +} diff --git a/assets/webconfig/i18n/it.json b/assets/webconfig/i18n/it.json index f12164c4..94d7dc7f 100644 --- a/assets/webconfig/i18n/it.json +++ b/assets/webconfig/i18n/it.json @@ -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": "" -} \ No newline at end of file +} diff --git a/assets/webconfig/i18n/nl.json b/assets/webconfig/i18n/nl.json index 55e4f198..2769b315 100644 --- a/assets/webconfig/i18n/nl.json +++ b/assets/webconfig/i18n/nl.json @@ -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": "" -} \ No newline at end of file +} diff --git a/assets/webconfig/i18n/pl.json b/assets/webconfig/i18n/pl.json index 8d13be9a..12f837fe 100644 --- a/assets/webconfig/i18n/pl.json +++ b/assets/webconfig/i18n/pl.json @@ -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": "" -} \ No newline at end of file +} diff --git a/assets/webconfig/i18n/ro.json b/assets/webconfig/i18n/ro.json index a696da96..166e58c4 100644 --- a/assets/webconfig/i18n/ro.json +++ b/assets/webconfig/i18n/ro.json @@ -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": "" -} \ No newline at end of file +} diff --git a/assets/webconfig/i18n/sv.json b/assets/webconfig/i18n/sv.json index 779286a1..d2e70d8a 100644 --- a/assets/webconfig/i18n/sv.json +++ b/assets/webconfig/i18n/sv.json @@ -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", diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index 347b1e11..5d5a0b4e 100644 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -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 -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"]); }); diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 03872699..cb5127b5 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -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); -}; \ No newline at end of file +}; diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index 11fef1d5..698b4506 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -8,6 +8,7 @@ #include #include #include +#include // util includes #include @@ -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 _v4lDevices; - int _input; - VideoStandard _videoStandard; - io_method _ioMethod; - int _fileDescriptor; - std::vector _buffers; + std::map _v4lDevices; + QMap _deviceProperties; + int _input; + VideoStandard _videoStandard; + io_method _ioMethod; + int _fileDescriptor; + std::vector _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); }; diff --git a/include/hyperion/Grabber.h b/include/hyperion/Grabber.h index 06a4e2df..aec65062 100644 --- a/include/hyperion/Grabber.h +++ b/include/hyperion/Grabber.h @@ -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; diff --git a/include/hyperion/GrabberWrapper.h b/include/hyperion/GrabberWrapper.h index 47b6640c..23d64994 100644 --- a/include/hyperion/GrabberWrapper.h +++ b/include/hyperion/GrabberWrapper.h @@ -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: diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 9d628901..679152c5 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -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; diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index a19363d0..94d53753 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -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; +} diff --git a/libsrc/hyperion/Grabber.cpp b/libsrc/hyperion/Grabber.cpp index 5678d5db..c203f760 100644 --- a/libsrc/hyperion/Grabber.cpp +++ b/libsrc/hyperion/Grabber.cpp @@ -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; -} \ No newline at end of file + + return (fps > 0) && (_fps != fps); +} diff --git a/libsrc/hyperion/GrabberWrapper.cpp b/libsrc/hyperion/GrabberWrapper.cpp index 07f168e3..73164cb7 100644 --- a/libsrc/hyperion/GrabberWrapper.cpp +++ b/libsrc/hyperion/GrabberWrapper.cpp @@ -9,6 +9,8 @@ // qt #include +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(); +} diff --git a/libsrc/hyperion/schema/schema-grabberV4L2.json b/libsrc/hyperion/schema/schema-grabberV4L2.json index 87e68ba1..6b239efa 100644 --- a/libsrc/hyperion/schema/schema-grabberV4L2.json +++ b/libsrc/hyperion/schema/schema-grabberV4L2.json @@ -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 diff --git a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp index bff2c75d..e6b11a73 100644 --- a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp +++ b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp @@ -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 {