yolobs-studio/UI/window-basic-status-bar.cpp

540 lines
13 KiB
C++
Raw Normal View History

2016-02-23 23:16:51 +00:00
#include <QLabel>
2017-04-19 19:54:15 +00:00
#include <QHBoxLayout>
#include <QPainter>
#include <QPixmap>
2016-02-23 23:16:51 +00:00
#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)
2019-09-22 21:19:10 +00:00
: QStatusBar(parent),
delayInfo(new QLabel),
droppedFrames(new QLabel),
2020-05-27 20:57:19 +00:00
streamIcon(new QLabel),
2019-09-22 21:19:10 +00:00
streamTime(new QLabel),
2020-05-27 20:57:19 +00:00
recordIcon(new QLabel),
2019-09-22 21:19:10 +00:00
recordTime(new QLabel),
cpuUsage(new QLabel),
transparentPixmap(20, 20),
greenPixmap(20, 20),
grayPixmap(20, 20),
2020-05-27 20:57:19 +00:00
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)))
2016-02-23 23:16:51 +00:00
{
2017-04-19 19:54:15 +00:00
streamTime->setText(QString("LIVE: 00:00:00"));
recordTime->setText(QString("REC: 00:00:00"));
cpuUsage->setText(QString("CPU: 0.0%, 0.00 fps"));
2020-05-27 20:57:19 +00:00
streamIcon->setPixmap(streamingInactivePixmap);
recordIcon->setPixmap(recordingInactivePixmap);
2017-04-19 19:54:15 +00:00
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);
2016-02-23 23:16:51 +00:00
delayInfo->setAlignment(Qt::AlignRight);
2017-04-19 19:54:15 +00:00
delayInfo->setAlignment(Qt::AlignVCenter);
2016-02-23 23:16:51 +00:00
droppedFrames->setAlignment(Qt::AlignRight);
2017-04-19 19:54:15 +00:00
droppedFrames->setAlignment(Qt::AlignVCenter);
2020-05-27 20:57:19 +00:00
streamIcon->setAlignment(Qt::AlignRight);
streamIcon->setAlignment(Qt::AlignVCenter);
2017-04-19 19:54:15 +00:00
streamTime->setAlignment(Qt::AlignRight);
streamTime->setAlignment(Qt::AlignVCenter);
2020-05-27 20:57:19 +00:00
recordIcon->setAlignment(Qt::AlignRight);
recordIcon->setAlignment(Qt::AlignVCenter);
2017-04-19 19:54:15 +00:00
recordTime->setAlignment(Qt::AlignRight);
recordTime->setAlignment(Qt::AlignVCenter);
2016-02-23 23:16:51 +00:00
cpuUsage->setAlignment(Qt::AlignRight);
2017-04-19 19:54:15 +00:00
cpuUsage->setAlignment(Qt::AlignVCenter);
2016-02-23 23:16:51 +00:00
kbps->setAlignment(Qt::AlignRight);
2017-04-19 19:54:15 +00:00
kbps->setAlignment(Qt::AlignVCenter);
2016-02-23 23:16:51 +00:00
delayInfo->setIndent(20);
droppedFrames->setIndent(20);
2020-05-27 20:57:19 +00:00
streamIcon->setIndent(20);
recordIcon->setIndent(20);
2016-02-23 23:16:51 +00:00
cpuUsage->setIndent(20);
kbps->setIndent(10);
addPermanentWidget(droppedFrames);
2020-05-27 20:57:19 +00:00
addPermanentWidget(streamIcon);
2017-04-19 19:54:15 +00:00
addPermanentWidget(streamTime);
2020-05-27 20:57:19 +00:00
addPermanentWidget(recordIcon);
2017-04-19 19:54:15 +00:00
addPermanentWidget(recordTime);
2016-02-23 23:16:51 +00:00
addPermanentWidget(cpuUsage);
addPermanentWidget(delayInfo);
2017-04-19 19:54:15 +00:00
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);
2016-02-23 23:16:51 +00:00
}
void OBSBasicStatusBar::Activate()
{
if (!active) {
refreshTimer = new QTimer(this);
2019-09-22 21:19:10 +00:00
connect(refreshTimer, SIGNAL(timeout()), this,
SLOT(UpdateStatusBar()));
2016-02-23 23:16:51 +00:00
int skipped = video_output_get_skipped_frames(obs_get_video());
2019-09-22 21:19:10 +00:00
int total = video_output_get_total_frames(obs_get_video());
2016-02-23 23:16:51 +00:00
2017-04-19 19:54:15 +00:00
totalStreamSeconds = 0;
totalRecordSeconds = 0;
2016-02-23 23:16:51 +00:00
lastSkippedFrameCount = 0;
startSkippedFrameCount = skipped;
startTotalFrameCount = total;
refreshTimer->start(1000);
active = true;
2017-04-19 19:54:15 +00:00
if (streamOutput) {
statusSquare->setPixmap(grayPixmap);
}
2016-02-23 23:16:51 +00:00
}
2020-05-27 20:57:19 +00:00
if (streamOutput) {
streamIcon->setPixmap(streamingActivePixmap);
}
if (recordOutput) {
recordIcon->setPixmap(recordingActivePixmap);
}
2016-02-23 23:16:51 +00:00
}
void OBSBasicStatusBar::Deactivate()
{
2019-09-22 21:19:10 +00:00
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
2016-02-23 23:16:51 +00:00
if (!main)
return;
2017-04-19 19:54:15 +00:00
if (!streamOutput) {
streamTime->setText(QString("LIVE: 00:00:00"));
2020-05-27 20:57:19 +00:00
streamIcon->setPixmap(streamingInactivePixmap);
2017-04-19 19:54:15 +00:00
totalStreamSeconds = 0;
}
if (!recordOutput) {
recordTime->setText(QString("REC: 00:00:00"));
2020-05-27 20:57:19 +00:00
recordIcon->setPixmap(recordingInactivePixmap);
2017-04-19 19:54:15 +00:00
totalRecordSeconds = 0;
}
2016-02-23 23:16:51 +00:00
if (!main->outputHandler->Active()) {
delete refreshTimer;
2017-04-19 19:54:15 +00:00
2016-02-23 23:16:51 +00:00
delayInfo->setText("");
droppedFrames->setText("");
kbps->setText("");
delaySecTotal = 0;
delaySecStarting = 0;
delaySecStopping = 0;
reconnectTimeout = 0;
active = false;
2016-10-10 19:01:40 +00:00
overloadedNotify = true;
2017-04-19 19:54:15 +00:00
statusSquare->setPixmap(transparentPixmap);
2016-02-23 23:16:51 +00:00
}
}
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),
2019-09-22 21:19:10 +00:00
QString::number(delaySecStarting));
2016-02-23 23:16:51 +00:00
} 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;
2019-09-22 21:19:10 +00:00
uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
2016-02-23 23:16:51 +00:00
uint64_t bytesSentTime = os_gettime_ns();
2016-08-28 12:07:43 +00:00
if (bytesSent < lastBytesSent)
bytesSent = 0;
2016-02-23 23:16:51 +00:00
if (bytesSent == 0)
lastBytesSent = 0;
2019-09-22 21:19:10 +00:00
uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;
2016-02-23 23:16:51 +00:00
2019-09-22 21:19:10 +00:00
double timePassed =
double(bytesSentTime - lastBytesSentTime) / 1000000000.0;
2016-02-23 23:16:51 +00:00
double kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;
QString text;
2019-09-22 21:19:10 +00:00
text += QString("kb/s: ") + QString::number(kbitsPerSec, 'f', 0);
2017-04-19 19:54:15 +00:00
2016-02-23 23:16:51 +00:00
kbps->setText(text);
kbps->setMinimumWidth(kbps->width());
2019-09-22 21:19:10 +00:00
lastBytesSent = bytesSent;
lastBytesSentTime = bytesSentTime;
2016-02-23 23:16:51 +00:00
bitrateUpdateSeconds = 0;
}
void OBSBasicStatusBar::UpdateCPUUsage()
{
2019-09-22 21:19:10 +00:00
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
2016-02-23 23:16:51 +00:00
if (!main)
return;
QString text;
text += QString("CPU: ") +
2016-10-10 19:01:40 +00:00
QString::number(main->GetCPUUsage(), 'f', 1) + QString("%, ") +
QString::number(obs_get_active_fps(), 'f', 2) + QString(" fps");
2016-02-23 23:16:51 +00:00
cpuUsage->setText(text);
cpuUsage->setMinimumWidth(cpuUsage->width());
}
2017-04-19 19:54:15 +00:00
void OBSBasicStatusBar::UpdateStreamTime()
2016-02-23 23:16:51 +00:00
{
2017-04-19 19:54:15 +00:00
totalStreamSeconds++;
2016-02-23 23:16:51 +00:00
2019-09-22 21:19:10 +00:00
int seconds = totalStreamSeconds % 60;
2017-04-19 19:54:15 +00:00
int totalMinutes = totalStreamSeconds / 60;
2019-09-22 21:19:10 +00:00
int minutes = totalMinutes % 60;
int hours = totalMinutes / 60;
2016-02-23 23:16:51 +00:00
2020-03-25 08:07:22 +00:00
QString text = QString::asprintf("LIVE: %02d:%02d:%02d", hours, minutes,
seconds);
2017-04-19 19:54:15 +00:00
streamTime->setText(text);
streamTime->setMinimumWidth(streamTime->width());
2016-02-23 23:16:51 +00:00
if (reconnectTimeout > 0) {
2016-10-10 19:01:40 +00:00
QString msg = QTStr("Basic.StatusBar.Reconnecting")
2019-09-22 21:19:10 +00:00
.arg(QString::number(retries),
QString::number(reconnectTimeout));
2016-10-10 19:01:40 +00:00
showMessage(msg);
2016-02-23 23:16:51 +00:00
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();
}
}
2019-09-22 21:19:10 +00:00
extern volatile bool recording_paused;
2017-04-19 19:54:15 +00:00
void OBSBasicStatusBar::UpdateRecordTime()
{
2019-09-22 21:19:10 +00:00
bool paused = os_atomic_load_bool(&recording_paused);
2017-04-19 19:54:15 +00:00
2020-05-27 20:57:19 +00:00
if (!paused) {
2019-09-22 21:19:10 +00:00
totalRecordSeconds++;
2017-04-19 19:54:15 +00:00
2019-09-22 21:19:10 +00:00
int seconds = totalRecordSeconds % 60;
int totalMinutes = totalRecordSeconds / 60;
int minutes = totalMinutes % 60;
int hours = totalMinutes / 60;
2020-05-27 20:57:19 +00:00
QString text = QString::asprintf("REC: %02d:%02d:%02d", hours,
minutes, seconds);
2019-09-22 21:19:10 +00:00
2020-05-27 20:57:19 +00:00
recordTime->setText(text);
recordTime->setMinimumWidth(recordTime->width());
} else {
recordIcon->setPixmap(streamPauseIconToggle
? recordingPauseInactivePixmap
: recordingPausePixmap);
streamPauseIconToggle = !streamPauseIconToggle;
}
2017-04-19 19:54:15 +00:00
}
2016-02-23 23:16:51 +00:00
void OBSBasicStatusBar::UpdateDroppedFrames()
{
if (!streamOutput)
return;
int totalDropped = obs_output_get_frames_dropped(streamOutput);
2019-09-22 21:19:10 +00:00
int totalFrames = obs_output_get_total_frames(streamOutput);
double percent = (double)totalDropped / (double)totalFrames * 100.0;
2016-02-23 23:16:51 +00:00
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());
2017-04-19 19:54:15 +00:00
/* ----------------------------------- *
* 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;
2019-09-22 21:19:10 +00:00
if (red > 1.0f)
red = 1.0f;
2017-04-19 19:54:15 +00:00
red *= 255.0;
float green = (1.0f - avgCongestion) * 2.0f;
2019-09-22 21:19:10 +00:00
if (green > 1.0f)
green = 1.0f;
2017-04-19 19:54:15 +00:00
green *= 255.0;
pixmap.fill(QColor(int(red), int(green), 0));
statusSquare->setPixmap(pixmap);
}
lastCongestion = congestion;
2016-02-23 23:16:51 +00:00
}
void OBSBasicStatusBar::OBSOutputReconnect(void *data, calldata_t *params)
{
OBSBasicStatusBar *statusBar =
2019-09-22 21:19:10 +00:00
reinterpret_cast<OBSBasicStatusBar *>(data);
2016-02-23 23:16:51 +00:00
int seconds = (int)calldata_int(params, "timeout_sec");
QMetaObject::invokeMethod(statusBar, "Reconnect", Q_ARG(int, seconds));
UNUSED_PARAMETER(params);
}
2019-09-22 21:19:10 +00:00
void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data,
calldata_t *params)
2016-02-23 23:16:51 +00:00
{
OBSBasicStatusBar *statusBar =
2019-09-22 21:19:10 +00:00
reinterpret_cast<OBSBasicStatusBar *>(data);
2016-02-23 23:16:51 +00:00
QMetaObject::invokeMethod(statusBar, "ReconnectSuccess");
UNUSED_PARAMETER(params);
}
void OBSBasicStatusBar::Reconnect(int seconds)
{
2019-09-22 21:19:10 +00:00
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
2016-10-10 19:01:40 +00:00
if (!retries)
main->SysTrayNotify(
2019-09-22 21:19:10 +00:00
QTStr("Basic.SystemTray.Message.Reconnecting"),
QSystemTrayIcon::Warning);
2016-10-10 19:01:40 +00:00
2016-02-23 23:16:51 +00:00
reconnectTimeout = seconds;
if (streamOutput) {
delaySecTotal = obs_output_get_active_delay(streamOutput);
UpdateDelayMsg();
2016-10-10 19:01:40 +00:00
retries++;
2016-02-23 23:16:51 +00:00
}
}
2016-08-28 12:07:43 +00:00
void OBSBasicStatusBar::ReconnectClear()
2016-02-23 23:16:51 +00:00
{
2019-09-22 21:19:10 +00:00
retries = 0;
reconnectTimeout = 0;
2016-02-23 23:16:51 +00:00
bitrateUpdateSeconds = -1;
2019-09-22 21:19:10 +00:00
lastBytesSent = 0;
lastBytesSentTime = os_gettime_ns();
delaySecTotal = 0;
2016-08-28 12:07:43 +00:00
UpdateDelayMsg();
}
void OBSBasicStatusBar::ReconnectSuccess()
{
2019-09-22 21:19:10 +00:00
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
2016-10-10 19:01:40 +00:00
QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful");
showMessage(msg, 4000);
main->SysTrayNotify(msg, QSystemTrayIcon::Information);
2016-08-28 12:07:43 +00:00
ReconnectClear();
2016-02-23 23:16:51 +00:00
if (streamOutput) {
delaySecTotal = obs_output_get_active_delay(streamOutput);
UpdateDelayMsg();
}
}
void OBSBasicStatusBar::UpdateStatusBar()
{
2019-09-22 21:19:10 +00:00
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
2016-10-10 19:01:40 +00:00
2016-02-23 23:16:51 +00:00
UpdateBandwidth();
2017-04-19 19:54:15 +00:00
if (streamOutput)
UpdateStreamTime();
if (recordOutput)
UpdateRecordTime();
2016-02-23 23:16:51 +00:00
UpdateDroppedFrames();
int skipped = video_output_get_skipped_frames(obs_get_video());
2019-09-22 21:19:10 +00:00
int total = video_output_get_total_frames(obs_get_video());
2016-02-23 23:16:51 +00:00
skipped -= startSkippedFrameCount;
2019-09-22 21:19:10 +00:00
total -= startTotalFrameCount;
2016-02-23 23:16:51 +00:00
int diff = skipped - lastSkippedFrameCount;
double percentage = double(skipped) / double(total) * 100.0;
2016-10-10 19:01:40 +00:00
if (diff > 10 && percentage >= 0.1f) {
2016-02-23 23:16:51 +00:00
showMessage(QTStr("HighResourceUsage"), 4000);
2016-10-10 19:01:40 +00:00
if (!main->isVisible() && overloadedNotify) {
main->SysTrayNotify(QTStr("HighResourceUsage"),
2019-09-22 21:19:10 +00:00
QSystemTrayIcon::Warning);
2016-10-10 19:01:40 +00:00
overloadedNotify = false;
}
}
2016-02-23 23:16:51 +00:00
lastSkippedFrameCount = skipped;
}
void OBSBasicStatusBar::StreamDelayStarting(int sec)
{
2019-09-22 21:19:10 +00:00
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
2016-02-23 23:16:51 +00:00
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),
2019-09-22 21:19:10 +00:00
"reconnect", OBSOutputReconnect, this);
2016-02-23 23:16:51 +00:00
signal_handler_connect(obs_output_get_signal_handler(streamOutput),
2019-09-22 21:19:10 +00:00
"reconnect_success", OBSOutputReconnectSuccess,
this);
2016-02-23 23:16:51 +00:00
2019-09-22 21:19:10 +00:00
retries = 0;
lastBytesSent = 0;
2016-02-23 23:16:51 +00:00
lastBytesSentTime = os_gettime_ns();
Activate();
}
void OBSBasicStatusBar::StreamStopped()
{
if (streamOutput) {
signal_handler_disconnect(
2019-09-22 21:19:10 +00:00
obs_output_get_signal_handler(streamOutput),
"reconnect", OBSOutputReconnect, this);
2016-02-23 23:16:51 +00:00
signal_handler_disconnect(
2019-09-22 21:19:10 +00:00
obs_output_get_signal_handler(streamOutput),
"reconnect_success", OBSOutputReconnectSuccess, this);
2016-02-23 23:16:51 +00:00
2016-08-28 12:07:43 +00:00
ReconnectClear();
2016-02-23 23:16:51 +00:00
streamOutput = nullptr;
clearMessage();
Deactivate();
}
}
void OBSBasicStatusBar::RecordingStarted(obs_output_t *output)
{
recordOutput = output;
Activate();
}
void OBSBasicStatusBar::RecordingStopped()
{
recordOutput = nullptr;
Deactivate();
}
2020-05-27 20:57:19 +00:00
void OBSBasicStatusBar::RecordingPaused()
{
QString text = QStringLiteral("REC: PAUSED");
recordTime->setText(text);
recordTime->setMinimumWidth(recordTime->width());
if (recordOutput) {
recordIcon->setPixmap(recordingPausePixmap);
streamPauseIconToggle = true;
}
}
void OBSBasicStatusBar::RecordingUnpaused()
{
if (recordOutput) {
recordIcon->setPixmap(recordingActivePixmap);
}
}