frontend: Prepare Qt UI Widgets for splits
This commit is contained in:
parent
f813121bb9
commit
9f887c76d3
2541
frontend/utility/AdvancedOutput.hpp
Normal file
2541
frontend/utility/AdvancedOutput.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/BasicOutputHandler.cpp
Normal file
2541
frontend/utility/BasicOutputHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1382
frontend/utility/SceneRenameDelegate.hpp
Normal file
1382
frontend/utility/SceneRenameDelegate.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/SimpleOutput.cpp
Normal file
2541
frontend/utility/SimpleOutput.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/SimpleOutput.hpp
Normal file
2541
frontend/utility/SimpleOutput.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/StartMultiTrackVideoStreamingGuard.hpp
Normal file
2541
frontend/utility/StartMultiTrackVideoStreamingGuard.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1507
frontend/utility/VolumeMeterTimer.cpp
Normal file
1507
frontend/utility/VolumeMeterTimer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/utility/VolumeMeterTimer.hpp
Normal file
341
frontend/utility/VolumeMeterTimer.hpp
Normal file
@ -0,0 +1,341 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
10203
frontend/widgets/ColorSelect.cpp
Normal file
10203
frontend/widgets/ColorSelect.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1382
frontend/widgets/ColorSelect.hpp
Normal file
1382
frontend/widgets/ColorSelect.hpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic.cpp
Normal file
10203
frontend/widgets/OBSBasic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1382
frontend/widgets/OBSBasic.hpp
Normal file
1382
frontend/widgets/OBSBasic.hpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Clipboard.cpp
Normal file
10203
frontend/widgets/OBSBasic_Clipboard.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_ContextToolbar.cpp
Normal file
10203
frontend/widgets/OBSBasic_ContextToolbar.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Docks.cpp
Normal file
10203
frontend/widgets/OBSBasic_Docks.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Hotkeys.cpp
Normal file
10203
frontend/widgets/OBSBasic_Hotkeys.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_MainControls.cpp
Normal file
10203
frontend/widgets/OBSBasic_MainControls.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_OutputHandler.cpp
Normal file
10203
frontend/widgets/OBSBasic_OutputHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Preview.cpp
Normal file
10203
frontend/widgets/OBSBasic_Preview.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Projectors.cpp
Normal file
10203
frontend/widgets/OBSBasic_Projectors.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Recording.cpp
Normal file
10203
frontend/widgets/OBSBasic_Recording.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_ReplayBuffer.cpp
Normal file
10203
frontend/widgets/OBSBasic_ReplayBuffer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_SceneCollections.cpp
Normal file
10203
frontend/widgets/OBSBasic_SceneCollections.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_SceneItems.cpp
Normal file
10203
frontend/widgets/OBSBasic_SceneItems.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Scenes.cpp
Normal file
10203
frontend/widgets/OBSBasic_Scenes.cpp
Normal file
File diff suppressed because it is too large
Load Diff
347
frontend/widgets/OBSBasic_Screenshots.cpp
Normal file
347
frontend/widgets/OBSBasic_Screenshots.cpp
Normal file
@ -0,0 +1,347 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "window-basic-main.hpp"
|
||||
#include "screenshot-obj.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wincodec.h>
|
||||
#include <wincodecsdk.h>
|
||||
#include <wrl/client.h>
|
||||
#pragma comment(lib, "windowscodecs.lib")
|
||||
#endif
|
||||
|
||||
static void ScreenshotTick(void *param, float);
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
ScreenshotObj::ScreenshotObj(obs_source_t *source) : weakSource(OBSGetWeakRef(source))
|
||||
{
|
||||
obs_add_tick_callback(ScreenshotTick, this);
|
||||
}
|
||||
|
||||
ScreenshotObj::~ScreenshotObj()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
gs_stagesurface_destroy(stagesurf);
|
||||
gs_texrender_destroy(texrender);
|
||||
obs_leave_graphics();
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
|
||||
if (th.joinable()) {
|
||||
th.join();
|
||||
|
||||
if (cx && cy) {
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
main->ShowStatusBarMessage(
|
||||
QTStr("Basic.StatusBar.ScreenshotSavedTo").arg(QT_UTF8(path.c_str())));
|
||||
|
||||
main->lastScreenshot = path;
|
||||
|
||||
main->OnEvent(OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotObj::Screenshot()
|
||||
{
|
||||
OBSSource source = OBSGetStrongRef(weakSource);
|
||||
|
||||
if (source) {
|
||||
cx = obs_source_get_width(source);
|
||||
cy = obs_source_get_height(source);
|
||||
} else {
|
||||
obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
cx = ovi.base_width;
|
||||
cy = ovi.base_height;
|
||||
}
|
||||
|
||||
if (!cx || !cy) {
|
||||
blog(LOG_WARNING, "Cannot screenshot, invalid target size");
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
enum gs_color_space space = obs_source_get_color_space(source, 0, nullptr);
|
||||
if (space == GS_CS_709_EXTENDED) {
|
||||
/* Convert for JXR */
|
||||
space = GS_CS_709_SCRGB;
|
||||
}
|
||||
#else
|
||||
/* Tonemap to SDR if HDR */
|
||||
const enum gs_color_space space = GS_CS_SRGB;
|
||||
#endif
|
||||
const enum gs_color_format format = gs_get_format_from_space(space);
|
||||
|
||||
texrender = gs_texrender_create(format, GS_ZS_NONE);
|
||||
stagesurf = gs_stagesurface_create(cx, cy, format);
|
||||
|
||||
if (gs_texrender_begin_with_color_space(texrender, cx, cy, space)) {
|
||||
vec4 zero;
|
||||
vec4_zero(&zero);
|
||||
|
||||
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
|
||||
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||
|
||||
gs_blend_state_push();
|
||||
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||
|
||||
if (source) {
|
||||
obs_source_inc_showing(source);
|
||||
obs_source_video_render(source);
|
||||
obs_source_dec_showing(source);
|
||||
} else {
|
||||
obs_render_main_texture();
|
||||
}
|
||||
|
||||
gs_blend_state_pop();
|
||||
gs_texrender_end(texrender);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotObj::Download()
|
||||
{
|
||||
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
|
||||
}
|
||||
|
||||
void ScreenshotObj::Copy()
|
||||
{
|
||||
uint8_t *videoData = nullptr;
|
||||
uint32_t videoLinesize = 0;
|
||||
|
||||
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||
if (gs_stagesurface_get_color_format(stagesurf) == GS_RGBA16F) {
|
||||
const uint32_t linesize = cx * 8;
|
||||
half_bytes.reserve(cx * cy * 8);
|
||||
|
||||
for (uint32_t y = 0; y < cy; y++) {
|
||||
const uint8_t *const line = videoData + (y * videoLinesize);
|
||||
half_bytes.insert(half_bytes.end(), line, line + linesize);
|
||||
}
|
||||
} else {
|
||||
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
||||
|
||||
int linesize = image.bytesPerLine();
|
||||
for (int y = 0; y < (int)cy; y++)
|
||||
memcpy(image.scanLine(y), videoData + (y * videoLinesize), linesize);
|
||||
}
|
||||
|
||||
gs_stagesurface_unmap(stagesurf);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotObj::Save()
|
||||
{
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
config_t *config = main->Config();
|
||||
|
||||
const char *mode = config_get_string(config, "Output", "Mode");
|
||||
const char *type = config_get_string(config, "AdvOut", "RecType");
|
||||
const char *adv_path = strcmp(type, "Standard") ? config_get_string(config, "AdvOut", "FFFilePath")
|
||||
: config_get_string(config, "AdvOut", "RecFilePath");
|
||||
const char *rec_path = strcmp(mode, "Advanced") ? config_get_string(config, "SimpleOutput", "FilePath")
|
||||
: adv_path;
|
||||
|
||||
bool noSpace = config_get_bool(config, "SimpleOutput", "FileNameWithoutSpace");
|
||||
const char *filenameFormat = config_get_string(config, "Output", "FilenameFormatting");
|
||||
bool overwriteIfExists = config_get_bool(config, "Output", "OverwriteIfExists");
|
||||
|
||||
const char *ext = half_bytes.empty() ? "png" : "jxr";
|
||||
path = GetOutputFilename(rec_path, ext, noSpace, overwriteIfExists,
|
||||
GetFormatString(filenameFormat, "Screenshot", nullptr).c_str());
|
||||
|
||||
th = std::thread([this] { MuxAndFinish(); });
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static HRESULT SaveJxrImage(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy, IWICBitmapFrameEncode *frameEncode,
|
||||
IPropertyBag2 *options)
|
||||
{
|
||||
wchar_t lossless[] = L"Lossless";
|
||||
PROPBAG2 bag = {};
|
||||
bag.pstrName = lossless;
|
||||
VARIANT value = {};
|
||||
value.vt = VT_BOOL;
|
||||
value.bVal = TRUE;
|
||||
HRESULT hr = options->Write(1, &bag, &value);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->Initialize(options);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->SetSize(cx, cy);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->SetResolution(72, 72);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat64bppRGBAHalf;
|
||||
hr = frameEncode->SetPixelFormat(&pixelFormat);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
if (memcmp(&pixelFormat, &GUID_WICPixelFormat64bppRGBAHalf, sizeof(WICPixelFormatGUID)) != 0)
|
||||
return E_FAIL;
|
||||
|
||||
hr = frameEncode->WritePixels(cy, cx * 8, cx * cy * 8, pixels);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->Commit();
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT SaveJxr(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IWICImagingFactory> factory;
|
||||
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(factory.GetAddressOf()));
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICStream> stream;
|
||||
hr = factory->CreateStream(stream.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = stream->InitializeFromFilename(path, GENERIC_WRITE);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICBitmapEncoder> encoder = NULL;
|
||||
hr = factory->CreateEncoder(GUID_ContainerFormatWmp, NULL, encoder.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICBitmapFrameEncode> frameEncode;
|
||||
Microsoft::WRL::ComPtr<IPropertyBag2> options;
|
||||
hr = encoder->CreateNewFrame(frameEncode.GetAddressOf(), options.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = SaveJxrImage(path, pixels, cx, cy, frameEncode.Get(), options.Get());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
encoder->Commit();
|
||||
return S_OK;
|
||||
}
|
||||
#endif // #ifdef _WIN32
|
||||
|
||||
void ScreenshotObj::MuxAndFinish()
|
||||
{
|
||||
if (half_bytes.empty()) {
|
||||
image.save(QT_UTF8(path.c_str()));
|
||||
blog(LOG_INFO, "Saved screenshot to '%s'", path.c_str());
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
wchar_t *path_w = nullptr;
|
||||
os_utf8_to_wcs_ptr(path.c_str(), 0, &path_w);
|
||||
if (path_w) {
|
||||
SaveJxr(path_w, half_bytes.data(), cx, cy);
|
||||
bfree(path_w);
|
||||
}
|
||||
#endif // #ifdef _WIN32
|
||||
}
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
#define STAGE_SCREENSHOT 0
|
||||
#define STAGE_DOWNLOAD 1
|
||||
#define STAGE_COPY_AND_SAVE 2
|
||||
#define STAGE_FINISH 3
|
||||
|
||||
static void ScreenshotTick(void *param, float)
|
||||
{
|
||||
ScreenshotObj *data = reinterpret_cast<ScreenshotObj *>(param);
|
||||
|
||||
if (data->stage == STAGE_FINISH) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
switch (data->stage) {
|
||||
case STAGE_SCREENSHOT:
|
||||
data->Screenshot();
|
||||
break;
|
||||
case STAGE_DOWNLOAD:
|
||||
data->Download();
|
||||
break;
|
||||
case STAGE_COPY_AND_SAVE:
|
||||
data->Copy();
|
||||
QMetaObject::invokeMethod(data, "Save");
|
||||
obs_remove_tick_callback(ScreenshotTick, data);
|
||||
break;
|
||||
}
|
||||
|
||||
obs_leave_graphics();
|
||||
|
||||
data->stage++;
|
||||
}
|
||||
|
||||
void OBSBasic::Screenshot(OBSSource source)
|
||||
{
|
||||
if (!!screenshotData) {
|
||||
blog(LOG_WARNING, "Cannot take new screenshot, "
|
||||
"screenshot currently in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
screenshotData = new ScreenshotObj(source);
|
||||
}
|
||||
|
||||
void OBSBasic::ScreenshotSelectedSource()
|
||||
{
|
||||
OBSSceneItem item = GetCurrentSceneItem();
|
||||
if (item) {
|
||||
Screenshot(obs_sceneitem_get_source(item));
|
||||
} else {
|
||||
blog(LOG_INFO, "Could not take a source screenshot: "
|
||||
"no source selected");
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasic::ScreenshotProgram()
|
||||
{
|
||||
Screenshot(GetProgramSource());
|
||||
}
|
||||
|
||||
void OBSBasic::ScreenshotScene()
|
||||
{
|
||||
Screenshot(GetCurrentSceneSource());
|
||||
}
|
10203
frontend/widgets/OBSBasic_Service.cpp
Normal file
10203
frontend/widgets/OBSBasic_Service.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_StatusBar.cpp
Normal file
10203
frontend/widgets/OBSBasic_StatusBar.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Streaming.cpp
Normal file
10203
frontend/widgets/OBSBasic_Streaming.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_StudioMode.cpp
Normal file
10203
frontend/widgets/OBSBasic_StudioMode.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_SysTray.cpp
Normal file
10203
frontend/widgets/OBSBasic_SysTray.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Transitions.cpp
Normal file
10203
frontend/widgets/OBSBasic_Transitions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Updater.cpp
Normal file
10203
frontend/widgets/OBSBasic_Updater.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_VirtualCam.cpp
Normal file
10203
frontend/widgets/OBSBasic_VirtualCam.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_VolControl.cpp
Normal file
10203
frontend/widgets/OBSBasic_VolControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_YouTube.cpp
Normal file
10203
frontend/widgets/OBSBasic_YouTube.cpp
Normal file
File diff suppressed because it is too large
Load Diff
243
frontend/widgets/OBSQTDisplay.cpp
Normal file
243
frontend/widgets/OBSQTDisplay.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
#include "moc_qt-display.cpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include <QWindow>
|
||||
#include <QScreen>
|
||||
#include <QResizeEvent>
|
||||
#include <QShowEvent>
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <obs-config.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <obs-nix-platform.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
class SurfaceEventFilter : public QObject {
|
||||
OBSQTDisplay *display;
|
||||
|
||||
public:
|
||||
SurfaceEventFilter(OBSQTDisplay *src) : QObject(src), display(src) {}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override
|
||||
{
|
||||
bool result = QObject::eventFilter(obj, event);
|
||||
QPlatformSurfaceEvent *surfaceEvent;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::PlatformSurface:
|
||||
surfaceEvent = static_cast<QPlatformSurfaceEvent *>(event);
|
||||
|
||||
switch (surfaceEvent->surfaceEventType()) {
|
||||
case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
|
||||
display->DestroyDisplay();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
static inline long long color_to_int(const QColor &color)
|
||||
{
|
||||
auto shift = [&](unsigned val, int shift) {
|
||||
return ((val & 0xff) << shift);
|
||||
};
|
||||
|
||||
return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
|
||||
}
|
||||
|
||||
static inline QColor rgba_to_color(uint32_t rgba)
|
||||
{
|
||||
return QColor::fromRgb(rgba & 0xFF, (rgba >> 8) & 0xFF, (rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
static bool QTToGSWindow(QWindow *window, gs_window &gswindow)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
gswindow.hwnd = (HWND)window->winId();
|
||||
#elif __APPLE__
|
||||
gswindow.view = (id)window->winId();
|
||||
#else
|
||||
switch (obs_get_nix_platform()) {
|
||||
case OBS_NIX_PLATFORM_X11_EGL:
|
||||
gswindow.id = window->winId();
|
||||
gswindow.display = obs_get_nix_platform_display();
|
||||
break;
|
||||
#ifdef ENABLE_WAYLAND
|
||||
case OBS_NIX_PLATFORM_WAYLAND: {
|
||||
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
|
||||
gswindow.display = native->nativeResourceForWindow("surface", window);
|
||||
success = gswindow.display != nullptr;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return success;
|
||||
}
|
||||
|
||||
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags)
|
||||
{
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
setAttribute(Qt::WA_StaticContents);
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
|
||||
auto windowVisible = [this](bool visible) {
|
||||
if (!visible) {
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
display = nullptr;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (!display) {
|
||||
CreateDisplay();
|
||||
} else {
|
||||
QSize size = GetPixelSize(this);
|
||||
obs_display_resize(display, size.width(), size.height());
|
||||
}
|
||||
};
|
||||
|
||||
auto screenChanged = [this](QScreen *) {
|
||||
CreateDisplay();
|
||||
|
||||
QSize size = GetPixelSize(this);
|
||||
obs_display_resize(display, size.width(), size.height());
|
||||
};
|
||||
|
||||
connect(windowHandle(), &QWindow::visibleChanged, windowVisible);
|
||||
connect(windowHandle(), &QWindow::screenChanged, screenChanged);
|
||||
|
||||
windowHandle()->installEventFilter(new SurfaceEventFilter(this));
|
||||
}
|
||||
|
||||
QColor OBSQTDisplay::GetDisplayBackgroundColor() const
|
||||
{
|
||||
return rgba_to_color(backgroundColor);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::SetDisplayBackgroundColor(const QColor &color)
|
||||
{
|
||||
uint32_t newBackgroundColor = (uint32_t)color_to_int(color);
|
||||
|
||||
if (newBackgroundColor != backgroundColor) {
|
||||
backgroundColor = newBackgroundColor;
|
||||
UpdateDisplayBackgroundColor();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSQTDisplay::UpdateDisplayBackgroundColor()
|
||||
{
|
||||
obs_display_set_background_color(display, backgroundColor);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::CreateDisplay()
|
||||
{
|
||||
if (display)
|
||||
return;
|
||||
|
||||
if (destroying)
|
||||
return;
|
||||
|
||||
if (!windowHandle()->isExposed())
|
||||
return;
|
||||
|
||||
QSize size = GetPixelSize(this);
|
||||
|
||||
gs_init_data info = {};
|
||||
info.cx = size.width();
|
||||
info.cy = size.height();
|
||||
info.format = GS_BGRA;
|
||||
info.zsformat = GS_ZS_NONE;
|
||||
|
||||
if (!QTToGSWindow(windowHandle(), info.window))
|
||||
return;
|
||||
|
||||
display = obs_display_create(&info, backgroundColor);
|
||||
|
||||
emit DisplayCreated(this);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
CreateDisplay();
|
||||
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::moveEvent(QMoveEvent *event)
|
||||
{
|
||||
QWidget::moveEvent(event);
|
||||
|
||||
OnMove();
|
||||
}
|
||||
|
||||
bool OBSQTDisplay::nativeEvent(const QByteArray &, void *message, qintptr *)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const MSG &msg = *static_cast<MSG *>(message);
|
||||
switch (msg.message) {
|
||||
case WM_DISPLAYCHANGE:
|
||||
OnDisplayChange();
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(message);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OBSQTDisplay::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
|
||||
CreateDisplay();
|
||||
|
||||
if (isVisible() && display) {
|
||||
QSize size = GetPixelSize(this);
|
||||
obs_display_resize(display, size.width(), size.height());
|
||||
}
|
||||
|
||||
emit DisplayResized();
|
||||
}
|
||||
|
||||
QPaintEngine *OBSQTDisplay::paintEngine() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OBSQTDisplay::OnMove()
|
||||
{
|
||||
if (display)
|
||||
obs_display_update_color_space(display);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::OnDisplayChange()
|
||||
{
|
||||
if (display)
|
||||
obs_display_update_color_space(display);
|
||||
}
|
601
frontend/widgets/StatusBarWidget.cpp
Normal file
601
frontend/widgets/StatusBarWidget.cpp
Normal file
@ -0,0 +1,601 @@
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include "obs-app.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "moc_window-basic-status-bar.cpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
#include "ui_StatusBarWidget.h"
|
||||
|
||||
static constexpr int bitrateUpdateSeconds = 2;
|
||||
static constexpr int congestionUpdateSeconds = 4;
|
||||
static constexpr float excellentThreshold = 0.0f;
|
||||
static constexpr float goodThreshold = 0.3333f;
|
||||
static constexpr float mediocreThreshold = 0.6667f;
|
||||
static constexpr float badThreshold = 1.0f;
|
||||
|
||||
StatusBarWidget::StatusBarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::StatusBarWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
StatusBarWidget::~StatusBarWidget() {}
|
||||
|
||||
OBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)
|
||||
: QStatusBar(parent),
|
||||
excellentPixmap(QIcon(":/res/images/network-excellent.svg").pixmap(QSize(16, 16))),
|
||||
goodPixmap(QIcon(":/res/images/network-good.svg").pixmap(QSize(16, 16))),
|
||||
mediocrePixmap(QIcon(":/res/images/network-mediocre.svg").pixmap(QSize(16, 16))),
|
||||
badPixmap(QIcon(":/res/images/network-bad.svg").pixmap(QSize(16, 16))),
|
||||
recordingActivePixmap(QIcon(":/res/images/recording-active.svg").pixmap(QSize(16, 16))),
|
||||
recordingPausePixmap(QIcon(":/res/images/recording-pause.svg").pixmap(QSize(16, 16))),
|
||||
streamingActivePixmap(QIcon(":/res/images/streaming-active.svg").pixmap(QSize(16, 16)))
|
||||
{
|
||||
congestionArray.reserve(congestionUpdateSeconds);
|
||||
|
||||
statusWidget = new StatusBarWidget(this);
|
||||
statusWidget->ui->delayInfo->setText("");
|
||||
statusWidget->ui->droppedFrames->setText(QTStr("DroppedFrames").arg("0", "0.0"));
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||
statusWidget->ui->streamTime->setDisabled(true);
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||
statusWidget->ui->recordTime->setDisabled(true);
|
||||
statusWidget->ui->delayFrame->hide();
|
||||
statusWidget->ui->issuesFrame->hide();
|
||||
statusWidget->ui->kbps->hide();
|
||||
|
||||
addPermanentWidget(statusWidget, 1);
|
||||
setMinimumHeight(statusWidget->height());
|
||||
|
||||
UpdateIcons();
|
||||
connect(App(), &OBSApp::StyleChanged, this, &OBSBasicStatusBar::UpdateIcons);
|
||||
|
||||
messageTimer = new QTimer(this);
|
||||
messageTimer->setSingleShot(true);
|
||||
connect(messageTimer, &QTimer::timeout, this, &OBSBasicStatusBar::clearMessage);
|
||||
|
||||
clearMessage();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::Activate()
|
||||
{
|
||||
if (!active) {
|
||||
refreshTimer = new QTimer(this);
|
||||
connect(refreshTimer, &QTimer::timeout, this, &OBSBasicStatusBar::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) {
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
}
|
||||
}
|
||||
|
||||
if (streamOutput) {
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingActivePixmap);
|
||||
statusWidget->ui->streamTime->setDisabled(false);
|
||||
statusWidget->ui->issuesFrame->show();
|
||||
statusWidget->ui->kbps->show();
|
||||
firstCongestionUpdate = true;
|
||||
}
|
||||
|
||||
if (recordOutput) {
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
|
||||
statusWidget->ui->recordTime->setDisabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::Deactivate()
|
||||
{
|
||||
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||
if (!main)
|
||||
return;
|
||||
|
||||
if (!streamOutput) {
|
||||
statusWidget->ui->streamTime->setText(QString("00:00:00"));
|
||||
statusWidget->ui->streamTime->setDisabled(true);
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
statusWidget->ui->delayFrame->hide();
|
||||
statusWidget->ui->issuesFrame->hide();
|
||||
statusWidget->ui->kbps->hide();
|
||||
totalStreamSeconds = 0;
|
||||
congestionArray.clear();
|
||||
disconnected = false;
|
||||
firstCongestionUpdate = false;
|
||||
}
|
||||
|
||||
if (!recordOutput) {
|
||||
statusWidget->ui->recordTime->setText(QString("00:00:00"));
|
||||
statusWidget->ui->recordTime->setDisabled(true);
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||
totalRecordSeconds = 0;
|
||||
}
|
||||
|
||||
if (main->outputHandler && !main->outputHandler->Active()) {
|
||||
delete refreshTimer;
|
||||
|
||||
statusWidget->ui->delayInfo->setText("");
|
||||
statusWidget->ui->droppedFrames->setText(QTStr("DroppedFrames").arg("0", "0.0"));
|
||||
statusWidget->ui->kbps->setText("0 kbps");
|
||||
|
||||
delaySecTotal = 0;
|
||||
delaySecStarting = 0;
|
||||
delaySecStopping = 0;
|
||||
reconnectTimeout = 0;
|
||||
active = false;
|
||||
overloadedNotify = true;
|
||||
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (!statusWidget->ui->delayFrame->isVisible())
|
||||
statusWidget->ui->delayFrame->show();
|
||||
|
||||
statusWidget->ui->delayInfo->setText(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateBandwidth()
|
||||
{
|
||||
if (!streamOutput)
|
||||
return;
|
||||
|
||||
if (++seconds < bitrateUpdateSeconds)
|
||||
return;
|
||||
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
uint64_t bytesSent = obs_output_get_total_bytes(output);
|
||||
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::number(kbitsPerSec, 'f', 0) + QString(" kbps");
|
||||
|
||||
statusWidget->ui->kbps->setText(text);
|
||||
statusWidget->ui->kbps->setMinimumWidth(statusWidget->ui->kbps->width());
|
||||
|
||||
if (!statusWidget->ui->kbps->isVisible())
|
||||
statusWidget->ui->kbps->show();
|
||||
|
||||
lastBytesSent = bytesSent;
|
||||
lastBytesSentTime = bytesSentTime;
|
||||
seconds = 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("%");
|
||||
|
||||
statusWidget->ui->cpuUsage->setText(text);
|
||||
statusWidget->ui->cpuUsage->setMinimumWidth(statusWidget->ui->cpuUsage->width());
|
||||
|
||||
UpdateCurrentFPS();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateCurrentFPS()
|
||||
{
|
||||
struct obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
float targetFPS = (float)ovi.fps_num / (float)ovi.fps_den;
|
||||
|
||||
QString text = QString::asprintf("%.2f / %.2f FPS", obs_get_active_fps(), targetFPS);
|
||||
|
||||
statusWidget->ui->fpsCurrent->setText(text);
|
||||
statusWidget->ui->fpsCurrent->setMinimumWidth(statusWidget->ui->fpsCurrent->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("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
statusWidget->ui->streamTime->setText(text);
|
||||
if (streamOutput && !statusWidget->ui->streamTime->isEnabled())
|
||||
statusWidget->ui->streamTime->setDisabled(false);
|
||||
|
||||
if (reconnectTimeout > 0) {
|
||||
QString msg = QTStr("Basic.StatusBar.Reconnecting")
|
||||
.arg(QString::number(retries), QString::number(reconnectTimeout));
|
||||
showMessage(msg);
|
||||
disconnected = true;
|
||||
statusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);
|
||||
congestionArray.clear();
|
||||
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++;
|
||||
|
||||
if (recordOutput && !statusWidget->ui->recordTime->isEnabled())
|
||||
statusWidget->ui->recordTime->setDisabled(false);
|
||||
} else {
|
||||
statusWidget->ui->recordIcon->setPixmap(streamPauseIconToggle ? recordingPauseInactivePixmap
|
||||
: recordingPausePixmap);
|
||||
|
||||
streamPauseIconToggle = !streamPauseIconToggle;
|
||||
}
|
||||
|
||||
UpdateRecordTimeLabel();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateRecordTimeLabel()
|
||||
{
|
||||
int seconds = totalRecordSeconds % 60;
|
||||
int totalMinutes = totalRecordSeconds / 60;
|
||||
int minutes = totalMinutes % 60;
|
||||
int hours = totalMinutes / 60;
|
||||
|
||||
QString text = QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
if (os_atomic_load_bool(&recording_paused)) {
|
||||
text += QStringLiteral(" (PAUSED)");
|
||||
}
|
||||
|
||||
statusWidget->ui->recordTime->setText(text);
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateDroppedFrames()
|
||||
{
|
||||
if (!streamOutput)
|
||||
return;
|
||||
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
int totalDropped = obs_output_get_frames_dropped(output);
|
||||
int totalFrames = obs_output_get_total_frames(output);
|
||||
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));
|
||||
statusWidget->ui->droppedFrames->setText(text);
|
||||
|
||||
if (!statusWidget->ui->issuesFrame->isVisible())
|
||||
statusWidget->ui->issuesFrame->show();
|
||||
|
||||
/* ----------------------------------- *
|
||||
* calculate congestion color */
|
||||
|
||||
float congestion = obs_output_get_congestion(output);
|
||||
float avgCongestion = (congestion + lastCongestion) * 0.5f;
|
||||
if (avgCongestion < congestion)
|
||||
avgCongestion = congestion;
|
||||
if (avgCongestion > 1.0f)
|
||||
avgCongestion = 1.0f;
|
||||
|
||||
lastCongestion = congestion;
|
||||
|
||||
if (disconnected)
|
||||
return;
|
||||
|
||||
bool update = firstCongestionUpdate;
|
||||
float congestionOverTime = avgCongestion;
|
||||
|
||||
if (congestionArray.size() >= congestionUpdateSeconds) {
|
||||
congestionOverTime = accumulate(congestionArray.begin(), congestionArray.end(), 0.0f) /
|
||||
(float)congestionArray.size();
|
||||
congestionArray.clear();
|
||||
update = true;
|
||||
} else {
|
||||
congestionArray.emplace_back(avgCongestion);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
if (congestionOverTime <= excellentThreshold + EPSILON)
|
||||
statusWidget->ui->statusIcon->setPixmap(excellentPixmap);
|
||||
else if (congestionOverTime <= goodThreshold)
|
||||
statusWidget->ui->statusIcon->setPixmap(goodPixmap);
|
||||
else if (congestionOverTime <= mediocreThreshold)
|
||||
statusWidget->ui->statusIcon->setPixmap(mediocrePixmap);
|
||||
else if (congestionOverTime <= badThreshold)
|
||||
statusWidget->ui->statusIcon->setPixmap(badPixmap);
|
||||
|
||||
firstCongestionUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
delaySecTotal = obs_output_get_active_delay(output);
|
||||
UpdateDelayMsg();
|
||||
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::ReconnectClear()
|
||||
{
|
||||
retries = 0;
|
||||
reconnectTimeout = 0;
|
||||
seconds = -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) {
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
delaySecTotal = obs_output_get_active_delay(output);
|
||||
UpdateDelayMsg();
|
||||
disconnected = false;
|
||||
firstCongestionUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
|
||||
streamOutput = OBSGetWeakRef(output);
|
||||
|
||||
delaySecTotal = delaySecStarting = sec;
|
||||
UpdateDelayMsg();
|
||||
Activate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::StreamDelayStopping(int sec)
|
||||
{
|
||||
delaySecTotal = delaySecStopping = sec;
|
||||
UpdateDelayMsg();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::StreamStarted(obs_output_t *output)
|
||||
{
|
||||
streamOutput = OBSGetWeakRef(output);
|
||||
|
||||
streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect", OBSOutputReconnect, this);
|
||||
streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect_success", OBSOutputReconnectSuccess,
|
||||
this);
|
||||
|
||||
retries = 0;
|
||||
lastBytesSent = 0;
|
||||
lastBytesSentTime = os_gettime_ns();
|
||||
Activate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::StreamStopped()
|
||||
{
|
||||
if (streamOutput) {
|
||||
streamSigs.clear();
|
||||
|
||||
ReconnectClear();
|
||||
streamOutput = nullptr;
|
||||
clearMessage();
|
||||
Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingStarted(obs_output_t *output)
|
||||
{
|
||||
recordOutput = OBSGetWeakRef(output);
|
||||
Activate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingStopped()
|
||||
{
|
||||
recordOutput = nullptr;
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingPaused()
|
||||
{
|
||||
if (recordOutput) {
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingPausePixmap);
|
||||
streamPauseIconToggle = true;
|
||||
}
|
||||
|
||||
UpdateRecordTimeLabel();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingUnpaused()
|
||||
{
|
||||
if (recordOutput) {
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
|
||||
}
|
||||
|
||||
UpdateRecordTimeLabel();
|
||||
}
|
||||
|
||||
static QPixmap GetPixmap(const QString &filename)
|
||||
{
|
||||
QString path = obs_frontend_is_theme_dark() ? "theme:Dark/" : ":/res/images/";
|
||||
return QIcon(path + filename).pixmap(QSize(16, 16));
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateIcons()
|
||||
{
|
||||
disconnectedPixmap = GetPixmap("network-disconnected.svg");
|
||||
inactivePixmap = GetPixmap("network-inactive.svg");
|
||||
|
||||
streamingInactivePixmap = GetPixmap("streaming-inactive.svg");
|
||||
|
||||
recordingInactivePixmap = GetPixmap("recording-inactive.svg");
|
||||
recordingPauseInactivePixmap = GetPixmap("recording-pause-inactive.svg");
|
||||
|
||||
bool streaming = obs_frontend_streaming_active();
|
||||
|
||||
if (!streaming) {
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
} else {
|
||||
if (disconnected)
|
||||
statusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);
|
||||
}
|
||||
|
||||
bool recording = obs_frontend_recording_active();
|
||||
|
||||
if (!recording)
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::showMessage(const QString &message, int timeout)
|
||||
{
|
||||
messageTimer->stop();
|
||||
|
||||
statusWidget->ui->message->setText(message);
|
||||
|
||||
if (timeout)
|
||||
messageTimer->start(timeout);
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::clearMessage()
|
||||
{
|
||||
statusWidget->ui->message->setText("");
|
||||
}
|
119
frontend/widgets/StatusBarWidget.hpp
Normal file
119
frontend/widgets/StatusBarWidget.hpp
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <QStatusBar>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
#include <obs.h>
|
||||
#include <memory>
|
||||
|
||||
class Ui_StatusBarWidget;
|
||||
|
||||
class StatusBarWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
friend class OBSBasicStatusBar;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui_StatusBarWidget> ui;
|
||||
|
||||
public:
|
||||
StatusBarWidget(QWidget *parent = nullptr);
|
||||
~StatusBarWidget();
|
||||
};
|
||||
|
||||
class OBSBasicStatusBar : public QStatusBar {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
StatusBarWidget *statusWidget = nullptr;
|
||||
|
||||
OBSWeakOutputAutoRelease streamOutput;
|
||||
std::vector<OBSSignal> streamSigs;
|
||||
OBSWeakOutputAutoRelease recordOutput;
|
||||
bool active = false;
|
||||
bool overloadedNotify = true;
|
||||
bool streamPauseIconToggle = false;
|
||||
bool disconnected = false;
|
||||
bool firstCongestionUpdate = false;
|
||||
|
||||
std::vector<float> congestionArray;
|
||||
|
||||
int retries = 0;
|
||||
int totalStreamSeconds = 0;
|
||||
int totalRecordSeconds = 0;
|
||||
|
||||
int reconnectTimeout = 0;
|
||||
|
||||
int delaySecTotal = 0;
|
||||
int delaySecStarting = 0;
|
||||
int delaySecStopping = 0;
|
||||
|
||||
int startSkippedFrameCount = 0;
|
||||
int startTotalFrameCount = 0;
|
||||
int lastSkippedFrameCount = 0;
|
||||
|
||||
int seconds = 0;
|
||||
uint64_t lastBytesSent = 0;
|
||||
uint64_t lastBytesSentTime = 0;
|
||||
|
||||
QPixmap excellentPixmap;
|
||||
QPixmap goodPixmap;
|
||||
QPixmap mediocrePixmap;
|
||||
QPixmap badPixmap;
|
||||
QPixmap disconnectedPixmap;
|
||||
QPixmap inactivePixmap;
|
||||
|
||||
QPixmap recordingActivePixmap;
|
||||
QPixmap recordingPausePixmap;
|
||||
QPixmap recordingPauseInactivePixmap;
|
||||
QPixmap recordingInactivePixmap;
|
||||
QPixmap streamingActivePixmap;
|
||||
QPixmap streamingInactivePixmap;
|
||||
|
||||
float lastCongestion = 0.0f;
|
||||
|
||||
QPointer<QTimer> refreshTimer;
|
||||
QPointer<QTimer> messageTimer;
|
||||
|
||||
obs_output_t *GetOutput();
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
|
||||
void UpdateDelayMsg();
|
||||
void UpdateBandwidth();
|
||||
void UpdateStreamTime();
|
||||
void UpdateRecordTime();
|
||||
void UpdateRecordTimeLabel();
|
||||
void UpdateDroppedFrames();
|
||||
|
||||
static void OBSOutputReconnect(void *data, calldata_t *params);
|
||||
static void OBSOutputReconnectSuccess(void *data, calldata_t *params);
|
||||
|
||||
public slots:
|
||||
void UpdateCPUUsage();
|
||||
|
||||
void clearMessage();
|
||||
void showMessage(const QString &message, int timeout = 0);
|
||||
|
||||
private slots:
|
||||
void Reconnect(int seconds);
|
||||
void ReconnectSuccess();
|
||||
void UpdateStatusBar();
|
||||
void UpdateCurrentFPS();
|
||||
void UpdateIcons();
|
||||
|
||||
public:
|
||||
OBSBasicStatusBar(QWidget *parent);
|
||||
|
||||
void StreamDelayStarting(int sec);
|
||||
void StreamDelayStopping(int sec);
|
||||
void StreamStarted(obs_output_t *output);
|
||||
void StreamStopped();
|
||||
void RecordingStarted(obs_output_t *output);
|
||||
void RecordingStopped();
|
||||
void RecordingPaused();
|
||||
void RecordingUnpaused();
|
||||
|
||||
void ReconnectClear();
|
||||
};
|
1507
frontend/widgets/VolControl.cpp
Normal file
1507
frontend/widgets/VolControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/widgets/VolControl.hpp
Normal file
341
frontend/widgets/VolControl.hpp
Normal file
@ -0,0 +1,341 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
1507
frontend/widgets/VolumeAccessibleInterface.cpp
Normal file
1507
frontend/widgets/VolumeAccessibleInterface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/widgets/VolumeAccessibleInterface.hpp
Normal file
341
frontend/widgets/VolumeAccessibleInterface.hpp
Normal file
@ -0,0 +1,341 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
1507
frontend/widgets/VolumeMeter.cpp
Normal file
1507
frontend/widgets/VolumeMeter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/widgets/VolumeMeter.hpp
Normal file
341
frontend/widgets/VolumeMeter.hpp
Normal file
@ -0,0 +1,341 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user