@ -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 \n ACK, 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 ( ) ) ;