diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui
index a14e29b51..5edee9bc8 100644
--- a/UI/forms/OBSBasicSettings.ui
+++ b/UI/forms/OBSBasicSettings.ui
@@ -2432,73 +2432,142 @@
-
-
+
0
0
-
-
- 0
+
+ 0
+
+
+
+
+ 0
+ 0
+
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- 1
-
-
- true
-
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ 1
+
+
+ true
+
+
+
+ -
+
+
+ 2
+
+
+
+ -
+
+
+ 3
+
+
- -
-
-
- 2
+
-
+
+
+ 4
+
+
+
+ -
+
+
+ 5
+
+
+
+ -
+
+
+ 6
+
+
+
+
+
+
+
+
+ 0
-
-
- -
-
-
- 3
+
+ 0
-
-
- -
-
-
- 4
+
+ 0
-
-
- -
-
-
- 5
+
+ 0
-
-
- -
-
-
- 6
-
-
-
-
-
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ 2
+
+
+
+ -
+
+
+ 3
+
+
+
+ -
+
+
+ 4
+
+
+
+ -
+
+
+ 5
+
+
+
+ -
+
+
+ 6
+
+
+
+
+
+
-
@@ -7861,6 +7930,12 @@
advOutTrack4
advOutTrack5
advOutTrack6
+ advOutMultiTrack1
+ advOutMultiTrack2
+ advOutMultiTrack3
+ advOutMultiTrack4
+ advOutMultiTrack5
+ advOutMultiTrack6
advOutEncoder
advOutUseRescale
advOutRescale
diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp
index c17b0d744..8e3be9ceb 100644
--- a/UI/window-basic-main-outputs.cpp
+++ b/UI/window-basic-main-outputs.cpp
@@ -19,6 +19,8 @@ volatile bool virtualcam_active = false;
#define FTL_PROTOCOL "ftl"
#define RTMP_PROTOCOL "rtmp"
+#define SRT_PROTOCOL "srt"
+#define RIST_PROTOCOL "rist"
static void OBSStreamStarting(void *data, calldata_t *params)
{
@@ -1473,6 +1475,7 @@ bool SimpleOutput::ReplayBufferActive() const
struct AdvancedOutput : BasicOutputHandler {
OBSEncoder streamAudioEnc;
OBSEncoder streamArchiveEnc;
+ OBSEncoder streamTrack[MAX_AUDIO_MIXES];
OBSEncoder recordTrack[MAX_AUDIO_MIXES];
OBSEncoder videoStreaming;
OBSEncoder videoRecording;
@@ -1508,6 +1511,7 @@ struct AdvancedOutput : BasicOutputHandler {
virtual bool StreamingActive() const override;
virtual bool RecordingActive() const override;
virtual bool ReplayBufferActive() const override;
+ bool allowsMultiTrack();
};
static OBSData GetDataFromJsonFile(const char *jsonFile)
@@ -1669,14 +1673,25 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
}
obs_encoder_release(recordTrack[i]);
+
+ snprintf(name, sizeof(name), "adv_stream_audio_%d", i);
+ streamTrack[i] = obs_audio_encoder_create(
+ streamAudioEncoder, name, nullptr, i, nullptr);
+
+ if (!streamTrack[i]) {
+ throw "Failed to create streaming audio encoders "
+ "(advanced output)";
+ }
+
+ obs_encoder_release(streamTrack[i]);
}
std::string id;
- int streamTrack =
+ int streamTrackIndex =
config_get_int(main->Config(), "AdvOut", "TrackIndex") - 1;
streamAudioEnc = obs_audio_encoder_create(streamAudioEncoder,
"adv_stream_audio", nullptr,
- streamTrack, nullptr);
+ streamTrackIndex, nullptr);
if (!streamAudioEnc)
throw "Failed to create streaming audio encoder "
"(advanced output)";
@@ -1782,13 +1797,28 @@ static inline bool ServiceSupportsVodTrack(const char *service)
return false;
}
+inline bool AdvancedOutput::allowsMultiTrack()
+{
+ const char *protocol = nullptr;
+ obs_service_t *service_obj = main->GetService();
+ protocol = obs_service_get_protocol(service_obj);
+ if (!protocol)
+ return false;
+ return astrcmpi_n(protocol, SRT_PROTOCOL, strlen(SRT_PROTOCOL)) == 0 ||
+ astrcmpi_n(protocol, RIST_PROTOCOL, strlen(RIST_PROTOCOL)) == 0;
+}
+
inline void AdvancedOutput::SetupStreaming()
{
bool rescale = config_get_bool(main->Config(), "AdvOut", "Rescale");
const char *rescaleRes =
config_get_string(main->Config(), "AdvOut", "RescaleRes");
+ int multiTrackAudioMixes = config_get_int(main->Config(), "AdvOut",
+ "StreamMultiTrackAudioMixes");
unsigned int cx = 0;
unsigned int cy = 0;
+ int idx = 0;
+ bool is_multitrack_output = allowsMultiTrack();
if (rescale && rescaleRes && *rescaleRes) {
if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) {
@@ -1797,7 +1827,18 @@ inline void AdvancedOutput::SetupStreaming()
}
}
- obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);
+ if (!is_multitrack_output) {
+ obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);
+ } else {
+ for (int i = 0; i < MAX_AUDIO_MIXES; i++) {
+ if ((multiTrackAudioMixes & (1 << i)) != 0) {
+ obs_output_set_audio_encoder(
+ streamOutput, streamTrack[i], idx);
+ idx++;
+ }
+ }
+ }
+
obs_encoder_set_scaled_size(videoStreaming, cx, cy);
const char *id = obs_service_get_id(main->GetService());
@@ -1988,6 +2029,9 @@ inline void AdvancedOutput::UpdateAudioSettings()
config_get_string(main->Config(), "AdvOut", "AudioEncoder");
const char *recAudioEncoder =
config_get_string(main->Config(), "AdvOut", "RecAudioEncoder");
+
+ bool is_multitrack_output = allowsMultiTrack();
+
OBSDataAutoRelease settings[MAX_AUDIO_MIXES];
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
@@ -2000,6 +2044,7 @@ inline void AdvancedOutput::UpdateAudioSettings()
string def_name = "Track";
def_name += to_string((int)i + 1);
SetEncoderName(recordTrack[i], name, def_name.c_str());
+ SetEncoderName(streamTrack[i], name, def_name.c_str());
}
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
@@ -2013,24 +2058,31 @@ inline void AdvancedOutput::UpdateAudioSettings()
obs_data_set_int(settings[i], "bitrate",
GetAudioBitrate(i, audioEncoder));
- if (track == streamTrackIndex || track == vodTrackIndex) {
- if (applyServiceSettings) {
- int bitrate = (int)obs_data_get_int(settings[i],
- "bitrate");
- obs_service_apply_encoder_settings(
- main->GetService(), nullptr,
- settings[i]);
+ if (!is_multitrack_output) {
+ if (track == streamTrackIndex ||
+ track == vodTrackIndex) {
+ if (applyServiceSettings) {
+ int bitrate = (int)obs_data_get_int(
+ settings[i], "bitrate");
+ obs_service_apply_encoder_settings(
+ main->GetService(), nullptr,
+ settings[i]);
- if (!enforceBitrate)
- obs_data_set_int(settings[i], "bitrate",
- bitrate);
+ if (!enforceBitrate)
+ obs_data_set_int(settings[i],
+ "bitrate",
+ bitrate);
+ }
}
- }
- if (track == streamTrackIndex)
- obs_encoder_update(streamAudioEnc, settings[i]);
- if (track == vodTrackIndex)
- obs_encoder_update(streamArchiveEnc, settings[i]);
+ if (track == streamTrackIndex)
+ obs_encoder_update(streamAudioEnc, settings[i]);
+ if (track == vodTrackIndex)
+ obs_encoder_update(streamArchiveEnc,
+ settings[i]);
+ } else {
+ obs_encoder_update(streamTrack[i], settings[i]);
+ }
}
}
@@ -2039,8 +2091,10 @@ void AdvancedOutput::SetupOutputs()
obs_encoder_set_video(videoStreaming, obs_get_video());
if (videoRecording)
obs_encoder_set_video(videoRecording, obs_get_video());
- for (size_t i = 0; i < MAX_AUDIO_MIXES; i++)
+ for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
+ obs_encoder_set_audio(streamTrack[i], obs_get_audio());
obs_encoder_set_audio(recordTrack[i], obs_get_audio());
+ }
obs_encoder_set_audio(streamAudioEnc, obs_get_audio());
obs_encoder_set_audio(streamArchiveEnc, obs_get_audio());
@@ -2064,7 +2118,7 @@ int AdvancedOutput::GetAudioBitrate(size_t i, const char *id) const
inline void AdvancedOutput::SetupVodTrack(obs_service_t *service)
{
- int streamTrack =
+ int streamTrackIndex =
config_get_int(main->Config(), "AdvOut", "TrackIndex");
bool vodTrackEnabled =
config_get_bool(main->Config(), "AdvOut", "VodTrackEnabled");
@@ -2083,8 +2137,7 @@ inline void AdvancedOutput::SetupVodTrack(obs_service_t *service)
if (!ServiceSupportsVodTrack(service))
vodTrackEnabled = false;
}
-
- if (vodTrackEnabled && streamTrack != vodTrackIndex)
+ if (vodTrackEnabled && streamTrackIndex != vodTrackIndex)
obs_output_set_audio_encoder(streamOutput, streamArchiveEnc, 1);
else
clear_archive_encoder(streamOutput, ADV_ARCHIVE_NAME);
@@ -2092,6 +2145,12 @@ inline void AdvancedOutput::SetupVodTrack(obs_service_t *service)
bool AdvancedOutput::SetupStreaming(obs_service_t *service)
{
+ int multiTrackAudioMixes = config_get_int(main->Config(), "AdvOut",
+ "StreamMultiTrackAudioMixes");
+ int idx = 0;
+
+ bool is_multitrack_output = allowsMultiTrack();
+
if (!useStreamEncoder ||
(!ffmpegOutput && !obs_output_active(fileOutput))) {
UpdateStreamSettings();
@@ -2147,8 +2206,17 @@ bool AdvancedOutput::SetupStreaming(obs_service_t *service)
}
obs_output_set_video_encoder(streamOutput, videoStreaming);
- obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);
-
+ if (!is_multitrack_output) {
+ obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);
+ } else {
+ for (int i = 0; i < MAX_AUDIO_MIXES; i++) {
+ if ((multiTrackAudioMixes & (1 << i)) != 0) {
+ obs_output_set_audio_encoder(
+ streamOutput, streamTrack[i], idx);
+ idx++;
+ }
+ }
+ }
return true;
}
@@ -2177,6 +2245,15 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service)
bool enableDynBitrate =
config_get_bool(main->Config(), "Output", "DynamicBitrate");
+ bool is_rtmp = false;
+ obs_service_t *service_obj = main->GetService();
+ const char *protocol = obs_service_get_protocol(service_obj);
+ if (protocol) {
+ if (strncmp(protocol, RTMP_PROTOCOL, strlen(RTMP_PROTOCOL)) ==
+ 0)
+ is_rtmp = true;
+ }
+
OBSDataAutoRelease settings = obs_data_create();
obs_data_set_string(settings, "bind_ip", bindIP);
obs_data_set_string(settings, "ip_family", ipFamily);
@@ -2196,9 +2273,9 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service)
preserveDelay ? OBS_OUTPUT_DELAY_PRESERVE : 0);
obs_output_set_reconnect_settings(streamOutput, maxRetries, retryDelay);
-
- SetupVodTrack(service);
-
+ if (is_rtmp) {
+ SetupVodTrack(service);
+ }
if (obs_output_start(streamOutput)) {
return true;
}
diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp
index d823f9997..67fe6a14f 100644
--- a/UI/window-basic-main.cpp
+++ b/UI/window-basic-main.cpp
@@ -1650,7 +1650,8 @@ bool OBSBasic::InitBasicConfigDefaults()
config_set_default_uint(basicConfig, "AdvOut", "RecTracks", (1 << 0));
config_set_default_string(basicConfig, "AdvOut", "RecEncoder", "none");
config_set_default_uint(basicConfig, "AdvOut", "FLVTrack", 1);
-
+ config_set_default_uint(basicConfig, "AdvOut",
+ "StreamMultiTrackAudioMixes", 1);
config_set_default_bool(basicConfig, "AdvOut", "FFOutputToFile", true);
config_set_default_string(basicConfig, "AdvOut", "FFFilePath",
GetDefaultVideoSavePath().c_str());
diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp
index 27c44990c..6c683ddb7 100644
--- a/UI/window-basic-settings-stream.cpp
+++ b/UI/window-basic-settings-stream.cpp
@@ -255,6 +255,7 @@ void OBSBasicSettings::SaveStream1Settings()
main->SetService(newService);
main->SaveService();
+ LoadOutputSettings();
main->auth = auth;
if (!!main->auth) {
main->auth->LoadUI();
diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
index 60b8dd88f..47f475ea7 100644
--- a/UI/window-basic-settings.cpp
+++ b/UI/window-basic-settings.cpp
@@ -450,6 +450,12 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->advOutTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack5, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack6, CHECK_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutMultiTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutMultiTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutMultiTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutMultiTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutMultiTrack5, CHECK_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutMultiTrack6, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecType, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecPath, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutNoSpace, CHECK_CHANGED, OUTPUTS_CHANGED);
@@ -2022,13 +2028,24 @@ static inline QString makeFormatToolTip()
return html;
}
+#define RTMP_PROTOCOL "rtmp"
+#define SRT_PROTOCOL "srt"
+#define RIST_PROTOCOL "rist"
+
+inline bool allowsMultiTrack(const char *protocol)
+{
+ return astrcmpi_n(protocol, SRT_PROTOCOL, strlen(SRT_PROTOCOL)) == 0 ||
+ astrcmpi_n(protocol, RIST_PROTOCOL, strlen(RIST_PROTOCOL)) == 0;
+}
+
void OBSBasicSettings::LoadAdvOutputStreamingSettings()
{
bool rescale = config_get_bool(main->Config(), "AdvOut", "Rescale");
const char *rescaleRes =
config_get_string(main->Config(), "AdvOut", "RescaleRes");
int trackIndex = config_get_int(main->Config(), "AdvOut", "TrackIndex");
-
+ int audioMixes = config_get_int(main->Config(), "AdvOut",
+ "StreamMultiTrackAudioMixes");
ui->advOutUseRescale->setChecked(rescale);
ui->advOutRescale->setEnabled(rescale);
ui->advOutRescale->setCurrentText(rescaleRes);
@@ -2061,6 +2078,28 @@ void OBSBasicSettings::LoadAdvOutputStreamingSettings()
ui->advOutTrack6->setChecked(true);
break;
}
+ ui->advOutMultiTrack1->setChecked(audioMixes & (1 << 0));
+ ui->advOutMultiTrack2->setChecked(audioMixes & (1 << 1));
+ ui->advOutMultiTrack3->setChecked(audioMixes & (1 << 2));
+ ui->advOutMultiTrack4->setChecked(audioMixes & (1 << 3));
+ ui->advOutMultiTrack5->setChecked(audioMixes & (1 << 4));
+ ui->advOutMultiTrack6->setChecked(audioMixes & (1 << 5));
+
+ bool is_multitrack_output = false;
+ obs_service_t *service_obj = main->GetService();
+ const char *protocol = nullptr;
+ protocol = obs_service_get_protocol(service_obj);
+ if (protocol) {
+ is_multitrack_output = allowsMultiTrack(protocol);
+ }
+
+ if (is_multitrack_output) {
+ ui->advStreamTrackWidget->setCurrentWidget(
+ ui->streamMultiTracks);
+ } else {
+ ui->advStreamTrackWidget->setCurrentWidget(
+ ui->streamSingleTracks);
+ }
}
OBSPropertiesView *
@@ -3821,7 +3860,8 @@ void OBSBasicSettings::SaveOutputSettings()
SaveTrackIndex(main->Config(), "AdvOut", "TrackIndex", ui->advOutTrack1,
ui->advOutTrack2, ui->advOutTrack3, ui->advOutTrack4,
ui->advOutTrack5, ui->advOutTrack6);
-
+ config_set_int(main->Config(), "AdvOut", "StreamMultiTrackAudioMixes",
+ AdvOutGetStreamingSelectedAudioTracks());
config_set_string(main->Config(), "AdvOut", "RecType",
RecTypeFromIdx(ui->advOutRecType->currentIndex()));
@@ -4191,6 +4231,8 @@ bool OBSBasicSettings::QueryAllowedToClose()
QString format = ui->advOutRecFormat->currentData().toString();
if (AdvOutGetSelectedAudioTracks() == 0 && format != "flv")
invalidTracks = true;
+ if (AdvOutGetStreamingSelectedAudioTracks() == 0)
+ invalidTracks = true;
}
if (invalidEncoder) {
@@ -6135,6 +6177,17 @@ int OBSBasicSettings::AdvOutGetSelectedAudioTracks()
return tracks;
}
+int OBSBasicSettings::AdvOutGetStreamingSelectedAudioTracks()
+{
+ int tracks = (ui->advOutMultiTrack1->isChecked() ? (1 << 0) : 0) |
+ (ui->advOutMultiTrack2->isChecked() ? (1 << 1) : 0) |
+ (ui->advOutMultiTrack3->isChecked() ? (1 << 2) : 0) |
+ (ui->advOutMultiTrack4->isChecked() ? (1 << 3) : 0) |
+ (ui->advOutMultiTrack5->isChecked() ? (1 << 4) : 0) |
+ (ui->advOutMultiTrack6->isChecked() ? (1 << 5) : 0);
+ return tracks;
+}
+
/* Using setEditable(true) on a QComboBox when there's a custom style in use
* does not work properly, so instead completely recreate the widget, which
* seems to work fine. */
diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp
index c6aae1861..8b7042283 100644
--- a/UI/window-basic-settings.hpp
+++ b/UI/window-basic-settings.hpp
@@ -375,6 +375,7 @@ private:
int CurrentFLVTrack();
int SimpleOutGetSelectedAudioTracks();
int AdvOutGetSelectedAudioTracks();
+ int AdvOutGetStreamingSelectedAudioTracks();
OBSService GetStream1Service();