Browse Source

Add QP ceiling and adaptation controls to the broadcast window

stream-multiview
evgeny 6 days ago
parent
commit
00698537ad
  1. 28
      tools/chatgui/src/stream_publish_window.cpp
  2. 7
      tools/chatgui/translations/chatgui_en.ts

28
tools/chatgui/src/stream_publish_window.cpp

@ -6,6 +6,7 @@
#include <QHBoxLayout>
#include <QFormLayout>
#include <QComboBox>
#include <QSpinBox>
#include <QCheckBox>
#include <QLabel>
#include <QMessageBox>
@ -86,6 +87,15 @@ StreamPublishWindow::StreamPublishWindow(StreamPublishEngine* engine, uint64_t g
form->addRow(text("Microphone"), m_microphone);
m_inputChannels = new QLabel(settings); form->addRow(text("Audio input"), m_inputChannels);
m_mute = new QCheckBox(text("Mute stream audio"), settings); form->addRow(m_mute);
m_maxQp = new QSpinBox(settings); m_maxQp->setObjectName(QStringLiteral("streamMaxQp"));
m_maxQp->setRange(0, 51); m_maxQp->setSpecialValueText(text("Automatic")); m_maxQp->setValue(28);
m_maxQp->setToolTip(text("Lower QP preserves more detail and may require a higher bitrate. Applies to H.264 and H.265."));
m_adaptiveQp = new QCheckBox(text("Adapt QP to the target bitrate"), settings);
m_adaptiveQp->setObjectName(QStringLiteral("streamAdaptiveQp"));
m_adaptiveQp->setToolTip(text("Gradually raise the QP ceiling above the selected value when bitrate is too high, then return it. Requires live QP adjustment."));
int bitrateRow = 0; QFormLayout::ItemRole bitrateRole;
form->getWidgetPosition(m_bitrate, &bitrateRow, &bitrateRole);
form->insertRow(bitrateRow + 1, text("Maximum QP"), m_maxQp); form->insertRow(bitrateRow + 2, m_adaptiveQp);
connect(m_mute, &QCheckBox::toggled, engine, &StreamPublishEngine::setMuted);
m_status = new QLabel(this); m_status->setWordWrap(true); layout->addWidget(m_status);
m_stats = new QLabel(this); m_stats->setObjectName(QStringLiteral("streamPublishStats"));
@ -102,9 +112,12 @@ StreamPublishWindow::StreamPublishWindow(StreamPublishEngine* engine, uint64_t g
if (!m_engine->active()) synchronize();
});
for (auto* box : {m_mode, m_codec, m_bitrate}) connect(box, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &StreamPublishWindow::apply);
connect(m_codec, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this] { if (!m_updating && !m_engine->active()) synchronize(); });
connect(m_microphone, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &StreamPublishWindow::apply);
connect(m_audio, &QCheckBox::toggled, this, &StreamPublishWindow::apply);
connect(m_cursor, &QCheckBox::toggled, this, &StreamPublishWindow::apply);
connect(m_maxQp, QOverload<int>::of(&QSpinBox::valueChanged), this, [this] { apply(); if (!m_engine->active()) synchronize(); });
connect(m_adaptiveQp, &QCheckBox::toggled, this, &StreamPublishWindow::apply);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(m_rotation, &QButtonGroup::idClicked, this, &StreamPublishWindow::apply);
#else
@ -143,6 +156,7 @@ StreamPublishWindow::StreamPublishWindow(StreamPublishEngine* engine, uint64_t g
connect(engine, &StreamPublishEngine::audioChannelsChanged, this, [this](int channels) { m_inputChannels->setText(channels == 2 ? text("Stereo") : text("Mono")); });
connect(engine, &StreamPublishEngine::telemetryChanged, this, [this](const stream_publisher_status& stats) {
m_stats->setText(text("Current video bitrate: %1 Mbit/s").arg(m_engine->videoBitrate() / 1000000.0, 0, 'f', 2) + '\n' +
(m_engine->videoMaxQp() >= 0 ? text("Current QP ceiling: %1").arg(m_engine->videoMaxQp()) + '\n' : QString()) +
text("Video queue: %1 · Inflight: %2 · Send buffer: %3\nACK, all connections, last second: %4")
.arg(bytes(stats.video_bytes), bytes(stats.inflight_bytes), bytes(stats.total_bytes), bytes(stats.acked_1s)));
});
@ -176,6 +190,9 @@ void StreamPublishWindow::restoreSettings() {
if (bitrate > 0 && m_bitrate->findData(bitrate) < 0)
m_bitrate->addItem(QStringLiteral("%1 Mbit/s").arg(bitrate / 1000000.0, 0, 'f', 2), bitrate);
select(m_bitrate, bitrate, "bitrate");
int maxQp = m_settings.value("max_qp").toInt(28);
if (maxQp < 0 || maxQp > 51) DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop saved QP ceiling invalid value=%d", maxQp);
m_maxQp->setValue(std::clamp(maxQp, 0, 51)); m_adaptiveQp->setChecked(m_settings.value("adaptive_qp").toBool());
if (auto* button = m_rotation->button(m_settings.value("rotation").toInt())) button->setChecked(true);
else DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop saved capture rotation invalid");
m_audio->setChecked(m_settings.value("audio").toBool(true));
@ -201,6 +218,7 @@ void StreamPublishWindow::saveSettings() {
saved["bitrate"] = m_bitrate->currentData().toInt(); saved["rotation"] = config.video.rotation;
saved["audio"] = config.audio; saved["microphone"] = config.microphone;
saved["show_cursor"] = config.showCursor;
saved["max_qp"] = m_maxQp->value(); saved["adaptive_qp"] = m_adaptiveQp->isChecked();
if (saved == m_settings) return;
m_settings = saved;
auto json = QJsonDocument(saved).toJson(QJsonDocument::Compact);
@ -291,6 +309,10 @@ StreamPublishConfig StreamPublishWindow::selection() const {
config.video.bitrate = m_bitrate->currentData().toInt();
config.automaticBitrate = !config.video.bitrate;
if (config.automaticBitrate) config.video.bitrate = std::clamp(config.video.width * config.video.height * config.video.fps / 11, 250000, 16000000);
if (config.video.codec == 1 || config.video.codec == 2) {
config.video.maxQp = m_maxQp->value() ? m_maxQp->value() : -1;
config.adaptiveQp = config.video.maxQp >= 0 && m_adaptiveQp->isChecked();
}
config.showCursor = m_cursor->isChecked();
config.microphone = m_microphone->currentData().toString(); config.audio = m_audio->isChecked(); return config;
}
@ -320,11 +342,17 @@ void StreamPublishWindow::synchronize() {
bitrateIndex = m_bitrate->count() - 1;
}
m_bitrate->setCurrentIndex(bitrateIndex);
if (config.video.codec == 1 || config.video.codec == 2) {
m_maxQp->setValue(config.video.maxQp < 0 ? 0 : config.video.maxQp); m_adaptiveQp->setChecked(config.adaptiveQp);
}
if (auto* button = m_rotation->button(config.video.rotation)) button->setChecked(true);
m_audio->setChecked(config.audio); m_microphone->setCurrentIndex(m_microphone->findData(config.microphone));
m_cursor->setChecked(config.showCursor);
}
m_cursor->setVisible(StreamPublishEngine::cursorSupported(selection().source));
bool qpSupported = m_codec->currentData().toInt() == 1 || m_codec->currentData().toInt() == 2;
m_maxQp->setEnabled(qpSupported && (!active || m_engine->running()));
m_adaptiveQp->setEnabled(m_maxQp->isEnabled() && m_maxQp->value() > 0);
bool portal = selection().source.id == "portal";
m_cursor->setEnabled(!active || (m_engine->running() && !portal));
m_cursor->setToolTip(portal ? text("Change cursor visibility before starting screen sharing") : QString());

7
tools/chatgui/translations/chatgui_en.ts

@ -3,6 +3,13 @@
<TS version="2.1" language="en_US" sourcelanguage="en_US">
<context>
<name>ChatGui</name>
<message><source>Maximum QP</source><translation>Maximum QP</translation></message>
<message><source>Adapt QP to the target bitrate</source><translation>Adapt QP to the target bitrate</translation></message>
<message><source>Current QP ceiling: %1</source><translation>Current QP ceiling: %1</translation></message>
<message><source>Lower QP preserves more detail and may require a higher bitrate. Applies to H.264 and H.265.</source><translation>Lower QP preserves more detail and may require a higher bitrate. Applies to H.264 and H.265.</translation></message>
<message><source>Gradually raise the QP ceiling above the selected value when bitrate is too high, then return it. Requires live QP adjustment.</source><translation>Gradually raise the QP ceiling above the selected value when bitrate is too high, then return it. Requires live QP adjustment.</translation></message>
<message><source>Select a maximum QP before enabling adaptation</source><translation>Select a maximum QP before enabling adaptation</translation></message>
<message><source>QP adaptation is unavailable for this encoder. Disable adaptation or choose another codec.</source><translation>QP adaptation is unavailable for this encoder. Disable adaptation or choose another codec.</translation></message>
<message><source>Local network</source><translation>Local network</translation></message>
<message><source>Receive invitations from the local network</source><translation>Receive invitations from the local network</translation></message>
<message><source>Broadcast presence on the local network</source><translation>Broadcast presence on the local network</translation></message>

Loading…
Cancel
Save