From 0a884d2fd55f170f9acc45fab42a2cdfa33d89b9 Mon Sep 17 00:00:00 2001 From: pkv Date: Fri, 22 Jul 2022 15:28:26 +0200 Subject: [PATCH 1/2] 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 f08a95b1db27c2..f609dbca13d855 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -991,6 +991,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 7eb79970070c94..b09ab68f38feb9 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 d6e2624cfc5d58..21f8771df7586d 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 4e81e78a09b518..9c08ca54148568 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1507,6 +1507,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 e4fb8964e7baa3..468b1d14036505 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); @@ -1925,6 +1931,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; @@ -3817,6 +3832,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); @@ -5318,12 +5341,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 @@ -5540,6 +5582,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; From c5173d11342110970f34979ec830c715cf0a9173 Mon Sep 17 00:00:00 2001 From: pkv Date: Sat, 25 Mar 2023 20:28:05 +0100 Subject: [PATCH 2/2] UI: Move tracks in Advanced Standard Recording Per request of our UI design chief, the tracks from Standard Recording (Advanced Output) are moved below the audio encoder field. Additionally, for consistency with Simple output, the video encoder field is moved above the audio encoder (suggestion from Rodney). Signed-off-by: pkv --- UI/forms/OBSBasicSettings.ui | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index b09ab68f38feb9..d08f5bb3421cd7 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -2895,14 +2895,14 @@ - + Basic.Settings.Output.Adv.AudioTrack - + @@ -3103,7 +3103,7 @@ - + Basic.Settings.Output.Encoder.Audio @@ -3120,14 +3120,14 @@ - + Basic.Settings.Output.Encoder.Video - +