Share radio microphone controls with desktop publication

This commit is contained in:
evgeny committed 2026-11-01 21:38:01 +01:00
1 parent b2bd2c284c
commit fbc030dc5b
2 files changed
+40 -8

No files matched your search

+39 -8
View File
@@ -2,6 +2,7 @@
#include "videoplayer_window.h"
#include "capture_controls.h"
#include "sound_manager.h"
#include "../transport/gui_bridge.h"
#include "debug_config.h"
#include <QVBoxLayout>
#include <QCoreApplication>
@@ -157,7 +158,9 @@ StreamPublishWindow::StreamPublishWindow(StreamPublishEngine* engine, uint64_t g
m_cursor->setObjectName(QStringLiteral("streamShowCursor")); form->addRow(m_cursor);
m_noAudio = addOption(m_microphone, text("No audio"), QVariant());
addOption(m_microphone, text("Default microphone"), QString()); select(m_microphone, QString());
for (const auto& device : SoundManager::enumerateCaptureDevices()) addOption(m_microphone, device.name, device.id);
refreshMicrophones();
connect(m_microphone, &QMenu::aboutToShow, this, &StreamPublishWindow::refreshMicrophones);
connect(SoundManager::instance(), &SoundManager::devicesChanged, this, [this] { refreshMicrophones(); synchronize(); });
m_inputChannels = new QLabel(settings); form->addRow(text("Audio input"), m_inputChannels);
for (int qp : {0, 18, 23, 28, 35, 40}) addOption(m_maxQp, qp ? QString::number(qp) : text("Automatic"), qp);
select(m_maxQp, 28);
@@ -190,8 +193,23 @@ StreamPublishWindow::StreamPublishWindow(StreamPublishEngine* engine, uint64_t g
connect(m_resolution, &QMenu::triggered, this, [this] {
populateFrameRates(m_fps, m_modes, value(m_resolution).toSize(), value(m_fps).toInt()); apply();
});
for (auto* menu : {m_fps, m_codec, m_bitrate, m_maxQp, m_rotation, m_microphone})
for (auto* menu : {m_fps, m_codec, m_bitrate, m_maxQp, m_rotation})
connect(menu, &QMenu::triggered, this, &StreamPublishWindow::apply);
connect(m_microphone, &QMenu::triggered, this, [this](QAction* action) {
if (m_updating || !action->isCheckable()) return;
if (action != m_noAudio) {
auto* sound = SoundManager::instance();
const QString id = action->data().toString();
if (id != sound->captureDevice()) {
if (gui_bridge_post_setting(GUI_SETTING_UI_STATE, "audio_input_id", id.toUtf8().constData()) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop microphone selection could not be saved input=%s", qPrintable(id));
m_status->setText(text("Failed to select device")); refreshMicrophones(); synchronize(); return;
}
sound->setDevices(sound->playbackDevice(), id);
}
}
synchronize(); saveSettings();
});
for (auto* menu : {m_codec, m_maxQp}) connect(menu, &QMenu::triggered, this, [this] { if (!m_updating) synchronize(); });
connect(m_cursor, &QCheckBox::toggled, this, &StreamPublishWindow::apply);
connect(m_adaptiveQp, &QCheckBox::toggled, this, &StreamPublishWindow::apply);
@@ -283,8 +301,7 @@ void StreamPublishWindow::restoreSettings() {
restore(m_maxQp, maxQp, "max_qp"); m_adaptiveQp->setChecked(m_settings.value("adaptive_qp").toBool());
restore(m_rotation, m_settings.value("rotation").toInt(), "rotation");
m_cursor->setChecked(m_settings.value("show_cursor").toBool(true));
if (m_settings.value("audio").toBool(true)) restore(m_microphone, m_settings.value("microphone").toString(), "microphone");
else m_noAudio->setChecked(true);
if (!m_settings.value("audio").toBool(true)) m_noAudio->setChecked(true);
m_updating = false;
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop capture settings restored: %s", QJsonDocument(m_settings).toJson(QJsonDocument::Compact).constData());
}
@@ -303,7 +320,7 @@ void StreamPublishWindow::saveSettings() {
}
if (selected(m_codec)) saved["codec"] = config.video.codec;
saved["bitrate"] = value(m_bitrate).toInt(); saved["rotation"] = config.video.rotation;
saved["audio"] = config.audio; saved["microphone"] = config.microphone;
saved["audio"] = config.audio; saved.remove("microphone");
saved["muted"] = m_audioMuted;
saved["show_cursor"] = config.showCursor;
saved["max_qp"] = value(m_maxQp).toInt(); saved["adaptive_qp"] = m_adaptiveQp->isChecked();
@@ -379,6 +396,19 @@ void StreamPublishWindow::refreshModes(const StreamPublishConfig& previous, bool
source.kind == StreamVideoSource::Screen && mode.size == m_modes.front().size);
}
void StreamPublishWindow::refreshMicrophones() {
QScopedValueRollback<bool> updating(m_updating, true);
const bool audio = m_engine->active() ? m_engine->config().audio : selected(m_microphone) != m_noAudio;
const QString input = SoundManager::instance()->captureDevice();
clearOptions(m_microphone, "Audio source");
m_noAudio = addOption(m_microphone, text("No audio"), QVariant());
m_noAudio->setEnabled(!m_engine->active());
addOption(m_microphone, text("Default microphone"), QString());
for (const auto& device : SoundManager::enumerateCaptureDevices()) addOption(m_microphone, device.name, device.id);
if (!option(m_microphone, input)) addOption(m_microphone, text("Selected microphone unavailable"), input);
if (audio) select(m_microphone, input); else m_noAudio->setChecked(true);
}
void StreamPublishWindow::refreshCodecs() {
QScopedValueRollback<bool> updating(m_updating, true);
int previous = selected(m_codec) ? value(m_codec).toInt() : m_settings.value("codec").toInt();
@@ -405,7 +435,7 @@ StreamPublishConfig StreamPublishWindow::selection() const {
config.adaptiveQp = config.video.maxQp >= 0 && m_adaptiveQp->isChecked();
}
config.showCursor = m_cursor->isChecked();
config.microphone = value(m_microphone).toString(); config.audio = selected(m_microphone) != m_noAudio;
config.audio = selected(m_microphone) != m_noAudio;
config.startMuted = m_audioMuted; return config;
}
void StreamPublishWindow::apply() {
@@ -422,7 +452,8 @@ void StreamPublishWindow::synchronize() {
&& selected(m_source) && selected(m_resolution) && selected(m_fps) && selected(m_codec)));
for (auto* menu : {m_source, m_resolution, m_fps, m_codec, m_bitrate, m_rotation})
menu->menuAction()->setEnabled(!active || m_engine->running());
m_microphone->menuAction()->setEnabled(!active);
m_microphone->menuAction()->setEnabled(!active || (m_engine->running() && m_engine->config().audio));
m_noAudio->setEnabled(!active);
if (active) m_audioMuted = m_engine->muted();
bool audio = active ? m_engine->config().audio : selection().audio;
m_sound->setEnabled(audio && (!active || m_engine->running()));
@@ -443,7 +474,7 @@ void StreamPublishWindow::synchronize() {
m_adaptiveQp->setChecked(config.adaptiveQp);
}
select(m_rotation, config.video.rotation);
if (config.audio) select(m_microphone, config.microphone); else m_noAudio->setChecked(true);
if (config.audio) select(m_microphone, SoundManager::instance()->captureDevice()); else m_noAudio->setChecked(true);
m_cursor->setChecked(config.showCursor);
}
m_cursor->setVisible(StreamPublishEngine::cursorSupported(selection().source));
@@ -51,6 +51,7 @@ private:
void refreshModes(const StreamPublishConfig& previous, bool maximum = false);
void updateMenuButtons(); // Обновляет иконки со значениями и подсказки верхнего ряда после выбора/синхронизации.
void refreshCodecs(); // Перечитывает engine.codecs(), сохраняет текущий/saved codec, недоступный заменяет первым.
void refreshMicrophones(); // Перечитывает устройства и общий с рацией выбор; «Без звука» относится только к трансляции.
void synchronize(); // Читает engine config/state без apply; настраивает controls и ограничения изменения audio/cursor/QP.
void apply(); // Для выбранного режима: reconfigure только active-engine, затем сохранение выбора; не во время m_updating.
void restoreSettings(); // До start восстанавливает доступные значения JSON; некорректные/отсутствующие диагностируются и заменяются.