From d18b38e784a1e8ffef4ded32eece7bf6b7ef0589 Mon Sep 17 00:00:00 2001 From: pkv Date: Fri, 22 Jul 2022 15:28:26 +0200 Subject: [PATCH] UI: Enable multiple audio tracks in Simple Output recording This adds support for multiple audio tracks in Simple Output for recordings. This is enabled for all quality presets (including "Lossless") except "Same as Stream". This is also enabled for the Replay Buffer. An exception is made for flv recording format since it only allows a single audio track. The recorded track (and streaming track) is then Track 1 as before. Signed-off-by: pkv --- UI/data/locale/en-US.ini | 1 + UI/forms/OBSBasicSettings.ui | 221 ++++++++++++++++++++++++++++++- UI/window-basic-main-outputs.cpp | 103 +++++++++++++- UI/window-basic-main.cpp | 2 + UI/window-basic-settings.cpp | 60 ++++++++- 5 files changed, 378 insertions(+), 9 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index c1e29ea24..e04cab93a 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -994,6 +994,7 @@ Basic.Settings.Output.Simple.Codec.AAC="AAC" Basic.Settings.Output.Simple.Codec.AAC.Default="AAC (Default)" Basic.Settings.Output.Simple.Codec.Opus="Opus" Basic.Settings.Output.Simple.TwitchVodTrack="Twitch VOD Track (Uses Track 2)" +Basic.Settings.Output.Simple.RecAudioTrack="Audio Track" Basic.Settings.Output.Warn.EnforceResolutionFPS.Title="Incompatible Resolution/Framerate" Basic.Settings.Output.Warn.EnforceResolutionFPS.Msg="This streaming service does not support your current output resolution and/or framerate. They will be changed to the closest compatible value:\n\n%1\n\nDo you want to continue?" Basic.Settings.Output.Warn.EnforceResolutionFPS.Resolution="Resolution: %1" diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 7eb799700..b09ab68f3 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1939,6 +1939,217 @@ + + + Basic.Settings.Output.Simple.RecAudioTrack + + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + 1 + + + + + + + + 0 + 0 + + + + 2 + + + + + + + + 0 + 0 + + + + 3 + + + + + + + + 0 + 0 + + + + 4 + + + + + + + + 0 + 0 + + + + 5 + + + + + + + + 0 + 0 + + + + 6 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + 1 + + + true + + + + + + + + 0 + 0 + + + + 2 + + + + + + + + 0 + 0 + + + + 3 + + + + + + + + 0 + 0 + + + + 4 + + + + + + + + 0 + 0 + + + + 5 + + + + + + + + 0 + 0 + + + + 6 + + + + + + + + Basic.Settings.Output.CustomMuxerSettings @@ -1948,10 +2159,10 @@ - + - + Basic.Settings.Output.UseReplayBuffer @@ -7621,6 +7832,12 @@ simpleOutPreset simpleOutAdvanced simpleOutCustom + simpleOutRecTrack1 + simpleOutRecTrack2 + simpleOutRecTrack3 + simpleOutRecTrack4 + simpleOutRecTrack5 + simpleOutRecTrack6 simpleOutputPath simpleOutputBrowse simpleNoSpace diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index e569d26ed..4cdeb94bd 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -438,6 +438,7 @@ struct SimpleOutput : BasicOutputHandler { OBSEncoder audioRecording; OBSEncoder audioArchive; OBSEncoder videoRecording; + OBSEncoder audioTrack[MAX_AUDIO_MIXES]; string videoEncoder; string videoQuality; @@ -502,8 +503,6 @@ void SimpleOutput::LoadRecordingPreset_Lossless() obs_data_set_string(settings, "video_encoder", "utvideo"); obs_data_set_string(settings, "audio_encoder", "pcm_s16le"); - int aMixes = 1; - obs_output_set_mixers(fileOutput, aMixes); obs_output_update(fileOutput, settings); } @@ -611,6 +610,25 @@ void SimpleOutput::LoadRecordingPreset() if (!success) throw "Failed to create audio recording encoder " "(simple output)"; + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + char name[23]; + if (strcmp(audio_encoder, "opus") == 0) { + snprintf(name, sizeof name, + "simple_opus_recording%d", i); + success = CreateSimpleOpusEncoder( + audioTrack[i], GetAudioBitrate(), name, + i); + } else { + snprintf(name, sizeof name, + "simple_aac_recording%d", i); + success = CreateSimpleAACEncoder( + audioTrack[i], GetAudioBitrate(), name, + i); + } + if (!success) + throw "Failed to create multi-track audio recording encoder " + "(simple output)"; + } } } @@ -813,7 +831,23 @@ void SimpleOutput::UpdateRecordingAudioSettings() obs_data_set_int(settings, "bitrate", 192); obs_data_set_string(settings, "rate_control", "CBR"); - obs_encoder_update(audioRecording, settings); + int tracks = + config_get_int(main->Config(), "SimpleOutput", "RecTracks"); + const char *recFormat = + config_get_string(main->Config(), "SimpleOutput", "RecFormat"); + const char *quality = + config_get_string(main->Config(), "SimpleOutput", "RecQuality"); + bool flv = strcmp(recFormat, "flv") == 0; + + if (flv || strcmp(quality, "Stream") == 0) { + obs_encoder_update(audioRecording, settings); + } else { + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + if ((tracks & (1 << i)) != 0) { + obs_encoder_update(audioTrack[i], settings); + } + } + } } #define CROSS_DIST_CUTOFF 2000.0 @@ -987,6 +1021,11 @@ inline void SimpleOutput::SetupOutputs() obs_encoder_set_video(videoStreaming, obs_get_video()); obs_encoder_set_audio(audioStreaming, obs_get_audio()); obs_encoder_set_audio(audioArchive, obs_get_audio()); + int tracks = + config_get_int(main->Config(), "SimpleOutput", "RecTracks"); + const char *recFormat = + config_get_string(main->Config(), "SimpleOutput", "RecFormat"); + bool flv = strcmp(recFormat, "flv") == 0; if (usingRecordingPreset) { if (ffmpegOutput) { @@ -994,8 +1033,21 @@ inline void SimpleOutput::SetupOutputs() obs_get_audio()); } else { obs_encoder_set_video(videoRecording, obs_get_video()); - obs_encoder_set_audio(audioRecording, obs_get_audio()); + if (flv) { + obs_encoder_set_audio(audioRecording, + obs_get_audio()); + } else { + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + if ((tracks & (1 << i)) != 0) { + obs_encoder_set_audio( + audioTrack[i], + obs_get_audio()); + } + } + } } + } else { + obs_encoder_set_audio(audioRecording, obs_get_audio()); } } @@ -1175,6 +1227,16 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) void SimpleOutput::UpdateRecording() { + const char *recFormat = + config_get_string(main->Config(), "SimpleOutput", "RecFormat"); + bool flv = strcmp(recFormat, "flv") == 0; + int tracks = + config_get_int(main->Config(), "SimpleOutput", "RecTracks"); + int idx = 0; + int idx2 = 0; + const char *quality = + config_get_string(main->Config(), "SimpleOutput", "RecQuality"); + if (replayBufferActive || recordingActive) return; @@ -1190,11 +1252,33 @@ void SimpleOutput::UpdateRecording() if (!ffmpegOutput) { obs_output_set_video_encoder(fileOutput, videoRecording); - obs_output_set_audio_encoder(fileOutput, audioRecording, 0); + if (flv || strcmp(quality, "Stream") == 0) { + obs_output_set_audio_encoder(fileOutput, audioRecording, + 0); + } else { + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + if ((tracks & (1 << i)) != 0) { + obs_output_set_audio_encoder( + fileOutput, audioTrack[i], + idx++); + } + } + } } if (replayBuffer) { obs_output_set_video_encoder(replayBuffer, videoRecording); - obs_output_set_audio_encoder(replayBuffer, audioRecording, 0); + if (flv || strcmp(quality, "Stream") == 0) { + obs_output_set_audio_encoder(replayBuffer, + audioRecording, 0); + } else { + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + if ((tracks & (1 << i)) != 0) { + obs_output_set_audio_encoder( + replayBuffer, audioTrack[i], + idx2++); + } + } + } } recordingConfigured = true; @@ -1222,6 +1306,11 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer) config_get_int(main->Config(), "SimpleOutput", "RecRBTime"); int rbSize = config_get_int(main->Config(), "SimpleOutput", "RecRBSize"); + int tracks = + config_get_int(main->Config(), "SimpleOutput", "RecTracks"); + const char *recFormat = + config_get_string(main->Config(), "SimpleOutput", "RecFormat"); + bool flv = strcmp(recFormat, "flv") == 0; bool is_fragmented = strcmp(format, "fmp4") == 0 || strcmp(format, "fmov") == 0; @@ -1253,6 +1342,8 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer) f.c_str(), ffmpegOutput); obs_data_set_string(settings, ffmpegOutput ? "url" : "path", strPath.c_str()); + if (ffmpegOutput) + obs_output_set_mixers(fileOutput, tracks); } // Use fragmented MOV/MP4 if user has not already specified custom movflags diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 7cc94006c..d7ae3e94f 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1534,6 +1534,8 @@ bool OBSBasic::InitBasicConfigDefaults() "StreamAudioEncoder", "aac"); config_set_default_string(basicConfig, "SimpleOutput", "RecAudioEncoder", "aac"); + config_set_default_uint(basicConfig, "SimpleOutput", "RecTracks", + (1 << 0)); config_set_default_bool(basicConfig, "AdvOut", "ApplyServiceSettings", true); diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 6114e703b..a50bc39ec 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -493,6 +493,12 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->simpleOutRecQuality, COMBO_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutRecEncoder, COMBO_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutRecAEncoder, COMBO_CHANGED, OUTPUTS_CHANGED); + HookWidget(ui->simpleOutRecTrack1, CHECK_CHANGED, OUTPUTS_CHANGED); + HookWidget(ui->simpleOutRecTrack2, CHECK_CHANGED, OUTPUTS_CHANGED); + HookWidget(ui->simpleOutRecTrack3, CHECK_CHANGED, OUTPUTS_CHANGED); + HookWidget(ui->simpleOutRecTrack4, CHECK_CHANGED, OUTPUTS_CHANGED); + HookWidget(ui->simpleOutRecTrack5, CHECK_CHANGED, OUTPUTS_CHANGED); + HookWidget(ui->simpleOutRecTrack6, CHECK_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutMuxCustom, EDIT_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleReplayBuf, CHECK_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleRBSecMax, SCROLL_CHANGED, OUTPUTS_CHANGED); @@ -1946,6 +1952,15 @@ void OBSBasicSettings::LoadSimpleOutputSettings() config_get_int(main->Config(), "SimpleOutput", "RecRBTime"); int rbSize = config_get_int(main->Config(), "SimpleOutput", "RecRBSize"); + int tracks = + config_get_int(main->Config(), "SimpleOutput", "RecTracks"); + + ui->simpleOutRecTrack1->setChecked(tracks & (1 << 0)); + ui->simpleOutRecTrack2->setChecked(tracks & (1 << 1)); + ui->simpleOutRecTrack3->setChecked(tracks & (1 << 2)); + ui->simpleOutRecTrack4->setChecked(tracks & (1 << 3)); + ui->simpleOutRecTrack5->setChecked(tracks & (1 << 4)); + ui->simpleOutRecTrack6->setChecked(tracks & (1 << 5)); curPreset = preset; curQSVPreset = qsvPreset; @@ -3837,6 +3852,14 @@ void OBSBasicSettings::SaveOutputSettings() SaveCheckBox(ui->simpleReplayBuf, "SimpleOutput", "RecRB"); SaveSpinBox(ui->simpleRBSecMax, "SimpleOutput", "RecRBTime"); SaveSpinBox(ui->simpleRBMegsMax, "SimpleOutput", "RecRBSize"); + config_set_int( + main->Config(), "SimpleOutput", "RecTracks", + (ui->simpleOutRecTrack1->isChecked() ? (1 << 0) : 0) | + (ui->simpleOutRecTrack2->isChecked() ? (1 << 1) : 0) | + (ui->simpleOutRecTrack3->isChecked() ? (1 << 2) : 0) | + (ui->simpleOutRecTrack4->isChecked() ? (1 << 3) : 0) | + (ui->simpleOutRecTrack5->isChecked() ? (1 << 4) : 0) | + (ui->simpleOutRecTrack6->isChecked() ? (1 << 5) : 0)); curAdvStreamEncoder = GetComboData(ui->advOutEncoder); @@ -5433,12 +5456,31 @@ void OBSBasicSettings::SimpleReplayBufferChanged() bool replayBufferEnabled = ui->simpleReplayBuf->isChecked(); bool lossless = qual == "Lossless"; bool streamQuality = qual == "Stream"; + int abitrate = 0; ui->simpleRBMegsMax->setVisible(!streamQuality); ui->simpleRBMegsMaxLabel->setVisible(!streamQuality); + if (ui->simpleOutRecFormat->currentText().compare("flv") == 0 || + streamQuality) { + abitrate = ui->simpleOutputABitrate->currentText().toInt(); + } else { + int delta = ui->simpleOutputABitrate->currentText().toInt(); + if (ui->simpleOutRecTrack1->isChecked()) + abitrate += delta; + if (ui->simpleOutRecTrack2->isChecked()) + abitrate += delta; + if (ui->simpleOutRecTrack3->isChecked()) + abitrate += delta; + if (ui->simpleOutRecTrack4->isChecked()) + abitrate += delta; + if (ui->simpleOutRecTrack5->isChecked()) + abitrate += delta; + if (ui->simpleOutRecTrack6->isChecked()) + abitrate += delta; + } + int vbitrate = ui->simpleOutputVBitrate->value(); - int abitrate = ui->simpleOutputABitrate->currentText().toInt(); int seconds = ui->simpleRBSecMax->value(); // Set maximum to 75% of installed memory @@ -5790,6 +5832,22 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged() QTStr("Basic.Settings.Advanced.AutoRemux").arg("mp4")); } + if (qual == "Stream") { + ui->simpleRecTrackWidget->setCurrentWidget(ui->simpleFlvTracks); + ui->simpleFlvTracks->setEnabled(false); + } else if (qual == "Lossless") { + ui->simpleRecTrackWidget->setCurrentWidget(ui->simpleRecTracks); + } else { + if (format == "flv") { + ui->simpleRecTrackWidget->setCurrentWidget( + ui->simpleFlvTracks); + ui->simpleFlvTracks->setEnabled(false); + } else { + ui->simpleRecTrackWidget->setCurrentWidget( + ui->simpleRecTracks); + } + } + if (warning.isEmpty()) return;