diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
index 80954326b..455dca71c 100644
--- a/UI/data/locale/en-US.ini
+++ b/UI/data/locale/en-US.ini
@@ -1044,6 +1044,11 @@ Basic.Settings.Audio.EnablePushToTalk="Enable Push-to-talk"
Basic.Settings.Audio.PushToTalkDelay="Push-to-talk delay"
Basic.Settings.Audio.UnknownAudioDevice="[Device not connected or not available]"
Basic.Settings.Audio.Disabled="Disabled"
+Basic.Settings.Audio.LowLatencyBufferingMode="Low Latency Audio Buffering Mode (For Decklink/NDI outputs)"
+Basic.Settings.Audio.LowLatencyBufferingWarning.Enabled="WARNING: Low latency audio buffering is enabled."
+Basic.Settings.Audio.LowLatencyBufferingWarning="Low latency audio buffering mode may cause audio to glitch or stop playing from some sources."
+Basic.Settings.Audio.LowLatencyBufferingWarning.Title="Enable low latency audio buffering mode?"
+Basic.Settings.Audio.LowLatencyBufferingWarning.Confirm="Are you sure you want to enable low latency audio buffering mode?"
# basic mode 'advanced' settings
Basic.Settings.Advanced="Advanced"
diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui
index c7c480cd1..a2cd2f604 100644
--- a/UI/forms/OBSBasicSettings.ui
+++ b/UI/forms/OBSBasicSettings.ui
@@ -151,8 +151,8 @@
0
0
- 806
- 1181
+ 803
+ 1226
@@ -1379,7 +1379,7 @@
0
0
820
- 679
+ 676
@@ -3972,7 +3972,7 @@
0
0
820
- 641
+ 632
@@ -4372,6 +4372,13 @@
+ -
+
+
+ Basic.Settings.Audio.LowLatencyBufferingMode
+
+
+
@@ -4904,8 +4911,8 @@
0
0
- 609
- 870
+ 713
+ 923
@@ -5925,7 +5932,7 @@
peakMeterType
monitoringDevice
disableAudioDucking
- baseResolution
+ lowLatencyBuffering
outputResolution
downscaleFilter
fpsType
@@ -5960,6 +5967,18 @@
enableLowLatencyMode
browserHWAccel
hotkeyFocusType
+ hideOBSFromCapture
+ closeProjectors
+ moreInfoButton
+ ignoreRecommended
+ useStreamKeyAdv
+ baseResolution
+ hotkeyFilterSearch
+ hotkeyFilterInput
+ hotkeyFilterReset
+ hotkeyScrollArea
+ sdrWhiteLevel
+ hdrNominalPeakLevel
diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp
index 712a1432f..f8155dc25 100644
--- a/UI/window-basic-main.cpp
+++ b/UI/window-basic-main.cpp
@@ -4409,7 +4409,7 @@ bool OBSBasic::ResetAudio()
{
ProfileScope("OBSBasic::ResetAudio");
- struct obs_audio_info ai;
+ struct obs_audio_info2 ai = {};
ai.samples_per_sec =
config_get_uint(basicConfig, "Audio", "SampleRate");
@@ -4431,7 +4431,14 @@ bool OBSBasic::ResetAudio()
else
ai.speakers = SPEAKERS_STEREO;
- return obs_reset_audio(&ai);
+ bool lowLatencyAudioBuffering = config_get_bool(
+ GetGlobalConfig(), "Audio", "LowLatencyAudioBuffering");
+ if (lowLatencyAudioBuffering) {
+ ai.max_buffering_ms = 20;
+ ai.fixed_buffering = true;
+ }
+
+ return obs_reset_audio2(&ai);
}
extern char *get_new_source_name(const char *name, const char *format);
diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
index 5122bd06c..aedefa216 100644
--- a/UI/window-basic-settings.cpp
+++ b/UI/window-basic-settings.cpp
@@ -760,6 +760,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
SLOT(SurroundWarning(int)));
connect(ui->channelSetup, SIGNAL(currentIndexChanged(int)), this,
SLOT(SpeakerLayoutChanged(int)));
+ connect(ui->lowLatencyBuffering, SIGNAL(clicked(bool)), this,
+ SLOT(LowLatencyBufferingChanged(bool)));
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)), this,
SLOT(SimpleRecordingQualityChanged()));
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)), this,
@@ -926,6 +928,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
channelIndex = ui->channelSetup->currentIndex();
sampleRateIndex = ui->sampleRate->currentIndex();
+ llBufferingEnabled = ui->lowLatencyBuffering->isChecked();
QRegularExpression rx("\\d{1,5}x\\d{1,5}");
QValidator *validator = new QRegularExpressionValidator(rx, this);
@@ -934,6 +937,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
connect(ui->useStreamKeyAdv, SIGNAL(clicked()), this,
SLOT(UseStreamKeyAdvClicked()));
+
+ UpdateAudioWarnings();
}
OBSBasicSettings::~OBSBasicSettings()
@@ -2536,6 +2541,8 @@ void OBSBasicSettings::LoadAudioSettings()
config_get_double(main->Config(), "Audio", "MeterDecayRate");
uint32_t peakMeterTypeIdx =
config_get_uint(main->Config(), "Audio", "PeakMeterType");
+ bool enableLLAudioBuffering = config_get_bool(
+ GetGlobalConfig(), "Audio", "LowLatencyAudioBuffering");
loading = true;
@@ -2572,6 +2579,7 @@ void OBSBasicSettings::LoadAudioSettings()
ui->meterDecayRate->setCurrentIndex(0);
ui->peakMeterType->setCurrentIndex(peakMeterTypeIdx);
+ ui->lowLatencyBuffering->setChecked(enableLLAudioBuffering);
LoadAudioDevices();
LoadAudioSources();
@@ -3754,6 +3762,14 @@ void OBSBasicSettings::SaveAudioSettings()
main->UpdateVolumeControlsPeakMeterType();
}
+ if (WidgetChanged(ui->lowLatencyBuffering)) {
+ bool enableLLAudioBuffering =
+ ui->lowLatencyBuffering->isChecked();
+ config_set_bool(GetGlobalConfig(), "Audio",
+ "LowLatencyAudioBuffering",
+ enableLLAudioBuffering);
+ }
+
for (auto &audioSource : audioSources) {
auto source = OBSGetStrongRef(get<0>(audioSource));
if (!source)
@@ -4287,9 +4303,12 @@ void OBSBasicSettings::AudioChangedRestart()
if (!loading) {
int currentChannelIndex = ui->channelSetup->currentIndex();
int currentSampleRateIndex = ui->sampleRate->currentIndex();
+ bool currentLLAudioBufVal =
+ ui->lowLatencyBuffering->isChecked();
if (currentChannelIndex != channelIndex ||
- currentSampleRateIndex != sampleRateIndex) {
+ currentSampleRateIndex != sampleRateIndex ||
+ currentLLAudioBufVal != llBufferingEnabled) {
audioChanged = true;
ui->audioMsg->setText(
QTStr("Basic.Settings.ProgramRestart"));
@@ -4318,13 +4337,9 @@ void OBSBasicSettings::SpeakerLayoutChanged(int idx)
bool surround = IsSurround(speakerLayout.c_str());
if (surround) {
- QString warning = QTStr(MULTI_CHANNEL_WARNING ".Enabled") +
- QStringLiteral("\n\n") +
- QTStr(MULTI_CHANNEL_WARNING);
/*
* Display all bitrates
*/
- ui->audioMsg_2->setText(warning);
PopulateAACBitrates(
{ui->simpleOutputABitrate, ui->advOutTrack1Bitrate,
ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,
@@ -4335,7 +4350,6 @@ void OBSBasicSettings::SpeakerLayoutChanged(int idx)
* Reset audio bitrate for simple and adv mode, update list of
* bitrates and save setting.
*/
- ui->audioMsg_2->setText(QString());
RestrictResetBitrates(
{ui->simpleOutputABitrate, ui->advOutTrack1Bitrate,
ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,
@@ -4351,6 +4365,8 @@ void OBSBasicSettings::SpeakerLayoutChanged(int idx)
SaveCombo(ui->advOutTrack5Bitrate, "AdvOut", "Track5Bitrate");
SaveCombo(ui->advOutTrack6Bitrate, "AdvOut", "Track6Bitrate");
}
+
+ UpdateAudioWarnings();
}
void OBSBasicSettings::HideOBSWindowWarning(int state)
@@ -5257,6 +5273,56 @@ void OBSBasicSettings::SurroundWarning(int idx)
lastChannelSetupIdx = idx;
}
+#define LL_BUFFERING_WARNING "Basic.Settings.Audio.LowLatencyBufferingWarning"
+
+void OBSBasicSettings::UpdateAudioWarnings()
+{
+ QString speakerLayoutQstr = ui->channelSetup->currentText();
+ bool surround = IsSurround(QT_TO_UTF8(speakerLayoutQstr));
+ bool lowBufferingActive = ui->lowLatencyBuffering->isChecked();
+
+ QString text;
+
+ if (surround) {
+ text = QTStr(MULTI_CHANNEL_WARNING ".Enabled") +
+ QStringLiteral("\n\n") + QTStr(MULTI_CHANNEL_WARNING);
+ }
+
+ if (lowBufferingActive) {
+ if (!text.isEmpty())
+ text += QStringLiteral("\n\n");
+
+ text += QTStr(LL_BUFFERING_WARNING ".Enabled") +
+ QStringLiteral("\n\n") + QTStr(LL_BUFFERING_WARNING);
+ }
+
+ ui->audioMsg_2->setText(text);
+}
+
+void OBSBasicSettings::LowLatencyBufferingChanged(bool checked)
+{
+ if (checked) {
+ QString warningStr = QTStr(LL_BUFFERING_WARNING) +
+ QStringLiteral("\n\n") +
+ QTStr(LL_BUFFERING_WARNING ".Confirm");
+
+ auto button = OBSMessageBox::question(
+ this, QTStr(LL_BUFFERING_WARNING ".Title"), warningStr);
+
+ if (button == QMessageBox::No) {
+ QMetaObject::invokeMethod(ui->lowLatencyBuffering,
+ "setChecked",
+ Qt::QueuedConnection,
+ Q_ARG(bool, false));
+ return;
+ }
+ }
+
+ QMetaObject::invokeMethod(this, "UpdateAudioWarnings",
+ Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "AudioChangedRestart");
+}
+
void OBSBasicSettings::SimpleRecordingQualityLosslessWarning(int idx)
{
if (idx == lastSimpleRecQualityIdx || idx == -1)
diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp
index b4bc43583..416942e0f 100644
--- a/UI/window-basic-settings.hpp
+++ b/UI/window-basic-settings.hpp
@@ -120,6 +120,7 @@ private:
std::string savedTheme;
int sampleRateIndex = 0;
int channelIndex = 0;
+ bool llBufferingEnabled = false;
int lastSimpleRecQualityIdx = 0;
int lastServiceIdx = -1;
@@ -379,6 +380,8 @@ private slots:
void ReloadAudioSources();
void SurroundWarning(int idx);
void SpeakerLayoutChanged(int idx);
+ void LowLatencyBufferingChanged(bool checked);
+ void UpdateAudioWarnings();
void OutputsChanged();
void Stream1Changed();
void VideoChanged();