Version 1.0.0
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1 +1,4 @@
|
|||||||
build/
|
build/
|
||||||
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
|
*.png
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(HDMIViewer VERSION 1.4.0 LANGUAGES CXX)
|
project(HDMIViewer VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|||||||
28
PKGBUILD
Normal file
28
PKGBUILD
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Maintainer: Dany Thinnes <dany@projekt-hirnfrei.de>
|
||||||
|
pkgname=hdmi-viewer
|
||||||
|
pkgver=1.0.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Schlanker Qt6/C++ Live-Viewer für HDMI-Grabber (V4L2 + ALSA), Companion zu OBS Studio"
|
||||||
|
arch=('x86_64')
|
||||||
|
url="https://git.projekt-hirnfrei.de/diabolus/hdmiviewer"
|
||||||
|
license=('MIT')
|
||||||
|
depends=('qt6-base' 'alsa-lib')
|
||||||
|
makedepends=('cmake')
|
||||||
|
source=("$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz")
|
||||||
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
|
build() {
|
||||||
|
# Der Ordnername im Archiv hängt von der Gitea-Instanz ab (meist
|
||||||
|
# "hdmiviewer", nicht versioniert) - daher robust per Glob statt fest
|
||||||
|
# verdrahtetem Pfad.
|
||||||
|
cd "$srcdir"/*/
|
||||||
|
cmake -B build -S . \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
cmake --build build
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$srcdir"/*/
|
||||||
|
DESTDIR="$pkgdir" cmake --install build
|
||||||
|
}
|
||||||
125
README.md
125
README.md
@@ -1,56 +1,87 @@
|
|||||||
# HDMI Viewer
|
# HDMI Viewer
|
||||||
|
|
||||||
Ein schlanker Qt6/C++ HDMI-Viewer für Arch Linux mit V4L2 (Video) und ALSA (Audio).
|
Ein schlanker Qt6/C++ HDMI-Viewer für Arch Linux mit V4L2 (Video) und ALSA (Audio).
|
||||||
Gedacht als Companion zu OBS Studio: **Anzeigen ohne Aufnehmen**.
|
Gedacht als Companion zu OBS Studio: **Anzeigen ohne Aufnehmen**.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Live-Vorschau vom HDMI-Grabber via V4L2 (YUYV und NV12)
|
- Live-Vorschau vom HDMI-Grabber via V4L2, mit automatischer Formatpriorität
|
||||||
- ALSA-Audio direkt vom Grabber-Gerät
|
MJPEG → YUYV → NV12 (MJPEG braucht deutlich weniger USB-Bandbreite und
|
||||||
- Gerät-Auswahl beim Start, **Audio-Gerät wird gespeichert**
|
erlaubt so höhere Auflösungen/Framerates)
|
||||||
|
- ALSA-Audio direkt vom Grabber-Gerät (Capture → Wiedergabe, per Thread)
|
||||||
|
- Gerät-Auswahl beim Start (Video, Audio, Ziel-Framerate), alle Einstellungen
|
||||||
|
werden gespeichert und beim nächsten Start vorausgewählt
|
||||||
- **F** → Vollbild / **F** oder **Esc** im Vollbild → Fenstermodus
|
- **F** → Vollbild / **F** oder **Esc** im Vollbild → Fenstermodus
|
||||||
- **Esc** im Fenstermodus → zurück zur Gerät-Auswahl (Device wird sauber freigegeben)
|
- **Esc** im Fenstermodus → zurück zur Gerät-Auswahl (Device wird sauber freigegeben)
|
||||||
- Seitenverhältnis immer bewahrt (Letterbox/Pillarbox)
|
- Seitenverhältnis immer bewahrt (Letterbox/Pillarbox)
|
||||||
- Titelleiste zeigt tatsächliche Auflösung und FPS
|
- Titelleiste und Statusleiste zeigen tatsächliche Auflösung, FPS und Pixelformat
|
||||||
- Colorspace-Konvertierung im ThreadPool (kein Ruckeln)
|
- GPU-Rendering per OpenGL 3.3 (kein Ruckeln durch CPU-seitiges Skalieren)
|
||||||
- FPS explizit auf 60fps gesetzt (was Grabber liefert wird in Statusbar angezeigt)
|
- **Bildglättung** (standardmäßig an, Taste **S**): GPU-Shader trennt Luma/Chroma
|
||||||
- **Bildglättung** (standardmäßig an): GPU-Shader trennt Luma/Chroma und
|
und behandelt sie getrennt – Chroma wird stärker weichgezeichnet (killt
|
||||||
behandelt sie getrennt – Chroma wird stärker weichgezeichnet (killt
|
Dot-Crawl/Farbrauschen billiger Composite→HDMI-Scaler), Luma wird leicht
|
||||||
Dot-Crawl/Farbrauschen billiger Composite→HDMI-Scaler wie Lioncast Mini
|
geglättet und dezent gegengeschärft (nimmt harte Scaler-Kanten raus, ohne
|
||||||
AV2HDMI o.ä.), Luma wird leicht geglättet und dezent gegengeschärft
|
matschig zu wirken). Keine Optik-Spielerei, sondern eine echte Bildverbesserung.
|
||||||
(nimmt harte Scaler-Kanten raus, ohne matschig zu wirken). Keine Optik-
|
- **Retro-Optik** (standardmäßig aus, Taste **C**): separater Nostalgie-Look –
|
||||||
Spielerei, sondern eine echte Bildverbesserung. Taste **S** zum Umschalten.
|
Scanlines, Phosphor-/Aperture-Grille-Maske, leichte Wölbung wie bei einem
|
||||||
- **Retro-Optik** (standardmäßig aus): separater Nostalgie-Look – Scanlines,
|
alten Röhrenfernseher. Lässt sich mit der Bildglättung kombinieren.
|
||||||
Phosphor-/Aperture-Grille-Maske, leichte Wölbung wie bei einem alten
|
- **Nachschärfen** (Unsharp Mask, Taste **X** zum Umschalten, **+**/**-** zum
|
||||||
Röhrenfernseher. Lässt sich mit der Bildglättung kombinieren. Taste **C**.
|
Fein-Justieren): unabhängig von der Bildglättung, live einstellbar und
|
||||||
- **Helligkeit** (Tasten **,** dunkler / **.** heller): Gamma-Anpassung, live
|
persistiert.
|
||||||
und persistiert. Sinnvoll als Feinjustierung, falls Farbraum-Erkennung
|
- **Hardware-Helligkeit/-Kontrast/-Sättigung** (Tasten **,**/**.**, **1**/**2**,
|
||||||
(siehe unten) allein nicht perfekt zum eigenen Bildschirm passt.
|
**3**/**4**): steuert die entsprechenden V4L2-Regler *direkt am
|
||||||
- **Hardware-Helligkeit**: Beim Öffnen des Geräts wird (falls vom Chip
|
Grabber-Chip*, also VOR der JPEG-Kompression – das rettet tatsächlich
|
||||||
unterstützt) der V4L2-Brightness-Regler direkt am Grabber moderat
|
Schattendetails, statt nur nachträglich am fertig komprimierten Bild zu
|
||||||
angehoben (+20 % des Wertebereichs ab Werkseinstellung) – das wirkt
|
strecken (was sichtbare Artefakte/Banding erzeugt). Beim Start wird der
|
||||||
bereits VOR der JPEG-Kompression und kann so tatsächlich verlorene
|
jeweilige Ausgangswert des Chips ausgelesen und gemerkt; beim Beenden wird
|
||||||
Schattendetails retten, statt nur nachträglich zu strecken, was von der
|
er exakt wiederhergestellt, sodass der Grabber für andere Programme
|
||||||
Kompression übrig blieb. Diagnose-Ausgabe der verfügbaren Regler landet
|
unverändert bleibt. Im Startdialog lassen sich für alle drei Regler feste
|
||||||
beim Start im Terminal.
|
Zielwerte vorgeben (`unverändert` = Regler beim Start gar nicht anfassen).
|
||||||
- **Studio-/Full-Range-Korrektur**: Das fertig dekodierte Bild wird von einem
|
- **Studio-/Full-Range-Korrektur**: Das fertig dekodierte Bild wird von einem
|
||||||
komprimierten Kontrastumfang (Schwarzpunkt niedrig angesetzt, Weißpunkt
|
komprimierten Kontrastumfang (Schwarzpunkt niedrig angesetzt, Weißpunkt
|
||||||
≈235/255) auf den vollen 0–255-Kontrastumfang gedehnt. Ohne diese
|
≈235/255) auf den vollen 0–255-Kontrastumfang gedehnt. Ohne diese Korrektur
|
||||||
Korrektur wirkt das Bild dumpf, kontrastarm und zu dunkel – viele billige
|
wirkt das Bild dumpf, kontrastarm und zu dunkel – viele billige HDMI-Grabber
|
||||||
HDMI-Grabber liefern (oder kodieren, auch bei MJPEG) genau in diesem
|
liefern (oder kodieren, auch bei MJPEG) genau in diesem komprimierten
|
||||||
komprimierten Bereich. Greift direkt im GPU-Shader auf dem fertigen
|
Bereich. Greift direkt im GPU-Shader auf dem fertigen RGB-Bild, also
|
||||||
RGB-Bild, also unabhängig vom Pixelformat.
|
unabhängig vom Pixelformat.
|
||||||
|
- **Roh-Modus** (Taste **R**): Diagnose-Umschalter, der sämtliche Verarbeitung
|
||||||
|
(Bildglättung, Retro-Optik, Studio-Range-Korrektur, Kontrast-/
|
||||||
|
Sättigungs-Punch, Gamma) umgeht und das Bild 1:1 anzeigt.
|
||||||
|
- **Live-Vergleich** (Taste **V**): zeigt links das rohe und rechts das
|
||||||
|
bearbeitete Bild nebeneinander – praktisch zum Fein-Tunen der Hardware-Regler.
|
||||||
|
- **Screenshot** (Taste **P**): speichert das aktuell angezeigte (bearbeitete)
|
||||||
|
Frame als PNG im Home-Verzeichnis.
|
||||||
|
- **Frame-Dump** (Taste **D**, nur bei MJPEG): speichert das nächste rohe,
|
||||||
|
unveränderte MJPEG-Frame direkt vom Chip als JPEG – nützlich, um zu prüfen,
|
||||||
|
ob Abweichungen schon in den Rohdaten liegen oder erst bei der eigenen
|
||||||
|
Verarbeitung entstehen.
|
||||||
|
|
||||||
## Abhängigkeiten (Arch Linux)
|
## Abhängigkeiten (Arch Linux)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo pacman -S qt6-base cmake alsa-lib linux-headers base-devel
|
sudo pacman -S qt6-base cmake alsa-lib base-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
`qt6-base` bringt auf Arch bereits die benötigten `Qt6OpenGL`- und
|
`qt6-base` bringt auf Arch bereits die benötigten `Qt6OpenGL`- und
|
||||||
`Qt6OpenGLWidgets`-Module mit.
|
`Qt6OpenGLWidgets`-Module mit. `linux/videodev2.h` ist Teil von
|
||||||
|
`linux-api-headers`, das über die `base`-Gruppe ohnehin installiert ist.
|
||||||
|
|
||||||
## Bauen
|
## Installation über AUR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yay -S hdmi-viewer
|
||||||
|
# oder mit einem anderen AUR-Helper, bzw. manuell:
|
||||||
|
git clone https://aur.archlinux.org/hdmi-viewer.git
|
||||||
|
cd hdmi-viewer
|
||||||
|
makepkg -si
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quellcode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.projekt-hirnfrei.de/diabolus/hdmiviewer.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manuell bauen
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@@ -66,17 +97,25 @@ make -j$(nproc)
|
|||||||
|
|
||||||
## Tastenkürzel
|
## Tastenkürzel
|
||||||
|
|
||||||
| Taste | Funktion |
|
| Taste | Funktion |
|
||||||
|--------------|---------------------------------------------|
|
|---------|--------------------------------------------------------|
|
||||||
| F | Vollbild ein/aus |
|
| F | Vollbild ein/aus |
|
||||||
| S | Bildglättung ein/aus |
|
| S | Bildglättung ein/aus |
|
||||||
| C | Retro-Optik (Scanlines/Maske) ein/aus |
|
| C | Retro-Optik (Scanlines/Maske) ein/aus |
|
||||||
| , / . | Dunkler / Heller (Helligkeit, gespeichert) |
|
| X | Nachschärfen ein/aus (letzter Wert ↔ aus) |
|
||||||
| Esc | Vollbild verlassen ODER zurück zum Dialog |
|
| + / - | Nachschärfen stärker / schwächer |
|
||||||
|
| , / . | Hardware-Helligkeit dunkler / heller |
|
||||||
|
| 1 / 2 | Hardware-Kontrast runter / hoch |
|
||||||
|
| 3 / 4 | Hardware-Sättigung runter / hoch |
|
||||||
|
| R | Roh-Modus (Diagnose, alle Effekte umgangen) ein/aus |
|
||||||
|
| V | Live-Vergleich (links roh, rechts bearbeitet) ein/aus |
|
||||||
|
| P | Screenshot speichern (PNG) |
|
||||||
|
| D | Rohes MJPEG-Frame dumpen (Diagnose) |
|
||||||
|
| Esc | Vollbild verlassen ODER zurück zum Gerätedialog |
|
||||||
|
|
||||||
## Einstellungen
|
## Einstellungen
|
||||||
|
|
||||||
Werden gespeichert in: `~/.config/hdmi-viewer/HDMIViewer.conf`
|
Werden gespeichert in: `~/.config/hdmi-viewer/HDMIViewer.conf`
|
||||||
(Standard XDG-Pfad, kein manuelles Anlegen nötig)
|
(Standard XDG-Pfad, kein manuelles Anlegen nötig)
|
||||||
|
|
||||||
## Audio-Gerät finden
|
## Audio-Gerät finden
|
||||||
@@ -90,5 +129,5 @@ arecord -D hw:2,0 -f S16_LE -r 48000 -c 2 /tmp/test.wav
|
|||||||
aplay /tmp/test.wav
|
aplay /tmp/test.wav
|
||||||
```
|
```
|
||||||
|
|
||||||
Das Gerät dann im Dialog so eingeben: z.B. `hw:2,0` oder `plughw:2,0`
|
Das Gerät dann im Dialog so eingeben: z.B. `hw:2,0` oder `plughw:2,0`
|
||||||
(plughw macht automatische Sample-Rate-Konvertierung, falls nötig)
|
(plughw macht automatische Sample-Rate-Konvertierung, falls nötig)
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ void CaptureThread::restoreHardwareControls()
|
|||||||
qDebug().noquote() << "Hardware-Regler auf Ausgangswerte zurückgesetzt.";
|
qDebug().noquote() << "Hardware-Regler auf Ausgangswerte zurückgesetzt.";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Für Live-Tasten (Kontrast/Sättigung): liest den aktuellen Wert, addiert
|
// Für Live-Tasten (Helligkeit/Kontrast/Sättigung): liest den aktuellen Wert, addiert
|
||||||
// delta, schreibt zurück, merkt sich den neuen Wert dauerhaft und meldet
|
// delta, schreibt zurück, merkt sich den neuen Wert dauerhaft und meldet
|
||||||
// ihn per Signal (für die Einblendung in der UI).
|
// ihn per Signal (für die Einblendung in der UI).
|
||||||
void CaptureThread::adjustHardwareControl(quint32 id, const char* settingsKey,
|
void CaptureThread::adjustHardwareControl(quint32 id, const char* settingsKey,
|
||||||
@@ -422,6 +422,11 @@ void CaptureThread::adjustHardwareControl(quint32 id, const char* settingsKey,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CaptureThread::adjustHardwareBrightness(int delta)
|
||||||
|
{
|
||||||
|
adjustHardwareControl(V4L2_CID_BRIGHTNESS, "hardware/brightness", "Hardware-Helligkeit", delta);
|
||||||
|
}
|
||||||
|
|
||||||
void CaptureThread::adjustHardwareContrast(int delta)
|
void CaptureThread::adjustHardwareContrast(int delta)
|
||||||
{
|
{
|
||||||
adjustHardwareControl(V4L2_CID_CONTRAST, "hardware/contrast", "Hardware-Kontrast", delta);
|
adjustHardwareControl(V4L2_CID_CONTRAST, "hardware/contrast", "Hardware-Kontrast", delta);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public:
|
|||||||
|
|
||||||
void stopAndWait();
|
void stopAndWait();
|
||||||
void requestFrameDump(); // speichert das nächste rohe MJPEG-Frame unverändert auf die Platte
|
void requestFrameDump(); // speichert das nächste rohe MJPEG-Frame unverändert auf die Platte
|
||||||
|
void adjustHardwareBrightness(int delta);
|
||||||
void adjustHardwareContrast(int delta);
|
void adjustHardwareContrast(int delta);
|
||||||
void adjustHardwareSaturation(int delta);
|
void adjustHardwareSaturation(int delta);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "DeviceDialog.h"
|
#include "DeviceDialog.h"
|
||||||
#include "VideoWidget.h" // für kBrightnessMin/Max, kSharpenMin/Max (eine Quelle der Wahrheit)
|
#include "VideoWidget.h" // für kSharpenMin/Max (eine Quelle der Wahrheit)
|
||||||
|
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
@@ -85,16 +85,17 @@ DeviceDialog::DeviceDialog(QWidget* parent)
|
|||||||
"0 = aus. Im laufenden Betrieb mit [X]/[+]/[-] änderbar.");
|
"0 = aus. Im laufenden Betrieb mit [X]/[+]/[-] änderbar.");
|
||||||
form->addRow("Nachschärfen:", m_sharpenSpin);
|
form->addRow("Nachschärfen:", m_sharpenSpin);
|
||||||
|
|
||||||
m_brightnessSpin = new QDoubleSpinBox(this);
|
m_hwBrightnessSpin = new QSpinBox(this);
|
||||||
m_brightnessSpin->setRange(VideoWidget::kBrightnessMin * 100.0, VideoWidget::kBrightnessMax * 100.0);
|
m_hwBrightnessSpin->setRange(-1, 100);
|
||||||
m_brightnessSpin->setSingleStep(VideoWidget::kBrightnessStep * 100.0);
|
m_hwBrightnessSpin->setValue(-1);
|
||||||
m_brightnessSpin->setDecimals(0);
|
m_hwBrightnessSpin->setSpecialValueText("unverändert");
|
||||||
m_brightnessSpin->setSuffix(" %");
|
m_hwBrightnessSpin->setToolTip(
|
||||||
m_brightnessSpin->setValue(100.0);
|
"Hardware-Helligkeit direkt am Grabber-Chip, VOR der JPEG-Kompression\n"
|
||||||
m_brightnessSpin->setToolTip(
|
"(Wertebereich hängt vom jeweiligen Chip ab, hier 0-100 angenommen).\n"
|
||||||
"Gamma-Helligkeit, 100 % = unverändert. Im laufenden Betrieb\n"
|
"\"unverändert\" lässt den aktuellen Chip-Wert einfach in Ruhe.\n"
|
||||||
"mit [,]/[.] änderbar.");
|
"Im laufenden Betrieb mit [,]/[.] änderbar. hdmi-viewer merkt sich\n"
|
||||||
form->addRow("Helligkeit:", m_brightnessSpin);
|
"beim Start den Ausgangswert und stellt ihn beim Beenden wieder her.");
|
||||||
|
form->addRow("Hardware-Helligkeit:", m_hwBrightnessSpin);
|
||||||
|
|
||||||
m_hwContrastSpin = new QSpinBox(this);
|
m_hwContrastSpin = new QSpinBox(this);
|
||||||
m_hwContrastSpin->setRange(-1, 100);
|
m_hwContrastSpin->setRange(-1, 100);
|
||||||
@@ -125,7 +126,7 @@ DeviceDialog::DeviceDialog(QWidget* parent)
|
|||||||
"Titelleiste angezeigt.<br>"
|
"Titelleiste angezeigt.<br>"
|
||||||
"<b>F</b> Vollbild · <b>S</b> Glättung · "
|
"<b>F</b> Vollbild · <b>S</b> Glättung · "
|
||||||
"<b>C</b> Retro · <b>X/+/-</b> Schärfen · "
|
"<b>C</b> Retro · <b>X/+/-</b> Schärfen · "
|
||||||
"<b>,/.</b> Helligkeit · <b>1/2</b> HW-Kontrast · "
|
"<b>,/.</b> HW-Helligkeit · <b>1/2</b> HW-Kontrast · "
|
||||||
"<b>3/4</b> HW-Sättigung · <b>V</b> Vergleich · "
|
"<b>3/4</b> HW-Sättigung · <b>V</b> Vergleich · "
|
||||||
"<b>P</b> Screenshot · <b>Esc</b> zurück hierher</small>",
|
"<b>P</b> Screenshot · <b>Esc</b> zurück hierher</small>",
|
||||||
this);
|
this);
|
||||||
@@ -156,7 +157,7 @@ int DeviceDialog::selectedFps() const { return m_fpsCombo->currentDa
|
|||||||
bool DeviceDialog::selectedSmoothMode() const { return m_smoothCheck->isChecked(); }
|
bool DeviceDialog::selectedSmoothMode() const { return m_smoothCheck->isChecked(); }
|
||||||
bool DeviceDialog::selectedRetroMode() const { return m_retroCheck->isChecked(); }
|
bool DeviceDialog::selectedRetroMode() const { return m_retroCheck->isChecked(); }
|
||||||
float DeviceDialog::selectedSharpenAmount() const { return static_cast<float>(m_sharpenSpin->value()); }
|
float DeviceDialog::selectedSharpenAmount() const { return static_cast<float>(m_sharpenSpin->value()); }
|
||||||
float DeviceDialog::selectedBrightness() const { return static_cast<float>(m_brightnessSpin->value() / 100.0); }
|
int DeviceDialog::selectedHwBrightness() const { return m_hwBrightnessSpin->value(); }
|
||||||
int DeviceDialog::selectedHwContrast() const { return m_hwContrastSpin->value(); }
|
int DeviceDialog::selectedHwContrast() const { return m_hwContrastSpin->value(); }
|
||||||
int DeviceDialog::selectedHwSaturation() const { return m_hwSaturationSpin->value(); }
|
int DeviceDialog::selectedHwSaturation() const { return m_hwSaturationSpin->value(); }
|
||||||
|
|
||||||
@@ -166,7 +167,7 @@ void DeviceDialog::setPreselectedFps(int fps) { selectByData(m_fpsCombo, QString
|
|||||||
void DeviceDialog::setPreselectedSmoothMode(bool enabled) { m_smoothCheck->setChecked(enabled); }
|
void DeviceDialog::setPreselectedSmoothMode(bool enabled) { m_smoothCheck->setChecked(enabled); }
|
||||||
void DeviceDialog::setPreselectedRetroMode(bool enabled) { m_retroCheck->setChecked(enabled); }
|
void DeviceDialog::setPreselectedRetroMode(bool enabled) { m_retroCheck->setChecked(enabled); }
|
||||||
void DeviceDialog::setPreselectedSharpenAmount(float amount) { m_sharpenSpin->setValue(amount); }
|
void DeviceDialog::setPreselectedSharpenAmount(float amount) { m_sharpenSpin->setValue(amount); }
|
||||||
void DeviceDialog::setPreselectedBrightness(float gamma) { m_brightnessSpin->setValue(gamma * 100.0); }
|
void DeviceDialog::setPreselectedHwBrightness(int value) { m_hwBrightnessSpin->setValue(value); }
|
||||||
void DeviceDialog::setPreselectedHwContrast(int value) { m_hwContrastSpin->setValue(value); }
|
void DeviceDialog::setPreselectedHwContrast(int value) { m_hwContrastSpin->setValue(value); }
|
||||||
void DeviceDialog::setPreselectedHwSaturation(int value) { m_hwSaturationSpin->setValue(value); }
|
void DeviceDialog::setPreselectedHwSaturation(int value) { m_hwSaturationSpin->setValue(value); }
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public:
|
|||||||
bool selectedSmoothMode() const;
|
bool selectedSmoothMode() const;
|
||||||
bool selectedRetroMode() const;
|
bool selectedRetroMode() const;
|
||||||
float selectedSharpenAmount() const;
|
float selectedSharpenAmount() const;
|
||||||
float selectedBrightness() const;
|
int selectedHwBrightness() const; // -1 = unverändert lassen
|
||||||
int selectedHwContrast() const; // -1 = unverändert lassen
|
int selectedHwContrast() const; // -1 = unverändert lassen
|
||||||
int selectedHwSaturation() const; // -1 = unverändert lassen
|
int selectedHwSaturation() const; // -1 = unverändert lassen
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ public:
|
|||||||
void setPreselectedSmoothMode(bool enabled);
|
void setPreselectedSmoothMode(bool enabled);
|
||||||
void setPreselectedRetroMode(bool enabled);
|
void setPreselectedRetroMode(bool enabled);
|
||||||
void setPreselectedSharpenAmount(float amount);
|
void setPreselectedSharpenAmount(float amount);
|
||||||
void setPreselectedBrightness(float gamma);
|
void setPreselectedHwBrightness(int value); // -1 = unverändert
|
||||||
void setPreselectedHwContrast(int value); // -1 = unverändert
|
void setPreselectedHwContrast(int value); // -1 = unverändert
|
||||||
void setPreselectedHwSaturation(int value); // -1 = unverändert
|
void setPreselectedHwSaturation(int value); // -1 = unverändert
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ private:
|
|||||||
QComboBox* m_fpsCombo = nullptr;
|
QComboBox* m_fpsCombo = nullptr;
|
||||||
QCheckBox* m_smoothCheck = nullptr;
|
QCheckBox* m_smoothCheck = nullptr;
|
||||||
QCheckBox* m_retroCheck = nullptr;
|
QCheckBox* m_retroCheck = nullptr;
|
||||||
QDoubleSpinBox* m_sharpenSpin = nullptr;
|
QDoubleSpinBox* m_sharpenSpin = nullptr;
|
||||||
QDoubleSpinBox* m_brightnessSpin = nullptr;
|
QSpinBox* m_hwBrightnessSpin = nullptr;
|
||||||
QSpinBox* m_hwContrastSpin = nullptr;
|
QSpinBox* m_hwContrastSpin = nullptr;
|
||||||
QSpinBox* m_hwSaturationSpin = nullptr;
|
QSpinBox* m_hwSaturationSpin = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ MainWindow::MainWindow(const QString& videoDevice,
|
|||||||
int targetFps,
|
int targetFps,
|
||||||
bool smoothModeEnabled,
|
bool smoothModeEnabled,
|
||||||
bool retroModeEnabled,
|
bool retroModeEnabled,
|
||||||
float brightness,
|
|
||||||
float sharpenAmount,
|
float sharpenAmount,
|
||||||
float lastSharpenAmount,
|
float lastSharpenAmount,
|
||||||
QWidget* parent)
|
QWidget* parent)
|
||||||
@@ -41,7 +40,6 @@ MainWindow::MainWindow(const QString& videoDevice,
|
|||||||
m_video->setSmoothModeEnabled(smoothModeEnabled);
|
m_video->setSmoothModeEnabled(smoothModeEnabled);
|
||||||
m_video->setRetroModeEnabled(retroModeEnabled);
|
m_video->setRetroModeEnabled(retroModeEnabled);
|
||||||
m_video->setSharpenAmount(sharpenAmount);
|
m_video->setSharpenAmount(sharpenAmount);
|
||||||
m_video->setBrightness(brightness);
|
|
||||||
setCentralWidget(m_video);
|
setCentralWidget(m_video);
|
||||||
|
|
||||||
// Kurzzeitige Einblendung unten links (funktioniert auch im Vollbild,
|
// Kurzzeitige Einblendung unten links (funktioniert auch im Vollbild,
|
||||||
@@ -118,11 +116,11 @@ void MainWindow::setupMenuBar()
|
|||||||
|
|
||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
QAction* brighterAct = viewMenu->addAction("Heller\t.");
|
QAction* brighterAct = viewMenu->addAction("Heller (Hardware)\t.");
|
||||||
connect(brighterAct, &QAction::triggered, this, &MainWindow::increaseBrightness);
|
connect(brighterAct, &QAction::triggered, this, &MainWindow::increaseHwBrightness);
|
||||||
|
|
||||||
QAction* darkerAct = viewMenu->addAction("Dunkler\t,");
|
QAction* darkerAct = viewMenu->addAction("Dunkler (Hardware)\t,");
|
||||||
connect(darkerAct, &QAction::triggered, this, &MainWindow::decreaseBrightness);
|
connect(darkerAct, &QAction::triggered, this, &MainWindow::decreaseHwBrightness);
|
||||||
|
|
||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
@@ -230,14 +228,20 @@ void MainWindow::applySharpen(float value)
|
|||||||
showOverlayMessage(QString("Nachschärfen: %1").arg(value, 0, 'f', 1));
|
showOverlayMessage(QString("Nachschärfen: %1").arg(value, 0, 'f', 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::increaseBrightness()
|
void MainWindow::increaseHwBrightness()
|
||||||
{
|
{
|
||||||
applyBrightness(m_video->brightness() + VideoWidget::kBrightnessStep);
|
if (m_capture)
|
||||||
|
{
|
||||||
|
m_capture->adjustHardwareBrightness(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::decreaseBrightness()
|
void MainWindow::decreaseHwBrightness()
|
||||||
{
|
{
|
||||||
applyBrightness(m_video->brightness() - VideoWidget::kBrightnessStep);
|
if (m_capture)
|
||||||
|
{
|
||||||
|
m_capture->adjustHardwareBrightness(-5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::toggleRaw()
|
void MainWindow::toggleRaw()
|
||||||
@@ -279,19 +283,6 @@ void MainWindow::saveScreenshot()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::applyBrightness(float value)
|
|
||||||
{
|
|
||||||
value = qBound(VideoWidget::kBrightnessMin, value, VideoWidget::kBrightnessMax);
|
|
||||||
m_video->setBrightness(value);
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
settings.setValue("display/brightness", static_cast<double>(value));
|
|
||||||
|
|
||||||
statusBar()->showMessage(
|
|
||||||
QString("Helligkeit: %1 %").arg(qRound(value * 100.0f)), 2000);
|
|
||||||
showOverlayMessage(QString("Helligkeit: %1 %").arg(qRound(value * 100.0f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::showAbout()
|
void MainWindow::showAbout()
|
||||||
{
|
{
|
||||||
auto* dlg = new QDialog(this);
|
auto* dlg = new QDialog(this);
|
||||||
@@ -345,7 +336,7 @@ void MainWindow::showAbout()
|
|||||||
grid->addWidget(v, row, 1);
|
grid->addWidget(v, row, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
addRow(0, "Version", "1.4.0");
|
addRow(0, "Version", "1.0");
|
||||||
addRow(1, "Entwickler", "Dany Thinnes");
|
addRow(1, "Entwickler", "Dany Thinnes");
|
||||||
addRow(2, "Projekt", "Projekt Hirnfrei");
|
addRow(2, "Projekt", "Projekt Hirnfrei");
|
||||||
addRow(3, "Framework", QString("Qt %1").arg(QT_VERSION_STR));
|
addRow(3, "Framework", QString("Qt %1").arg(QT_VERSION_STR));
|
||||||
@@ -365,7 +356,7 @@ void MainWindow::showAbout()
|
|||||||
"<b>S</b> Glättung "
|
"<b>S</b> Glättung "
|
||||||
"<b>C</b> Retro "
|
"<b>C</b> Retro "
|
||||||
"<b>X</b>/<b>+</b>/<b>-</b> Schärfen "
|
"<b>X</b>/<b>+</b>/<b>-</b> Schärfen "
|
||||||
"<b>,</b>/<b>.</b> Helligkeit<br>"
|
"<b>,</b>/<b>.</b> HW-Helligkeit<br>"
|
||||||
"<b>1</b>/<b>2</b> HW-Kontrast "
|
"<b>1</b>/<b>2</b> HW-Kontrast "
|
||||||
"<b>3</b>/<b>4</b> HW-Sättigung "
|
"<b>3</b>/<b>4</b> HW-Sättigung "
|
||||||
"<b>R</b> Roh-Diagnose<br>"
|
"<b>R</b> Roh-Diagnose<br>"
|
||||||
@@ -442,10 +433,10 @@ void MainWindow::keyPressEvent(QKeyEvent* event)
|
|||||||
decreaseSharpen();
|
decreaseSharpen();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Period:
|
case Qt::Key_Period:
|
||||||
increaseBrightness();
|
increaseHwBrightness();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Comma:
|
case Qt::Key_Comma:
|
||||||
decreaseBrightness();
|
decreaseHwBrightness();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_R:
|
case Qt::Key_R:
|
||||||
toggleRaw();
|
toggleRaw();
|
||||||
@@ -503,7 +494,7 @@ void MainWindow::updateStatusBar()
|
|||||||
info = QString("%1×%2 @ %3fps %4 ")
|
info = QString("%1×%2 @ %3fps %4 ")
|
||||||
.arg(m_capWidth).arg(m_capHeight).arg(m_capFps).arg(m_capFmt);
|
.arg(m_capWidth).arg(m_capHeight).arg(m_capFps).arg(m_capFmt);
|
||||||
|
|
||||||
info += QString("Video: %1 Audio: %2 [F] Vollbild [S] Glättung [C] Retro [X/+/-] Schärfen [,/.] Helligkeit [1/2] HW-Kontrast [3/4] HW-Sättigung [R] Roh [V] Vergleich [P] Screenshot [D] Frame dumpen [Esc] Einstellungen")
|
info += QString("Video: %1 Audio: %2 [F] Vollbild [S] Glättung [C] Retro [X/+/-] Schärfen [,/.] HW-Helligkeit [1/2] HW-Kontrast [3/4] HW-Sättigung [R] Roh [V] Vergleich [P] Screenshot [D] Frame dumpen [Esc] Einstellungen")
|
||||||
.arg(m_videoDevice,
|
.arg(m_videoDevice,
|
||||||
m_audioDevice.isEmpty() ? QStringLiteral("–") : m_audioDevice);
|
m_audioDevice.isEmpty() ? QStringLiteral("–") : m_audioDevice);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ public:
|
|||||||
int targetFps,
|
int targetFps,
|
||||||
bool smoothModeEnabled = true,
|
bool smoothModeEnabled = true,
|
||||||
bool retroModeEnabled = false,
|
bool retroModeEnabled = false,
|
||||||
float brightness = 1.0f,
|
|
||||||
float sharpenAmount = 0.0f,
|
float sharpenAmount = 0.0f,
|
||||||
float lastSharpenAmount = 2.5f,
|
float lastSharpenAmount = 2.5f,
|
||||||
QWidget* parent = nullptr);
|
QWidget* parent = nullptr);
|
||||||
@@ -44,8 +43,8 @@ private slots:
|
|||||||
void toggleSharpen();
|
void toggleSharpen();
|
||||||
void increaseSharpen();
|
void increaseSharpen();
|
||||||
void decreaseSharpen();
|
void decreaseSharpen();
|
||||||
void increaseBrightness();
|
void increaseHwBrightness();
|
||||||
void decreaseBrightness();
|
void decreaseHwBrightness();
|
||||||
void toggleRaw();
|
void toggleRaw();
|
||||||
void toggleCompare();
|
void toggleCompare();
|
||||||
void saveScreenshot();
|
void saveScreenshot();
|
||||||
@@ -55,7 +54,6 @@ private:
|
|||||||
void toggleFullscreen();
|
void toggleFullscreen();
|
||||||
void stopAll();
|
void stopAll();
|
||||||
void updateStatusBar();
|
void updateStatusBar();
|
||||||
void applyBrightness(float value);
|
|
||||||
void applySharpen(float value);
|
void applySharpen(float value);
|
||||||
void showOverlayMessage(const QString& text);
|
void showOverlayMessage(const QString& text);
|
||||||
|
|
||||||
|
|||||||
13
src/main.cpp
13
src/main.cpp
@@ -18,7 +18,7 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
app.setApplicationName("HDMIViewer");
|
app.setApplicationName("HDMIViewer");
|
||||||
app.setApplicationVersion("1.4.0");
|
app.setApplicationVersion("1.0");
|
||||||
app.setOrganizationName("hdmi-viewer");
|
app.setOrganizationName("hdmi-viewer");
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
@@ -31,7 +31,7 @@ int main(int argc, char* argv[])
|
|||||||
dlg.setPreselectedSmoothMode(settings.value("display/smooth", true).toBool());
|
dlg.setPreselectedSmoothMode(settings.value("display/smooth", true).toBool());
|
||||||
dlg.setPreselectedRetroMode(settings.value("display/retro", false).toBool());
|
dlg.setPreselectedRetroMode(settings.value("display/retro", false).toBool());
|
||||||
dlg.setPreselectedSharpenAmount(settings.value("display/sharpen", 0.0).toFloat());
|
dlg.setPreselectedSharpenAmount(settings.value("display/sharpen", 0.0).toFloat());
|
||||||
dlg.setPreselectedBrightness(settings.value("display/brightness", 1.0).toFloat());
|
dlg.setPreselectedHwBrightness(settings.value("hardware/brightness", -1).toInt());
|
||||||
dlg.setPreselectedHwContrast(settings.value("hardware/contrast", -1).toInt());
|
dlg.setPreselectedHwContrast(settings.value("hardware/contrast", -1).toInt());
|
||||||
dlg.setPreselectedHwSaturation(settings.value("hardware/saturation", -1).toInt());
|
dlg.setPreselectedHwSaturation(settings.value("hardware/saturation", -1).toInt());
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ int main(int argc, char* argv[])
|
|||||||
const bool smoothMode = dlg.selectedSmoothMode();
|
const bool smoothMode = dlg.selectedSmoothMode();
|
||||||
const bool retroMode = dlg.selectedRetroMode();
|
const bool retroMode = dlg.selectedRetroMode();
|
||||||
const float sharpenAmt = dlg.selectedSharpenAmount();
|
const float sharpenAmt = dlg.selectedSharpenAmount();
|
||||||
const float brightness = dlg.selectedBrightness();
|
const int hwBrightness = dlg.selectedHwBrightness();
|
||||||
const int hwContrast = dlg.selectedHwContrast();
|
const int hwContrast = dlg.selectedHwContrast();
|
||||||
const int hwSaturation = dlg.selectedHwSaturation();
|
const int hwSaturation = dlg.selectedHwSaturation();
|
||||||
// Für die Taste [X] (Quick-Toggle 0 ↔ letzter Wert) zur Laufzeit:
|
// Für die Taste [X] (Quick-Toggle 0 ↔ letzter Wert) zur Laufzeit:
|
||||||
@@ -66,11 +66,14 @@ int main(int argc, char* argv[])
|
|||||||
settings.setValue("display/smooth", smoothMode);
|
settings.setValue("display/smooth", smoothMode);
|
||||||
settings.setValue("display/retro", retroMode);
|
settings.setValue("display/retro", retroMode);
|
||||||
settings.setValue("display/sharpen", static_cast<double>(sharpenAmt));
|
settings.setValue("display/sharpen", static_cast<double>(sharpenAmt));
|
||||||
settings.setValue("display/brightness", static_cast<double>(brightness));
|
|
||||||
if (sharpenAmt > 0.0f)
|
if (sharpenAmt > 0.0f)
|
||||||
settings.setValue("display/sharpen_last", static_cast<double>(sharpenAmt));
|
settings.setValue("display/sharpen_last", static_cast<double>(sharpenAmt));
|
||||||
// -1 = "unverändert" -> kein expliziter Nutzerwert, Regler bleibt
|
// -1 = "unverändert" -> kein expliziter Nutzerwert, Regler bleibt
|
||||||
// beim Öffnen unangetastet (siehe CaptureThread::applyHardwareControls)
|
// beim Öffnen unangetastet (siehe CaptureThread::applyHardwareControls)
|
||||||
|
if (hwBrightness >= 0)
|
||||||
|
settings.setValue("hardware/brightness", hwBrightness);
|
||||||
|
else
|
||||||
|
settings.remove("hardware/brightness");
|
||||||
if (hwContrast >= 0)
|
if (hwContrast >= 0)
|
||||||
settings.setValue("hardware/contrast", hwContrast);
|
settings.setValue("hardware/contrast", hwContrast);
|
||||||
else
|
else
|
||||||
@@ -81,7 +84,7 @@ int main(int argc, char* argv[])
|
|||||||
settings.remove("hardware/saturation");
|
settings.remove("hardware/saturation");
|
||||||
settings.sync();
|
settings.sync();
|
||||||
|
|
||||||
MainWindow win(videoDevice, audioDevice, fps, smoothMode, retroMode, brightness, sharpenAmt, lastSharpen);
|
MainWindow win(videoDevice, audioDevice, fps, smoothMode, retroMode, sharpenAmt, lastSharpen);
|
||||||
win.show();
|
win.show();
|
||||||
app.exec();
|
app.exec();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user