123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- #include <QLabel>
- #include <QHBoxLayout>
- #include <QPainter>
- #include <QPixmap>
- #include "obs-app.hpp"
- #include "window-basic-main.hpp"
- #include "window-basic-status-bar.hpp"
- #include "window-basic-main-outputs.hpp"
- OBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)
- : QStatusBar(parent),
- delayInfo(new QLabel),
- droppedFrames(new QLabel),
- streamIcon(new QLabel),
- streamTime(new QLabel),
- recordTime(new QLabel),
- recordIcon(new QLabel),
- cpuUsage(new QLabel),
- transparentPixmap(20, 20),
- greenPixmap(20, 20),
- grayPixmap(20, 20),
- redPixmap(20, 20),
- recordingActivePixmap(QIcon(":/res/images/recording-active.svg")
- .pixmap(QSize(20, 20))),
- recordingPausePixmap(QIcon(":/res/images/recording-pause.svg")
- .pixmap(QSize(20, 20))),
- recordingPauseInactivePixmap(
- QIcon(":/res/images/recording-pause-inactive.svg")
- .pixmap(QSize(20, 20))),
- recordingInactivePixmap(QIcon(":/res/images/recording-inactive.svg")
- .pixmap(QSize(20, 20))),
- streamingActivePixmap(QIcon(":/res/images/streaming-active.svg")
- .pixmap(QSize(20, 20))),
- streamingInactivePixmap(QIcon(":/res/images/streaming-inactive.svg")
- .pixmap(QSize(20, 20)))
- {
- streamTime->setText(QString("LIVE: 00:00:00"));
- recordTime->setText(QString("REC: 00:00:00"));
- cpuUsage->setText(QString("CPU: 0.0%, 0.00 fps"));
- streamIcon->setPixmap(streamingInactivePixmap);
- recordIcon->setPixmap(recordingInactivePixmap);
- QWidget *brWidget = new QWidget(this);
- QHBoxLayout *brLayout = new QHBoxLayout(brWidget);
- brLayout->setContentsMargins(0, 0, 0, 0);
- statusSquare = new QLabel(brWidget);
- brLayout->addWidget(statusSquare);
- kbps = new QLabel(brWidget);
- brLayout->addWidget(kbps);
- brWidget->setLayout(brLayout);
- delayInfo->setAlignment(Qt::AlignRight);
- delayInfo->setAlignment(Qt::AlignVCenter);
- droppedFrames->setAlignment(Qt::AlignRight);
- droppedFrames->setAlignment(Qt::AlignVCenter);
- streamIcon->setAlignment(Qt::AlignRight);
- streamIcon->setAlignment(Qt::AlignVCenter);
- streamTime->setAlignment(Qt::AlignRight);
- streamTime->setAlignment(Qt::AlignVCenter);
- recordIcon->setAlignment(Qt::AlignRight);
- recordIcon->setAlignment(Qt::AlignVCenter);
- recordTime->setAlignment(Qt::AlignRight);
- recordTime->setAlignment(Qt::AlignVCenter);
- cpuUsage->setAlignment(Qt::AlignRight);
- cpuUsage->setAlignment(Qt::AlignVCenter);
- kbps->setAlignment(Qt::AlignRight);
- kbps->setAlignment(Qt::AlignVCenter);
- delayInfo->setIndent(20);
- droppedFrames->setIndent(20);
- streamIcon->setIndent(20);
- recordIcon->setIndent(20);
- cpuUsage->setIndent(20);
- kbps->setIndent(10);
- addPermanentWidget(droppedFrames);
- addPermanentWidget(streamIcon);
- addPermanentWidget(streamTime);
- addPermanentWidget(recordIcon);
- addPermanentWidget(recordTime);
- addPermanentWidget(cpuUsage);
- addPermanentWidget(delayInfo);
- addPermanentWidget(brWidget);
- transparentPixmap.fill(QColor(0, 0, 0, 0));
- greenPixmap.fill(QColor(0, 255, 0));
- grayPixmap.fill(QColor(72, 72, 72));
- redPixmap.fill(QColor(255, 0, 0));
- statusSquare->setPixmap(transparentPixmap);
- }
- void OBSBasicStatusBar::Activate()
- {
- if (!active) {
- refreshTimer = new QTimer(this);
- connect(refreshTimer, SIGNAL(timeout()), this,
- SLOT(UpdateStatusBar()));
- int skipped = video_output_get_skipped_frames(obs_get_video());
- int total = video_output_get_total_frames(obs_get_video());
- totalStreamSeconds = 0;
- totalRecordSeconds = 0;
- lastSkippedFrameCount = 0;
- startSkippedFrameCount = skipped;
- startTotalFrameCount = total;
- refreshTimer->start(1000);
- active = true;
- if (streamOutput) {
- statusSquare->setPixmap(grayPixmap);
- }
- }
- if (streamOutput) {
- streamIcon->setPixmap(streamingActivePixmap);
- }
- if (recordOutput) {
- recordIcon->setPixmap(recordingActivePixmap);
- }
- }
- void OBSBasicStatusBar::Deactivate()
- {
- OBSBasic *main = qobject_cast<OBSBasic *>(parent());
- if (!main)
- return;
- if (!streamOutput) {
- streamTime->setText(QString("LIVE: 00:00:00"));
- streamIcon->setPixmap(streamingInactivePixmap);
- totalStreamSeconds = 0;
- }
- if (!recordOutput) {
- recordTime->setText(QString("REC: 00:00:00"));
- recordIcon->setPixmap(recordingInactivePixmap);
- totalRecordSeconds = 0;
- }
- if (main->outputHandler && !main->outputHandler->Active()) {
- delete refreshTimer;
- delayInfo->setText("");
- droppedFrames->setText("");
- kbps->setText("");
- delaySecTotal = 0;
- delaySecStarting = 0;
- delaySecStopping = 0;
- reconnectTimeout = 0;
- active = false;
- overloadedNotify = true;
- statusSquare->setPixmap(transparentPixmap);
- }
- }
- void OBSBasicStatusBar::UpdateDelayMsg()
- {
- QString msg;
- if (delaySecTotal) {
- if (delaySecStarting && !delaySecStopping) {
- msg = QTStr("Basic.StatusBar.DelayStartingIn");
- msg = msg.arg(QString::number(delaySecStarting));
- } else if (!delaySecStarting && delaySecStopping) {
- msg = QTStr("Basic.StatusBar.DelayStoppingIn");
- msg = msg.arg(QString::number(delaySecStopping));
- } else if (delaySecStarting && delaySecStopping) {
- msg = QTStr("Basic.StatusBar.DelayStartingStoppingIn");
- msg = msg.arg(QString::number(delaySecStopping),
- QString::number(delaySecStarting));
- } else {
- msg = QTStr("Basic.StatusBar.Delay");
- msg = msg.arg(QString::number(delaySecTotal));
- }
- }
- delayInfo->setText(msg);
- }
- #define BITRATE_UPDATE_SECONDS 2
- void OBSBasicStatusBar::UpdateBandwidth()
- {
- if (!streamOutput)
- return;
- if (++bitrateUpdateSeconds < BITRATE_UPDATE_SECONDS)
- return;
- uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
- uint64_t bytesSentTime = os_gettime_ns();
- if (bytesSent < lastBytesSent)
- bytesSent = 0;
- if (bytesSent == 0)
- lastBytesSent = 0;
- uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;
- double timePassed =
- double(bytesSentTime - lastBytesSentTime) / 1000000000.0;
- double kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;
- QString text;
- text += QString("kb/s: ") + QString::number(kbitsPerSec, 'f', 0);
- kbps->setText(text);
- kbps->setMinimumWidth(kbps->width());
- lastBytesSent = bytesSent;
- lastBytesSentTime = bytesSentTime;
- bitrateUpdateSeconds = 0;
- }
- void OBSBasicStatusBar::UpdateCPUUsage()
- {
- OBSBasic *main = qobject_cast<OBSBasic *>(parent());
- if (!main)
- return;
- QString text;
- text += QString("CPU: ") +
- QString::number(main->GetCPUUsage(), 'f', 1) + QString("%, ") +
- QString::number(obs_get_active_fps(), 'f', 2) + QString(" fps");
- cpuUsage->setText(text);
- cpuUsage->setMinimumWidth(cpuUsage->width());
- }
- void OBSBasicStatusBar::UpdateStreamTime()
- {
- totalStreamSeconds++;
- int seconds = totalStreamSeconds % 60;
- int totalMinutes = totalStreamSeconds / 60;
- int minutes = totalMinutes % 60;
- int hours = totalMinutes / 60;
- QString text = QString::asprintf("LIVE: %02d:%02d:%02d", hours, minutes,
- seconds);
- streamTime->setText(text);
- streamTime->setMinimumWidth(streamTime->width());
- if (reconnectTimeout > 0) {
- QString msg = QTStr("Basic.StatusBar.Reconnecting")
- .arg(QString::number(retries),
- QString::number(reconnectTimeout));
- showMessage(msg);
- reconnectTimeout--;
- } else if (retries > 0) {
- QString msg = QTStr("Basic.StatusBar.AttemptingReconnect");
- showMessage(msg.arg(QString::number(retries)));
- }
- if (delaySecStopping > 0 || delaySecStarting > 0) {
- if (delaySecStopping > 0)
- --delaySecStopping;
- if (delaySecStarting > 0)
- --delaySecStarting;
- UpdateDelayMsg();
- }
- }
- extern volatile bool recording_paused;
- void OBSBasicStatusBar::UpdateRecordTime()
- {
- bool paused = os_atomic_load_bool(&recording_paused);
- if (!paused) {
- totalRecordSeconds++;
- int seconds = totalRecordSeconds % 60;
- int totalMinutes = totalRecordSeconds / 60;
- int minutes = totalMinutes % 60;
- int hours = totalMinutes / 60;
- QString text = QString::asprintf("REC: %02d:%02d:%02d", hours,
- minutes, seconds);
- recordTime->setText(text);
- recordTime->setMinimumWidth(recordTime->width());
- } else {
- recordIcon->setPixmap(streamPauseIconToggle
- ? recordingPauseInactivePixmap
- : recordingPausePixmap);
- streamPauseIconToggle = !streamPauseIconToggle;
- }
- }
- void OBSBasicStatusBar::UpdateDroppedFrames()
- {
- if (!streamOutput)
- return;
- int totalDropped = obs_output_get_frames_dropped(streamOutput);
- int totalFrames = obs_output_get_total_frames(streamOutput);
- double percent = (double)totalDropped / (double)totalFrames * 100.0;
- if (!totalFrames)
- return;
- QString text = QTStr("DroppedFrames");
- text = text.arg(QString::number(totalDropped),
- QString::number(percent, 'f', 1));
- droppedFrames->setText(text);
- droppedFrames->setMinimumWidth(droppedFrames->width());
- /* ----------------------------------- *
- * calculate congestion color */
- float congestion = obs_output_get_congestion(streamOutput);
- float avgCongestion = (congestion + lastCongestion) * 0.5f;
- if (avgCongestion < congestion)
- avgCongestion = congestion;
- if (avgCongestion > 1.0f)
- avgCongestion = 1.0f;
- if (avgCongestion < EPSILON) {
- statusSquare->setPixmap(greenPixmap);
- } else if (fabsf(avgCongestion - 1.0f) < EPSILON) {
- statusSquare->setPixmap(redPixmap);
- } else {
- QPixmap pixmap(20, 20);
- float red = avgCongestion * 2.0f;
- if (red > 1.0f)
- red = 1.0f;
- red *= 255.0;
- float green = (1.0f - avgCongestion) * 2.0f;
- if (green > 1.0f)
- green = 1.0f;
- green *= 255.0;
- pixmap.fill(QColor(int(red), int(green), 0));
- statusSquare->setPixmap(pixmap);
- }
- lastCongestion = congestion;
- }
- void OBSBasicStatusBar::OBSOutputReconnect(void *data, calldata_t *params)
- {
- OBSBasicStatusBar *statusBar =
- reinterpret_cast<OBSBasicStatusBar *>(data);
- int seconds = (int)calldata_int(params, "timeout_sec");
- QMetaObject::invokeMethod(statusBar, "Reconnect", Q_ARG(int, seconds));
- }
- void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *)
- {
- OBSBasicStatusBar *statusBar =
- reinterpret_cast<OBSBasicStatusBar *>(data);
- QMetaObject::invokeMethod(statusBar, "ReconnectSuccess");
- }
- void OBSBasicStatusBar::Reconnect(int seconds)
- {
- OBSBasic *main = qobject_cast<OBSBasic *>(parent());
- if (!retries)
- main->SysTrayNotify(
- QTStr("Basic.SystemTray.Message.Reconnecting"),
- QSystemTrayIcon::Warning);
- reconnectTimeout = seconds;
- if (streamOutput) {
- delaySecTotal = obs_output_get_active_delay(streamOutput);
- UpdateDelayMsg();
- retries++;
- }
- }
- void OBSBasicStatusBar::ReconnectClear()
- {
- retries = 0;
- reconnectTimeout = 0;
- bitrateUpdateSeconds = -1;
- lastBytesSent = 0;
- lastBytesSentTime = os_gettime_ns();
- delaySecTotal = 0;
- UpdateDelayMsg();
- }
- void OBSBasicStatusBar::ReconnectSuccess()
- {
- OBSBasic *main = qobject_cast<OBSBasic *>(parent());
- QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful");
- showMessage(msg, 4000);
- main->SysTrayNotify(msg, QSystemTrayIcon::Information);
- ReconnectClear();
- if (streamOutput) {
- delaySecTotal = obs_output_get_active_delay(streamOutput);
- UpdateDelayMsg();
- }
- }
- void OBSBasicStatusBar::UpdateStatusBar()
- {
- OBSBasic *main = qobject_cast<OBSBasic *>(parent());
- UpdateBandwidth();
- if (streamOutput)
- UpdateStreamTime();
- if (recordOutput)
- UpdateRecordTime();
- UpdateDroppedFrames();
- int skipped = video_output_get_skipped_frames(obs_get_video());
- int total = video_output_get_total_frames(obs_get_video());
- skipped -= startSkippedFrameCount;
- total -= startTotalFrameCount;
- int diff = skipped - lastSkippedFrameCount;
- double percentage = double(skipped) / double(total) * 100.0;
- if (diff > 10 && percentage >= 0.1f) {
- showMessage(QTStr("HighResourceUsage"), 4000);
- if (!main->isVisible() && overloadedNotify) {
- main->SysTrayNotify(QTStr("HighResourceUsage"),
- QSystemTrayIcon::Warning);
- overloadedNotify = false;
- }
- }
- lastSkippedFrameCount = skipped;
- }
- void OBSBasicStatusBar::StreamDelayStarting(int sec)
- {
- OBSBasic *main = qobject_cast<OBSBasic *>(parent());
- if (!main || !main->outputHandler)
- return;
- streamOutput = main->outputHandler->streamOutput;
- delaySecTotal = delaySecStarting = sec;
- UpdateDelayMsg();
- Activate();
- }
- void OBSBasicStatusBar::StreamDelayStopping(int sec)
- {
- delaySecTotal = delaySecStopping = sec;
- UpdateDelayMsg();
- }
- void OBSBasicStatusBar::StreamStarted(obs_output_t *output)
- {
- streamOutput = output;
- signal_handler_connect(obs_output_get_signal_handler(streamOutput),
- "reconnect", OBSOutputReconnect, this);
- signal_handler_connect(obs_output_get_signal_handler(streamOutput),
- "reconnect_success", OBSOutputReconnectSuccess,
- this);
- retries = 0;
- lastBytesSent = 0;
- lastBytesSentTime = os_gettime_ns();
- Activate();
- }
- void OBSBasicStatusBar::StreamStopped()
- {
- if (streamOutput) {
- signal_handler_disconnect(
- obs_output_get_signal_handler(streamOutput),
- "reconnect", OBSOutputReconnect, this);
- signal_handler_disconnect(
- obs_output_get_signal_handler(streamOutput),
- "reconnect_success", OBSOutputReconnectSuccess, this);
- ReconnectClear();
- streamOutput = nullptr;
- clearMessage();
- Deactivate();
- }
- }
- void OBSBasicStatusBar::RecordingStarted(obs_output_t *output)
- {
- recordOutput = output;
- Activate();
- }
- void OBSBasicStatusBar::RecordingStopped()
- {
- recordOutput = nullptr;
- Deactivate();
- }
- void OBSBasicStatusBar::RecordingPaused()
- {
- QString text = recordTime->text() + QStringLiteral(" (PAUSED)");
- recordTime->setText(text);
- if (recordOutput) {
- recordIcon->setPixmap(recordingPausePixmap);
- streamPauseIconToggle = true;
- }
- }
- void OBSBasicStatusBar::RecordingUnpaused()
- {
- if (recordOutput) {
- recordIcon->setPixmap(recordingActivePixmap);
- }
- }
|