66#include < util/config-file.h>
77
88#include < QFormLayout>
9- #include < QGridLayout>
109#include < QVBoxLayout>
11- #include < QGroupBox>
1210#include < QLineEdit>
1311#include < QPushButton>
1412#include < QLabel>
13+ #include < QFont>
1514#include < QTimer>
1615#include < QDir>
1716#include < QFileInfo>
@@ -72,27 +71,6 @@ std::string SettingsPath()
7271 return s;
7372}
7473
75- QString FormatDuration (int seconds)
76- {
77- int h = seconds / 3600 ;
78- int m = (seconds % 3600 ) / 60 ;
79- int s = seconds % 60 ;
80- return QString::asprintf (" %02d:%02d:%02d" , h, m, s);
81- }
82-
83- // Add a "name: value" row to the stats grid and return the (right-aligned) value label.
84- QLabel *AddStatRow (QGridLayout *grid, int row, const QString &name)
85- {
86- auto *nameLabel = new QLabel (name);
87- nameLabel->setStyleSheet (" color: palette(mid);" );
88- auto *valueLabel = new QLabel (" —" );
89- valueLabel->setAlignment (Qt::AlignRight | Qt::AlignVCenter);
90- valueLabel->setTextInteractionFlags (Qt::TextSelectableByMouse);
91- grid->addWidget (nameLabel, row, 0 );
92- grid->addWidget (valueLabel, row, 1 );
93- return valueLabel;
94- }
95-
9674} // namespace
9775
9876MoQDock::MoQDock (QWidget *parent) : QWidget(parent)
@@ -105,48 +83,40 @@ MoQDock::MoQDock(QWidget *parent) : QWidget(parent)
10583 pathEdit->setText (" obs" );
10684 pathEdit->setPlaceholderText (" (optional) broadcast name" );
10785
108- // Labels above the fields (WrapAllRows) so the inputs get the full width.
86+ // Labels above the fields (WrapAllRows), and let the fields grow to the full
87+ // dock width (the macOS default keeps them at their size hint otherwise).
10988 auto *form = new QFormLayout ();
11089 form->setRowWrapPolicy (QFormLayout::WrapAllRows);
90+ form->setFieldGrowthPolicy (QFormLayout::AllNonFixedFieldsGrow);
11191 form->setContentsMargins (0 , 0 , 0 , 0 );
11292 form->addRow (" Relay URL" , urlEdit);
113- form->addRow (" Broadcast path " , pathEdit);
93+ form->addRow (" Broadcast name " , pathEdit);
11494
11595 button = new QPushButton (" Go Live" , this );
11696 button->setCursor (Qt::PointingHandCursor);
11797 connect (button, &QPushButton::clicked, this , &MoQDock::ToggleStream);
11898
119- status = new QLabel (" Idle " , this );
99+ status = new QLabel (this );
120100 status->setWordWrap (true );
121- status->setStyleSheet (" color: palette(mid);" );
122-
123- auto *statsBox = new QGroupBox (" Statistics" , this );
124- auto *grid = new QGridLayout (statsBox);
125- grid->setColumnStretch (1 , 1 );
126- grid->setVerticalSpacing (4 );
127- statState = AddStatRow (grid, 0 , " Status" );
128- statDuration = AddStatRow (grid, 1 , " Duration" );
129- statBitrate = AddStatRow (grid, 2 , " Bitrate" );
130- statSent = AddStatRow (grid, 3 , " Data sent" );
131- statDropped = AddStatRow (grid, 4 , " Dropped frames" );
132- statConnect = AddStatRow (grid, 5 , " Connect time" );
101+ QFont statusFont = status->font ();
102+ statusFont.setBold (true );
103+ status->setFont (statusFont);
133104
134105 auto *versionLabel = new QLabel (QString (" libmoq %1" ).arg (MOQ_VERSION_STRING ), this );
135106 versionLabel->setAlignment (Qt::AlignRight | Qt::AlignBottom);
136- versionLabel->setStyleSheet (" color: palette(mid) ; font-size: 10px;" );
107+ versionLabel->setStyleSheet (" color: #888888 ; font-size: 10px;" );
137108
138109 auto *layout = new QVBoxLayout (this );
139110 layout->setSpacing (10 );
140111 layout->addLayout (form);
141112 layout->addWidget (button);
142113 layout->addWidget (status);
143- layout->addWidget (statsBox);
144114 layout->addStretch ();
145115 layout->addWidget (versionLabel);
146116
147- statsTimer = new QTimer (this );
148- statsTimer ->setInterval (1000 );
149- connect (statsTimer , &QTimer::timeout, this , &MoQDock::UpdateStats );
117+ pollTimer = new QTimer (this );
118+ pollTimer ->setInterval (1000 );
119+ connect (pollTimer , &QTimer::timeout, this , &MoQDock::UpdateStatus );
150120
151121 connect (urlEdit, &QLineEdit::editingFinished, this , &MoQDock::SaveSettings);
152122 connect (pathEdit, &QLineEdit::editingFinished, this , &MoQDock::SaveSettings);
@@ -301,18 +271,16 @@ void MoQDock::StartStream()
301271 return ;
302272 }
303273
304- lastBytes = 0 ;
305- lastSample = std::chrono::steady_clock::now ();
306- streamStart = lastSample;
307- statsTimer->start ();
274+ pollTimer->start ();
308275
309276 SetRunning (true );
310- status->setText (" Connecting…" );
277+ status->setText (" ● Connecting…" );
278+ status->setStyleSheet (" color: #d08b1d;" );
311279}
312280
313281void MoQDock::StopStream ()
314282{
315- statsTimer ->stop ();
283+ pollTimer ->stop ();
316284
317285 if (output) {
318286 signal_handler_disconnect (obs_output_get_signal_handler (output), " stop" , OnOutputStopped, this );
@@ -342,48 +310,22 @@ void MoQDock::SetRunning(bool isRunning)
342310 pathEdit->setEnabled (!isRunning);
343311
344312 if (!isRunning) {
345- status->setText (" Idle" );
346- statState->setText (" Offline" );
347- statState->setStyleSheet (" color: palette(mid);" );
348- statDuration->setText (" —" );
349- statBitrate->setText (" —" );
350- statSent->setText (" —" );
351- statDropped->setText (" —" );
352- statConnect->setText (" —" );
313+ status->setText (" ● Disconnected" );
314+ status->setStyleSheet (" color: #888888;" );
353315 }
354316}
355317
356- void MoQDock::UpdateStats ()
318+ void MoQDock::UpdateStatus ()
357319{
358320 if (!output || !running)
359321 return ;
360322
361- const auto now = std::chrono::steady_clock::now ();
362- const uint64_t bytes = obs_output_get_total_bytes (output);
363- const double secs = std::chrono::duration<double >(now - lastSample).count ();
364- const double kbps = secs > 0.0 ? (double )(bytes - lastBytes) * 8.0 / 1000.0 / secs : 0.0 ;
365- lastBytes = bytes;
366- lastSample = now;
367-
368- const bool connected = obs_output_active (output) && bytes > 0 ;
369- statState->setText (connected ? " ● Live" : " Connecting…" );
370- statState->setStyleSheet (connected ? " color: #36a45e; font-weight: bold;" : " color: palette(mid);" );
371-
372- const int liveSecs = (int )std::chrono::duration_cast<std::chrono::seconds>(now - streamStart).count ();
373- statDuration->setText (FormatDuration (liveSecs));
374- statBitrate->setText (QString (" %1 kb/s" ).arg ((int )(kbps + 0.5 )));
375- statSent->setText (QString (" %1 MB" ).arg ((double )bytes / (1024.0 * 1024.0 ), 0 , ' f' , 1 ));
376-
377- const int total = obs_output_get_total_frames (output);
378- const int dropped = obs_output_get_frames_dropped (output);
379- const double dropPct = total > 0 ? (double )dropped * 100.0 / (double )total : 0.0 ;
380- statDropped->setText (QString (" %1 (%2%)" ).arg (dropped).arg (dropPct, 0 , ' f' , 1 ));
381-
382- const int connectMs = obs_output_get_connect_time_ms (output);
383- statConnect->setText (connectMs > 0 ? QString (" %1 ms" ).arg (connectMs) : " —" );
384-
385- if (connected)
386- status->setText (" Streaming" );
323+ // libmoq surfaces connection state via the session-connect callback, which
324+ // MoQOutput records as the output's connect time; until that fires we're
325+ // still connecting. There's no per-frame stats API to show beyond this.
326+ const bool connected = obs_output_get_connect_time_ms (output) > 0 ;
327+ status->setText (connected ? " ● Connected" : " ● Connecting…" );
328+ status->setStyleSheet (connected ? " color: #36a45e;" : " color: #d08b1d;" );
387329}
388330
389331void MoQDock::LoadSettings ()
0 commit comments