UI: Add low latency audio buffering mode to UI
This feature is meant to reduce maximum audio buffering and turn off dynamic buffering mode. This allows the lowest possible consistent latency for audio buffering, which is useful for the decklink and NDI outputs which cannot rely on audio timestamps for synchronization. This can have a negative effect of making audio segments (partial or in full) cut out. So audio glitching or audio loss can occur if this is enabled.master
parent
9025d92f7a
commit
0a218e06b7
|
@ -1044,6 +1044,11 @@ Basic.Settings.Audio.EnablePushToTalk="Enable Push-to-talk"
|
||||||
Basic.Settings.Audio.PushToTalkDelay="Push-to-talk delay"
|
Basic.Settings.Audio.PushToTalkDelay="Push-to-talk delay"
|
||||||
Basic.Settings.Audio.UnknownAudioDevice="[Device not connected or not available]"
|
Basic.Settings.Audio.UnknownAudioDevice="[Device not connected or not available]"
|
||||||
Basic.Settings.Audio.Disabled="Disabled"
|
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 mode 'advanced' settings
|
||||||
Basic.Settings.Advanced="Advanced"
|
Basic.Settings.Advanced="Advanced"
|
||||||
|
|
|
@ -151,8 +151,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>806</width>
|
<width>803</width>
|
||||||
<height>1181</height>
|
<height>1226</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||||
|
@ -1379,7 +1379,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>820</width>
|
<width>820</width>
|
||||||
<height>679</height>
|
<height>676</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_21">
|
<layout class="QVBoxLayout" name="verticalLayout_21">
|
||||||
|
@ -3972,7 +3972,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>820</width>
|
<width>820</width>
|
||||||
<height>641</height>
|
<height>632</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_50">
|
<layout class="QVBoxLayout" name="verticalLayout_50">
|
||||||
|
@ -4372,6 +4372,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="lowLatencyBuffering">
|
||||||
|
<property name="text">
|
||||||
|
<string>Basic.Settings.Audio.LowLatencyBufferingMode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -4904,8 +4911,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>609</width>
|
<width>713</width>
|
||||||
<height>870</height>
|
<height>923</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_23">
|
<layout class="QVBoxLayout" name="verticalLayout_23">
|
||||||
|
@ -5925,7 +5932,7 @@
|
||||||
<tabstop>peakMeterType</tabstop>
|
<tabstop>peakMeterType</tabstop>
|
||||||
<tabstop>monitoringDevice</tabstop>
|
<tabstop>monitoringDevice</tabstop>
|
||||||
<tabstop>disableAudioDucking</tabstop>
|
<tabstop>disableAudioDucking</tabstop>
|
||||||
<tabstop>baseResolution</tabstop>
|
<tabstop>lowLatencyBuffering</tabstop>
|
||||||
<tabstop>outputResolution</tabstop>
|
<tabstop>outputResolution</tabstop>
|
||||||
<tabstop>downscaleFilter</tabstop>
|
<tabstop>downscaleFilter</tabstop>
|
||||||
<tabstop>fpsType</tabstop>
|
<tabstop>fpsType</tabstop>
|
||||||
|
@ -5960,6 +5967,18 @@
|
||||||
<tabstop>enableLowLatencyMode</tabstop>
|
<tabstop>enableLowLatencyMode</tabstop>
|
||||||
<tabstop>browserHWAccel</tabstop>
|
<tabstop>browserHWAccel</tabstop>
|
||||||
<tabstop>hotkeyFocusType</tabstop>
|
<tabstop>hotkeyFocusType</tabstop>
|
||||||
|
<tabstop>hideOBSFromCapture</tabstop>
|
||||||
|
<tabstop>closeProjectors</tabstop>
|
||||||
|
<tabstop>moreInfoButton</tabstop>
|
||||||
|
<tabstop>ignoreRecommended</tabstop>
|
||||||
|
<tabstop>useStreamKeyAdv</tabstop>
|
||||||
|
<tabstop>baseResolution</tabstop>
|
||||||
|
<tabstop>hotkeyFilterSearch</tabstop>
|
||||||
|
<tabstop>hotkeyFilterInput</tabstop>
|
||||||
|
<tabstop>hotkeyFilterReset</tabstop>
|
||||||
|
<tabstop>hotkeyScrollArea</tabstop>
|
||||||
|
<tabstop>sdrWhiteLevel</tabstop>
|
||||||
|
<tabstop>hdrNominalPeakLevel</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="obs.qrc"/>
|
<include location="obs.qrc"/>
|
||||||
|
|
|
@ -4409,7 +4409,7 @@ bool OBSBasic::ResetAudio()
|
||||||
{
|
{
|
||||||
ProfileScope("OBSBasic::ResetAudio");
|
ProfileScope("OBSBasic::ResetAudio");
|
||||||
|
|
||||||
struct obs_audio_info ai;
|
struct obs_audio_info2 ai = {};
|
||||||
ai.samples_per_sec =
|
ai.samples_per_sec =
|
||||||
config_get_uint(basicConfig, "Audio", "SampleRate");
|
config_get_uint(basicConfig, "Audio", "SampleRate");
|
||||||
|
|
||||||
|
@ -4431,7 +4431,14 @@ bool OBSBasic::ResetAudio()
|
||||||
else
|
else
|
||||||
ai.speakers = SPEAKERS_STEREO;
|
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);
|
extern char *get_new_source_name(const char *name, const char *format);
|
||||||
|
|
|
@ -760,6 +760,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
|
||||||
SLOT(SurroundWarning(int)));
|
SLOT(SurroundWarning(int)));
|
||||||
connect(ui->channelSetup, SIGNAL(currentIndexChanged(int)), this,
|
connect(ui->channelSetup, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(SpeakerLayoutChanged(int)));
|
SLOT(SpeakerLayoutChanged(int)));
|
||||||
|
connect(ui->lowLatencyBuffering, SIGNAL(clicked(bool)), this,
|
||||||
|
SLOT(LowLatencyBufferingChanged(bool)));
|
||||||
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)), this,
|
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(SimpleRecordingQualityChanged()));
|
SLOT(SimpleRecordingQualityChanged()));
|
||||||
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)), this,
|
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
@ -926,6 +928,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
|
||||||
|
|
||||||
channelIndex = ui->channelSetup->currentIndex();
|
channelIndex = ui->channelSetup->currentIndex();
|
||||||
sampleRateIndex = ui->sampleRate->currentIndex();
|
sampleRateIndex = ui->sampleRate->currentIndex();
|
||||||
|
llBufferingEnabled = ui->lowLatencyBuffering->isChecked();
|
||||||
|
|
||||||
QRegularExpression rx("\\d{1,5}x\\d{1,5}");
|
QRegularExpression rx("\\d{1,5}x\\d{1,5}");
|
||||||
QValidator *validator = new QRegularExpressionValidator(rx, this);
|
QValidator *validator = new QRegularExpressionValidator(rx, this);
|
||||||
|
@ -934,6 +937,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
|
||||||
|
|
||||||
connect(ui->useStreamKeyAdv, SIGNAL(clicked()), this,
|
connect(ui->useStreamKeyAdv, SIGNAL(clicked()), this,
|
||||||
SLOT(UseStreamKeyAdvClicked()));
|
SLOT(UseStreamKeyAdvClicked()));
|
||||||
|
|
||||||
|
UpdateAudioWarnings();
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSBasicSettings::~OBSBasicSettings()
|
OBSBasicSettings::~OBSBasicSettings()
|
||||||
|
@ -2536,6 +2541,8 @@ void OBSBasicSettings::LoadAudioSettings()
|
||||||
config_get_double(main->Config(), "Audio", "MeterDecayRate");
|
config_get_double(main->Config(), "Audio", "MeterDecayRate");
|
||||||
uint32_t peakMeterTypeIdx =
|
uint32_t peakMeterTypeIdx =
|
||||||
config_get_uint(main->Config(), "Audio", "PeakMeterType");
|
config_get_uint(main->Config(), "Audio", "PeakMeterType");
|
||||||
|
bool enableLLAudioBuffering = config_get_bool(
|
||||||
|
GetGlobalConfig(), "Audio", "LowLatencyAudioBuffering");
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
|
@ -2572,6 +2579,7 @@ void OBSBasicSettings::LoadAudioSettings()
|
||||||
ui->meterDecayRate->setCurrentIndex(0);
|
ui->meterDecayRate->setCurrentIndex(0);
|
||||||
|
|
||||||
ui->peakMeterType->setCurrentIndex(peakMeterTypeIdx);
|
ui->peakMeterType->setCurrentIndex(peakMeterTypeIdx);
|
||||||
|
ui->lowLatencyBuffering->setChecked(enableLLAudioBuffering);
|
||||||
|
|
||||||
LoadAudioDevices();
|
LoadAudioDevices();
|
||||||
LoadAudioSources();
|
LoadAudioSources();
|
||||||
|
@ -3754,6 +3762,14 @@ void OBSBasicSettings::SaveAudioSettings()
|
||||||
main->UpdateVolumeControlsPeakMeterType();
|
main->UpdateVolumeControlsPeakMeterType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (WidgetChanged(ui->lowLatencyBuffering)) {
|
||||||
|
bool enableLLAudioBuffering =
|
||||||
|
ui->lowLatencyBuffering->isChecked();
|
||||||
|
config_set_bool(GetGlobalConfig(), "Audio",
|
||||||
|
"LowLatencyAudioBuffering",
|
||||||
|
enableLLAudioBuffering);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &audioSource : audioSources) {
|
for (auto &audioSource : audioSources) {
|
||||||
auto source = OBSGetStrongRef(get<0>(audioSource));
|
auto source = OBSGetStrongRef(get<0>(audioSource));
|
||||||
if (!source)
|
if (!source)
|
||||||
|
@ -4287,9 +4303,12 @@ void OBSBasicSettings::AudioChangedRestart()
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
int currentChannelIndex = ui->channelSetup->currentIndex();
|
int currentChannelIndex = ui->channelSetup->currentIndex();
|
||||||
int currentSampleRateIndex = ui->sampleRate->currentIndex();
|
int currentSampleRateIndex = ui->sampleRate->currentIndex();
|
||||||
|
bool currentLLAudioBufVal =
|
||||||
|
ui->lowLatencyBuffering->isChecked();
|
||||||
|
|
||||||
if (currentChannelIndex != channelIndex ||
|
if (currentChannelIndex != channelIndex ||
|
||||||
currentSampleRateIndex != sampleRateIndex) {
|
currentSampleRateIndex != sampleRateIndex ||
|
||||||
|
currentLLAudioBufVal != llBufferingEnabled) {
|
||||||
audioChanged = true;
|
audioChanged = true;
|
||||||
ui->audioMsg->setText(
|
ui->audioMsg->setText(
|
||||||
QTStr("Basic.Settings.ProgramRestart"));
|
QTStr("Basic.Settings.ProgramRestart"));
|
||||||
|
@ -4318,13 +4337,9 @@ void OBSBasicSettings::SpeakerLayoutChanged(int idx)
|
||||||
bool surround = IsSurround(speakerLayout.c_str());
|
bool surround = IsSurround(speakerLayout.c_str());
|
||||||
|
|
||||||
if (surround) {
|
if (surround) {
|
||||||
QString warning = QTStr(MULTI_CHANNEL_WARNING ".Enabled") +
|
|
||||||
QStringLiteral("\n\n") +
|
|
||||||
QTStr(MULTI_CHANNEL_WARNING);
|
|
||||||
/*
|
/*
|
||||||
* Display all bitrates
|
* Display all bitrates
|
||||||
*/
|
*/
|
||||||
ui->audioMsg_2->setText(warning);
|
|
||||||
PopulateAACBitrates(
|
PopulateAACBitrates(
|
||||||
{ui->simpleOutputABitrate, ui->advOutTrack1Bitrate,
|
{ui->simpleOutputABitrate, ui->advOutTrack1Bitrate,
|
||||||
ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,
|
ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,
|
||||||
|
@ -4335,7 +4350,6 @@ void OBSBasicSettings::SpeakerLayoutChanged(int idx)
|
||||||
* Reset audio bitrate for simple and adv mode, update list of
|
* Reset audio bitrate for simple and adv mode, update list of
|
||||||
* bitrates and save setting.
|
* bitrates and save setting.
|
||||||
*/
|
*/
|
||||||
ui->audioMsg_2->setText(QString());
|
|
||||||
RestrictResetBitrates(
|
RestrictResetBitrates(
|
||||||
{ui->simpleOutputABitrate, ui->advOutTrack1Bitrate,
|
{ui->simpleOutputABitrate, ui->advOutTrack1Bitrate,
|
||||||
ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,
|
ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,
|
||||||
|
@ -4351,6 +4365,8 @@ void OBSBasicSettings::SpeakerLayoutChanged(int idx)
|
||||||
SaveCombo(ui->advOutTrack5Bitrate, "AdvOut", "Track5Bitrate");
|
SaveCombo(ui->advOutTrack5Bitrate, "AdvOut", "Track5Bitrate");
|
||||||
SaveCombo(ui->advOutTrack6Bitrate, "AdvOut", "Track6Bitrate");
|
SaveCombo(ui->advOutTrack6Bitrate, "AdvOut", "Track6Bitrate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateAudioWarnings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBSBasicSettings::HideOBSWindowWarning(int state)
|
void OBSBasicSettings::HideOBSWindowWarning(int state)
|
||||||
|
@ -5257,6 +5273,56 @@ void OBSBasicSettings::SurroundWarning(int idx)
|
||||||
lastChannelSetupIdx = 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)
|
void OBSBasicSettings::SimpleRecordingQualityLosslessWarning(int idx)
|
||||||
{
|
{
|
||||||
if (idx == lastSimpleRecQualityIdx || idx == -1)
|
if (idx == lastSimpleRecQualityIdx || idx == -1)
|
||||||
|
|
|
@ -120,6 +120,7 @@ private:
|
||||||
std::string savedTheme;
|
std::string savedTheme;
|
||||||
int sampleRateIndex = 0;
|
int sampleRateIndex = 0;
|
||||||
int channelIndex = 0;
|
int channelIndex = 0;
|
||||||
|
bool llBufferingEnabled = false;
|
||||||
|
|
||||||
int lastSimpleRecQualityIdx = 0;
|
int lastSimpleRecQualityIdx = 0;
|
||||||
int lastServiceIdx = -1;
|
int lastServiceIdx = -1;
|
||||||
|
@ -379,6 +380,8 @@ private slots:
|
||||||
void ReloadAudioSources();
|
void ReloadAudioSources();
|
||||||
void SurroundWarning(int idx);
|
void SurroundWarning(int idx);
|
||||||
void SpeakerLayoutChanged(int idx);
|
void SpeakerLayoutChanged(int idx);
|
||||||
|
void LowLatencyBufferingChanged(bool checked);
|
||||||
|
void UpdateAudioWarnings();
|
||||||
void OutputsChanged();
|
void OutputsChanged();
|
||||||
void Stream1Changed();
|
void Stream1Changed();
|
||||||
void VideoChanged();
|
void VideoChanged();
|
||||||
|
|
Loading…
Reference in New Issue