Erster Commit
This commit is contained in:
148
src/DeviceDialog.cpp
Normal file
148
src/DeviceDialog.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "DeviceDialog.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
static int xioctl(int fd, unsigned long req, void* arg)
|
||||
{
|
||||
int r;
|
||||
do { r = ioctl(fd, req, arg); } while (r == -1 && errno == EINTR);
|
||||
return r;
|
||||
}
|
||||
|
||||
DeviceDialog::DeviceDialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle("HDMI Viewer – Gerät auswählen");
|
||||
setMinimumWidth(520);
|
||||
|
||||
auto* form = new QFormLayout;
|
||||
m_videoCombo = new QComboBox(this);
|
||||
m_audioCombo = new QComboBox(this);
|
||||
m_fpsCombo = new QComboBox(this);
|
||||
|
||||
m_videoCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
m_audioCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
populateVideoDevices();
|
||||
populateAudioDevices();
|
||||
|
||||
// FPS-Optionen: gängige Werte für HDMI-Grabber
|
||||
for (int fps : {10, 15, 20, 24, 25, 30, 50, 60})
|
||||
m_fpsCombo->addItem(QString::number(fps) + " fps", fps);
|
||||
// Default: 30
|
||||
setPreselectedFps(30);
|
||||
|
||||
form->addRow("Video-Gerät (V4L2):", m_videoCombo);
|
||||
form->addRow("Audio-Gerät (ALSA):", m_audioCombo);
|
||||
form->addRow("Ziel-Framerate:", m_fpsCombo);
|
||||
|
||||
auto* hint = new QLabel(
|
||||
"<small><b>Hinweis:</b> Der Grabber liefert die gewünschte FPS-Zahl nur wenn "
|
||||
"genug USB-Bandbreite vorhanden ist. Die tatsächliche Rate wird in der "
|
||||
"Titelleiste angezeigt.<br>"
|
||||
"<b>F</b> = Vollbild · <b>Esc</b> = zurück hierher</small>",
|
||||
this);
|
||||
hint->setWordWrap(true);
|
||||
|
||||
auto* btnBox = new QHBoxLayout;
|
||||
auto* btnOk = new QPushButton("Starten", this);
|
||||
auto* btnClose = new QPushButton("Beenden", this);
|
||||
btnOk->setDefault(true);
|
||||
btnBox->addStretch();
|
||||
btnBox->addWidget(btnClose);
|
||||
btnBox->addWidget(btnOk);
|
||||
|
||||
connect(btnOk, &QPushButton::clicked, this, &QDialog::accept);
|
||||
connect(btnClose, &QPushButton::clicked, this, &QDialog::reject);
|
||||
|
||||
auto* main = new QVBoxLayout(this);
|
||||
main->addLayout(form);
|
||||
main->addSpacing(8);
|
||||
main->addWidget(hint);
|
||||
main->addSpacing(12);
|
||||
main->addLayout(btnBox);
|
||||
}
|
||||
|
||||
QString DeviceDialog::selectedVideoDevice() const { return m_videoCombo->currentData().toString(); }
|
||||
QString DeviceDialog::selectedAudioDevice() const { return m_audioCombo->currentData().toString(); }
|
||||
int DeviceDialog::selectedFps() const { return m_fpsCombo->currentData().toInt(); }
|
||||
|
||||
void DeviceDialog::setPreselectedVideo(const QString& device) { selectByData(m_videoCombo, device); }
|
||||
void DeviceDialog::setPreselectedAudio(const QString& device) { selectByData(m_audioCombo, device); }
|
||||
void DeviceDialog::setPreselectedFps(int fps) { selectByData(m_fpsCombo, QString::number(fps)); }
|
||||
|
||||
void DeviceDialog::selectByData(QComboBox* combo, const QString& data)
|
||||
{
|
||||
for (int i = 0; i < combo->count(); ++i)
|
||||
if (combo->itemData(i).toString() == data) { combo->setCurrentIndex(i); return; }
|
||||
}
|
||||
|
||||
void DeviceDialog::populateVideoDevices()
|
||||
{
|
||||
QDir dev("/dev");
|
||||
const QStringList entries = dev.entryList({"video*"}, QDir::System, QDir::Name);
|
||||
|
||||
for (const QString& entry : entries) {
|
||||
const QString path = "/dev/" + entry;
|
||||
int fd = ::open(path.toLocal8Bit().constData(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1) continue;
|
||||
|
||||
v4l2_capability cap{};
|
||||
QString label = path;
|
||||
if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
|
||||
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { ::close(fd); continue; }
|
||||
const QString card = QString::fromLatin1(
|
||||
reinterpret_cast<const char*>(cap.card), 32).trimmed();
|
||||
if (!card.isEmpty())
|
||||
label = QString("%1 [%2]").arg(path, card);
|
||||
}
|
||||
::close(fd);
|
||||
m_videoCombo->addItem(label, path);
|
||||
}
|
||||
|
||||
if (m_videoCombo->count() == 0)
|
||||
m_videoCombo->addItem("Kein V4L2-Gerät gefunden", "");
|
||||
}
|
||||
|
||||
void DeviceDialog::populateAudioDevices()
|
||||
{
|
||||
m_audioCombo->addItem("default [System-Standard]", "default");
|
||||
|
||||
void** hints = nullptr;
|
||||
if (snd_device_name_hint(-1, "pcm", &hints) < 0) return;
|
||||
|
||||
for (void** hint = hints; *hint != nullptr; ++hint) {
|
||||
char* ioid = snd_device_name_get_hint(*hint, "IOID");
|
||||
const bool isCapture = (ioid == nullptr || strcmp(ioid, "Input") == 0);
|
||||
free(ioid);
|
||||
if (!isCapture) continue;
|
||||
|
||||
char* name = snd_device_name_get_hint(*hint, "NAME");
|
||||
char* desc = snd_device_name_get_hint(*hint, "DESC");
|
||||
|
||||
if (name) {
|
||||
const QString n = QString::fromLatin1(name);
|
||||
const QString d = desc ? QString::fromLatin1(desc).replace('\n', ' ') : n;
|
||||
if (n != "default")
|
||||
m_audioCombo->addItem(QString("%1 [%2]").arg(n, d.left(60)), n);
|
||||
free(name);
|
||||
}
|
||||
if (desc) free(desc);
|
||||
}
|
||||
snd_device_name_free_hint(hints);
|
||||
}
|
||||
Reference in New Issue
Block a user