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();