mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-01-15 09:48:34 -05:00
libobs: Add surround sound audio support
(This commit also modifies the following modules: UI,
deps/media-playback, coreaudio-encoder, decklink, linux-alsa,
linux-pulseaudio, mac-capture, obs-ffmpeg, obs-filters, obs-libfdk,
obs-outputs, win-dshow, and win-wasapi)
Adds surround sound audio support to the core, core plugins, and user
interface.
Compatible streaming services: Twitch, FB 360 live
Compatible protocols: rtmp / mpeg-ts tcp udp
Compatible file formats: mkv mp4 ts (others untested)
Compatible codecs: ffmpeg aac, fdk_aac, CoreAudio aac,
opus, vorbis, pcm (others untested).
Tested streaming servers: wowza, nginx
HLS, mpeg-dash : surround passthrough
Html5 players tested with live surround:
videojs, mediaelement, viblast (hls+dash), hls.js
Decklink: on win32, swap channels order for 5.1 7.1
(due to different channel mapping on wav, mpeg, ffmpeg)
Audio filters: surround working.
Monitoring: surround working (win macOs linux (pulse-audio)).
VST: stereo plugins keep in general only the first two channels.
surround plugins should work (e.g. mcfx does).
OS: win, macOs, linux (alsa, pulse-audio).
Misc: larger audio bitrates unlocked to accommodate more channels
NB: mf-aac only supports mono and stereo + 5.1 on win 10
(not implemented due to lack of usefulness)
Closes jp9000/obs-studio#968
This commit is contained in:
@@ -243,6 +243,9 @@ static void PopulateAACBitrates(initializer_list<QComboBox*> boxes)
|
||||
}
|
||||
}
|
||||
|
||||
void RestrictResetBitrates(initializer_list<QComboBox*> boxes,
|
||||
int maxbitrate);
|
||||
|
||||
void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
|
||||
const char *slot)
|
||||
{
|
||||
@@ -256,6 +259,7 @@ void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
|
||||
#define CHECK_CHANGED SIGNAL(clicked(bool))
|
||||
#define SCROLL_CHANGED SIGNAL(valueChanged(int))
|
||||
#define DSCROLL_CHANGED SIGNAL(valueChanged(double))
|
||||
#define TOGGLE_CHANGED SIGNAL(toggled(bool))
|
||||
|
||||
#define GENERAL_CHANGED SLOT(GeneralChanged())
|
||||
#define STREAM1_CHANGED SLOT(Stream1Changed())
|
||||
@@ -587,6 +591,10 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
|
||||
FillAudioMonitoringDevices();
|
||||
#endif
|
||||
|
||||
connect(ui->channelSetup, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(SurroundWarning(int)));
|
||||
connect(ui->channelSetup, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(SpeakerLayoutChanged(int)));
|
||||
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(SimpleRecordingQualityChanged()));
|
||||
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)),
|
||||
@@ -1368,6 +1376,30 @@ void OBSBasicSettings::LoadVideoSettings()
|
||||
loading = false;
|
||||
}
|
||||
|
||||
static inline bool IsSurround(const char *speakers)
|
||||
{
|
||||
static const char *surroundLayouts[] = {
|
||||
"2.1",
|
||||
"4.0 Quad",
|
||||
"4.1",
|
||||
"5.1",
|
||||
"7.1",
|
||||
nullptr
|
||||
};
|
||||
|
||||
if (!speakers || !*speakers)
|
||||
return false;
|
||||
|
||||
const char **curLayout = surroundLayouts;
|
||||
for (; *curLayout; ++curLayout) {
|
||||
if (strcmp(*curLayout, speakers) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OBSBasicSettings::LoadSimpleOutputSettings()
|
||||
{
|
||||
const char *path = config_get_string(main->Config(), "SimpleOutput",
|
||||
@@ -1423,6 +1455,13 @@ void OBSBasicSettings::LoadSimpleOutputSettings()
|
||||
int idx = ui->simpleOutRecFormat->findText(format);
|
||||
ui->simpleOutRecFormat->setCurrentIndex(idx);
|
||||
|
||||
const char *speakers = config_get_string(main->Config(), "Audio",
|
||||
"ChannelSetup");
|
||||
|
||||
// restrict list of bitrates when multichannel is OFF
|
||||
if (!IsSurround(speakers))
|
||||
RestrictResetBitrates({ui->simpleOutputABitrate}, 320);
|
||||
|
||||
SetComboByName(ui->simpleOutputABitrate,
|
||||
std::to_string(audioBitrate).c_str());
|
||||
|
||||
@@ -1741,6 +1780,20 @@ void OBSBasicSettings::LoadAdvOutputAudioSettings()
|
||||
track5Bitrate = FindClosestAvailableAACBitrate(track5Bitrate);
|
||||
track6Bitrate = FindClosestAvailableAACBitrate(track6Bitrate);
|
||||
|
||||
// restrict list of bitrates when multichannel is OFF
|
||||
const char *speakers = config_get_string(main->Config(), "Audio",
|
||||
"ChannelSetup");
|
||||
|
||||
// restrict list of bitrates when multichannel is OFF
|
||||
if (!IsSurround(speakers)) {
|
||||
RestrictResetBitrates({ui->advOutTrack1Bitrate,
|
||||
ui->advOutTrack2Bitrate,
|
||||
ui->advOutTrack3Bitrate,
|
||||
ui->advOutTrack4Bitrate,
|
||||
ui->advOutTrack5Bitrate,
|
||||
ui->advOutTrack6Bitrate}, 320);
|
||||
}
|
||||
|
||||
SetComboByName(ui->advOutTrack1Bitrate,
|
||||
std::to_string(track1Bitrate).c_str());
|
||||
SetComboByName(ui->advOutTrack2Bitrate,
|
||||
@@ -2044,6 +2097,16 @@ void OBSBasicSettings::LoadAudioSettings()
|
||||
|
||||
if (strcmp(speakers, "Mono") == 0)
|
||||
ui->channelSetup->setCurrentIndex(0);
|
||||
else if (strcmp(speakers, "2.1") == 0)
|
||||
ui->channelSetup->setCurrentIndex(2);
|
||||
else if (strcmp(speakers, "4.0 Quad") == 0)
|
||||
ui->channelSetup->setCurrentIndex(3);
|
||||
else if (strcmp(speakers, "4.1") == 0)
|
||||
ui->channelSetup->setCurrentIndex(4);
|
||||
else if (strcmp(speakers, "5.1") == 0)
|
||||
ui->channelSetup->setCurrentIndex(5);
|
||||
else if (strcmp(speakers, "7.1") == 0)
|
||||
ui->channelSetup->setCurrentIndex(6);
|
||||
else
|
||||
ui->channelSetup->setCurrentIndex(1);
|
||||
|
||||
@@ -2918,7 +2981,34 @@ void OBSBasicSettings::SaveAudioSettings()
|
||||
QString sampleRateStr = ui->sampleRate->currentText();
|
||||
int channelSetupIdx = ui->channelSetup->currentIndex();
|
||||
|
||||
const char *channelSetup = (channelSetupIdx == 0) ? "Mono" : "Stereo";
|
||||
const char *channelSetup;
|
||||
switch (channelSetupIdx) {
|
||||
case 0:
|
||||
channelSetup = "Mono";
|
||||
break;
|
||||
case 1:
|
||||
channelSetup = "Stereo";
|
||||
break;
|
||||
case 2:
|
||||
channelSetup = "2.1";
|
||||
break;
|
||||
case 3:
|
||||
channelSetup = "4.0 Quad";
|
||||
break;
|
||||
case 4:
|
||||
channelSetup = "4.1";
|
||||
break;
|
||||
case 5:
|
||||
channelSetup = "5.1";
|
||||
break;
|
||||
case 6:
|
||||
channelSetup = "7.1";
|
||||
break;
|
||||
|
||||
default:
|
||||
channelSetup = "Stereo";
|
||||
break;
|
||||
}
|
||||
|
||||
int sampleRate = 44100;
|
||||
if (sampleRateStr == "48khz")
|
||||
@@ -3436,6 +3526,85 @@ void OBSBasicSettings::ReloadAudioSources()
|
||||
LoadAudioSources();
|
||||
}
|
||||
|
||||
#define MULTI_CHANNEL_WARNING "Basic.Settings.Audio.MultichannelWarning"
|
||||
|
||||
void OBSBasicSettings::SpeakerLayoutChanged(int idx)
|
||||
{
|
||||
QString speakerLayoutQstr = ui->channelSetup->itemText(idx);
|
||||
std::string speakerLayout = QT_TO_UTF8(speakerLayoutQstr);
|
||||
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,
|
||||
ui->advOutTrack4Bitrate,
|
||||
ui->advOutTrack5Bitrate,
|
||||
ui->advOutTrack6Bitrate});
|
||||
} else {
|
||||
/*
|
||||
* 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,
|
||||
ui->advOutTrack4Bitrate,
|
||||
ui->advOutTrack5Bitrate,
|
||||
ui->advOutTrack6Bitrate}, 320);
|
||||
|
||||
SaveCombo(ui->simpleOutputABitrate, "SimpleOutput", "ABitrate");
|
||||
SaveCombo(ui->advOutTrack1Bitrate, "AdvOut", "Track1Bitrate");
|
||||
SaveCombo(ui->advOutTrack2Bitrate, "AdvOut", "Track2Bitrate");
|
||||
SaveCombo(ui->advOutTrack3Bitrate, "AdvOut", "Track3Bitrate");
|
||||
SaveCombo(ui->advOutTrack4Bitrate, "AdvOut", "Track4Bitrate");
|
||||
SaveCombo(ui->advOutTrack5Bitrate, "AdvOut", "Track5Bitrate");
|
||||
SaveCombo(ui->advOutTrack6Bitrate, "AdvOut", "Track6Bitrate");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* resets current bitrate if too large and restricts the number of bitrates
|
||||
* displayed when multichannel OFF
|
||||
*/
|
||||
|
||||
void RestrictResetBitrates(initializer_list<QComboBox*> boxes, int maxbitrate)
|
||||
{
|
||||
for (auto box : boxes) {
|
||||
int idx = box->currentIndex();
|
||||
int max_bitrate = FindClosestAvailableAACBitrate(maxbitrate);
|
||||
int count = box->count();
|
||||
int max_idx = box->findText(QT_UTF8(std::to_string
|
||||
(max_bitrate).c_str()));
|
||||
|
||||
for (int i = (count - 1); i > max_idx; i--)
|
||||
box->removeItem(i);
|
||||
|
||||
if (idx > max_idx) {
|
||||
int default_bitrate = FindClosestAvailableAACBitrate(
|
||||
maxbitrate / 2);
|
||||
int default_idx = box->findText(QT_UTF8(std::to_string
|
||||
(default_bitrate).c_str()));
|
||||
|
||||
box->setCurrentIndex(default_idx);
|
||||
box->setProperty("changed", QVariant(true));
|
||||
} else {
|
||||
box->setCurrentIndex(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicSettings::VideoChangedRestart()
|
||||
{
|
||||
if (!loading) {
|
||||
@@ -4007,6 +4176,47 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged()
|
||||
ui->simpleOutInfoLayout->addWidget(simpleOutRecWarning);
|
||||
}
|
||||
|
||||
void OBSBasicSettings::SurroundWarning(int idx)
|
||||
{
|
||||
if (idx == lastChannelSetupIdx || idx == -1)
|
||||
return;
|
||||
|
||||
if (loading) {
|
||||
lastChannelSetupIdx = idx;
|
||||
return;
|
||||
}
|
||||
|
||||
QString speakerLayoutQstr = ui->channelSetup->itemText(idx);
|
||||
bool surround = IsSurround(QT_TO_UTF8(speakerLayoutQstr));
|
||||
|
||||
QString lastQstr = ui->channelSetup->itemText(lastChannelSetupIdx);
|
||||
bool wasSurround = IsSurround(QT_TO_UTF8(lastQstr));
|
||||
|
||||
if (surround && !wasSurround) {
|
||||
QMessageBox::StandardButton button;
|
||||
|
||||
QString warningString =
|
||||
QTStr("Basic.Settings.ProgramRestart") +
|
||||
QStringLiteral("\n\n") +
|
||||
QTStr(MULTI_CHANNEL_WARNING) +
|
||||
QStringLiteral("\n\n") +
|
||||
QTStr(MULTI_CHANNEL_WARNING ".Confirm");
|
||||
|
||||
button = OBSMessageBox::question(this,
|
||||
QTStr(MULTI_CHANNEL_WARNING ".Title"),
|
||||
warningString);
|
||||
|
||||
if (button == QMessageBox::No) {
|
||||
QMetaObject::invokeMethod(ui->channelSetup,
|
||||
"setCurrentIndex", Qt::QueuedConnection,
|
||||
Q_ARG(int, lastChannelSetupIdx));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lastChannelSetupIdx = idx;
|
||||
}
|
||||
|
||||
void OBSBasicSettings::SimpleRecordingQualityLosslessWarning(int idx)
|
||||
{
|
||||
if (idx == lastSimpleRecQualityIdx || idx == -1)
|
||||
|
||||
Reference in New Issue
Block a user