Răsfoiți Sursa

UI: Prevent thread stalls with fader/volume widgets

When the OBS signal is triggered for these widgets, the invokeMethod
could cause the thread to stall, which could make it wait much longer
than necessary to output audio data.  When that happens, it causes audio
monitoring to get backed up and get unnecessarily delayed, as well as
cause general audio buffering in libobs to increase unnecessarily.

A simple fix both in terms of preventing that stall and improving UI
performance is to not call invokeMethod to update the widget each time,
and then instead have those widgets update themselves via a timer at a
specific interval.
jp9000 8 ani în urmă
părinte
comite
27a3b97f48
2 a modificat fișierele cu 94 adăugiri și 26 ștergeri
  1. 62 22
      UI/volume-control.cpp
  2. 32 4
      UI/volume-control.hpp

+ 62 - 22
UI/volume-control.cpp

@@ -17,6 +17,8 @@
 
 
 using namespace std;
 using namespace std;
 
 
+QWeakPointer<VolumeMeterTimer> VolumeMeter::updateTimer;
+
 void VolControl::OBSVolumeChanged(void *data, float db)
 void VolControl::OBSVolumeChanged(void *data, float db)
 {
 {
 	Q_UNUSED(db);
 	Q_UNUSED(db);
@@ -30,11 +32,10 @@ void VolControl::OBSVolumeLevel(void *data, float level, float mag,
 {
 {
 	VolControl *volControl = static_cast<VolControl*>(data);
 	VolControl *volControl = static_cast<VolControl*>(data);
 
 
-	QMetaObject::invokeMethod(volControl, "VolumeLevel",
-		Q_ARG(float, mag),
-		Q_ARG(float, level),
-		Q_ARG(float, peak),
-		Q_ARG(bool,  muted));
+	if (muted)
+		level = mag = peak = 0.0f;
+
+	volControl->volMeter->setLevels(mag, level, peak);
 }
 }
 
 
 void VolControl::OBSVolumeMuted(void *data, calldata_t *calldata)
 void VolControl::OBSVolumeMuted(void *data, calldata_t *calldata)
@@ -256,30 +257,51 @@ VolumeMeter::VolumeMeter(QWidget *parent)
 	peakColor.setRgb(0x3E, 0xF1, 0x2B);
 	peakColor.setRgb(0x3E, 0xF1, 0x2B);
 	peakHoldColor.setRgb(0x00, 0x00, 0x00);
 	peakHoldColor.setRgb(0x00, 0x00, 0x00);
 	
 	
-	resetTimer = new QTimer(this);
-	connect(resetTimer, SIGNAL(timeout()), this, SLOT(resetState()));
+	updateTimerRef = updateTimer.toStrongRef();
+	if (!updateTimerRef) {
+		updateTimerRef = QSharedPointer<VolumeMeterTimer>::create();
+		updateTimerRef->start(100);
+		updateTimer = updateTimerRef;
+	}
 
 
-	resetState();
+	updateTimerRef->AddVolControl(this);
 }
 }
 
 
-void VolumeMeter::resetState(void)
+VolumeMeter::~VolumeMeter()
 {
 {
-	setLevels(0.0f, 0.0f, 0.0f);
-	if (resetTimer->isActive())
-		resetTimer->stop();
+	updateTimerRef->RemoveVolControl(this);
 }
 }
 
 
 void VolumeMeter::setLevels(float nmag, float npeak, float npeakHold)
 void VolumeMeter::setLevels(float nmag, float npeak, float npeakHold)
 {
 {
-	mag      = nmag;
-	peak     = npeak;
-	peakHold = npeakHold;
+	uint64_t ts = os_gettime_ns();
+	QMutexLocker locker(&dataMutex);
+
+	mag += nmag;
+	peak += npeak;
+	peakHold += npeakHold;
+	multiple += 1.0f;
+	lastUpdateTime = ts;
+}
+
+inline void VolumeMeter::calcLevels()
+{
+	uint64_t ts = os_gettime_ns();
+	QMutexLocker locker(&dataMutex);
+
+	if (lastUpdateTime && ts - lastUpdateTime > 1000000000) {
+		mag = peak = peakHold = 0.0f;
+		multiple = 1.0f;
+		lastUpdateTime = 0;
+	}
 
 
-	update();
+	if (multiple > 0.0f) {
+		curMag = mag / multiple;
+		curPeak = peak / multiple;
+		curPeakHold = peakHold / multiple;
 
 
-	if (resetTimer->isActive())
-		resetTimer->stop();
-	resetTimer->start(1000);
+		mag = peak = peakHold = multiple = 0.0f;
+	}
 }
 }
 
 
 void VolumeMeter::paintEvent(QPaintEvent *event)
 void VolumeMeter::paintEvent(QPaintEvent *event)
@@ -292,9 +314,11 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
 	int width  = size().width();
 	int width  = size().width();
 	int height = size().height();
 	int height = size().height();
 
 
-	int scaledMag      = int((float)width * mag);
-	int scaledPeak     = int((float)width * peak);
-	int scaledPeakHold = int((float)width * peakHold);
+	calcLevels();
+
+	int scaledMag      = int((float)width * curMag);
+	int scaledPeak     = int((float)width * curPeak);
+	int scaledPeakHold = int((float)width * curPeakHold);
 
 
 	gradient.setStart(qreal(scaledMag), 0);
 	gradient.setStart(qreal(scaledMag), 0);
 	gradient.setFinalStop(qreal(scaledPeak), 0);
 	gradient.setFinalStop(qreal(scaledPeak), 0);
@@ -325,3 +349,19 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
 		scaledPeakHold, height);
 		scaledPeakHold, height);
 
 
 }
 }
+
+void VolumeMeterTimer::AddVolControl(VolumeMeter *meter)
+{
+	volumeMeters.push_back(meter);
+}
+
+void VolumeMeterTimer::RemoveVolControl(VolumeMeter *meter)
+{
+	volumeMeters.removeOne(meter);
+}
+
+void VolumeMeterTimer::timerEvent(QTimerEvent*)
+{
+	for (VolumeMeter *meter : volumeMeters)
+		meter->update();
+}

+ 32 - 4
UI/volume-control.hpp

@@ -2,8 +2,13 @@
 
 
 #include <obs.hpp>
 #include <obs.hpp>
 #include <QWidget>
 #include <QWidget>
+#include <QSharedPointer>
+#include <QTimer>
+#include <QMutex>
+#include <QList>
 
 
 class QPushButton;
 class QPushButton;
+class VolumeMeterTimer;
 
 
 class VolumeMeter : public QWidget
 class VolumeMeter : public QWidget
 {
 {
@@ -14,12 +19,23 @@ class VolumeMeter : public QWidget
 	Q_PROPERTY(QColor peakHoldColor READ getPeakHoldColor WRITE setPeakHoldColor DESIGNABLE true)
 	Q_PROPERTY(QColor peakHoldColor READ getPeakHoldColor WRITE setPeakHoldColor DESIGNABLE true)
 
 
 private:
 private:
-	float mag, peak, peakHold;
+	static QWeakPointer<VolumeMeterTimer> updateTimer;
+	QSharedPointer<VolumeMeterTimer> updateTimerRef;
+	float curMag = 0.0f, curPeak = 0.0f, curPeakHold = 0.0f;
+
+	inline void calcLevels();
+
+	QMutex dataMutex;
+	float mag = 0.0f, peak = 0.0f, peakHold = 0.0f;
+	float multiple = 0.0f;
+	uint64_t lastUpdateTime = 0;
+
 	QColor bkColor, magColor, peakColor, peakHoldColor;
 	QColor bkColor, magColor, peakColor, peakHoldColor;
-	QTimer *resetTimer;
 
 
 public:
 public:
 	explicit VolumeMeter(QWidget *parent = 0);
 	explicit VolumeMeter(QWidget *parent = 0);
+	~VolumeMeter();
+
 	void setLevels(float nmag, float npeak, float npeakHold);
 	void setLevels(float nmag, float npeak, float npeakHold);
 	QColor getBkColor() const;
 	QColor getBkColor() const;
 	void setBkColor(QColor c);
 	void setBkColor(QColor c);
@@ -32,8 +48,20 @@ public:
 
 
 protected:
 protected:
 	void paintEvent(QPaintEvent *event);
 	void paintEvent(QPaintEvent *event);
-private slots:
-	void resetState();
+};
+
+class VolumeMeterTimer : public QTimer {
+	Q_OBJECT
+
+public:
+	inline VolumeMeterTimer() : QTimer() {}
+
+	void AddVolControl(VolumeMeter *meter);
+	void RemoveVolControl(VolumeMeter *meter);
+
+protected:
+	virtual void timerEvent(QTimerEvent *event) override;
+	QList<VolumeMeter*> volumeMeters;
 };
 };
 
 
 class QLabel;
 class QLabel;