diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 988317099..8e781b05c 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -769,6 +769,7 @@ Basic.Settings.Output.Adv.Audio.Track3="Track 3" Basic.Settings.Output.Adv.Audio.Track4="Track 4" Basic.Settings.Output.Adv.Audio.Track5="Track 5" Basic.Settings.Output.Adv.Audio.Track6="Track 6" +Basic.Settings.Output.Adv.TwitchVodTrack="Twitch VOD Track" # basic mode 'output' settings - advanced section - recording subsection Basic.Settings.Output.Adv.Recording="Recording" diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 49ae397ee..83c6cd204 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1907,7 +1907,7 @@ 0 - + QFormLayout::AllNonFixedFieldsGrow diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 8a127b72d..7df2a2c95 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1054,6 +1054,7 @@ bool SimpleOutput::ReplayBufferActive() const struct AdvancedOutput : BasicOutputHandler { OBSEncoder streamAudioEnc; + OBSEncoder streamArchiveEnc; OBSEncoder aacTrack[MAX_AUDIO_MIXES]; OBSEncoder h264Streaming; OBSEncoder h264Recording; @@ -1229,6 +1230,14 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) throw "Failed to create streaming audio encoder " "(advanced output)"; + id = ""; + int vodTrack = + config_get_int(main->Config(), "AdvOut", "VodTrackIndex") - 1; + if (!CreateAACEncoder(streamArchiveEnc, id, GetAudioBitrate(vodTrack), + "avc_aac_archive", vodTrack)) + throw "Failed to create archive audio encoder " + "(advanced output)"; + startRecording.Connect(obs_output_get_signal_handler(fileOutput), "start", OBSStartRecording, this); stopRecording.Connect(obs_output_get_signal_handler(fileOutput), "stop", @@ -1280,6 +1289,18 @@ void AdvancedOutput::Update() UpdateAudioSettings(); } +static inline bool ServiceSupportsVodTrack(const char *service) +{ + static const char *vodTrackServices[] = {"Twitch"}; + + for (const char *vodTrackService : vodTrackServices) { + if (astrcmpi(vodTrackService, service) == 0) + return true; + } + + return false; +} + inline void AdvancedOutput::SetupStreaming() { bool rescale = config_get_bool(main->Config(), "AdvOut", "Rescale"); @@ -1461,6 +1482,8 @@ inline void AdvancedOutput::UpdateAudioSettings() "ApplyServiceSettings"); int streamTrackIndex = config_get_int(main->Config(), "AdvOut", "TrackIndex"); + int vodTrackIndex = + config_get_int(main->Config(), "AdvOut", "VodTrackIndex"); obs_data_t *settings[MAX_AUDIO_MIXES]; for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) { @@ -1481,18 +1504,23 @@ inline void AdvancedOutput::UpdateAudioSettings() } for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) { + int track = (int)(i + 1); + obs_encoder_update(aacTrack[i], settings[i]); - if ((int)(i + 1) == streamTrackIndex) { + if (track == streamTrackIndex || track == vodTrackIndex) { if (applyServiceSettings) { obs_service_apply_encoder_settings( main->GetService(), nullptr, settings[i]); } - - obs_encoder_update(streamAudioEnc, settings[i]); } + if (track == streamTrackIndex) + obs_encoder_update(streamAudioEnc, settings[i]); + if (track == vodTrackIndex) + obs_encoder_update(streamArchiveEnc, settings[i]); + obs_data_release(settings[i]); } } @@ -1505,6 +1533,7 @@ void AdvancedOutput::SetupOutputs() for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) obs_encoder_set_audio(aacTrack[i], obs_get_audio()); obs_encoder_set_audio(streamAudioEnc, obs_get_audio()); + obs_encoder_set_audio(streamArchiveEnc, obs_get_audio()); SetupStreaming(); @@ -1527,7 +1556,11 @@ int AdvancedOutput::GetAudioBitrate(size_t i) const bool AdvancedOutput::SetupStreaming(obs_service_t *service) { int streamTrack = - config_get_int(main->Config(), "AdvOut", "TrackIndex") - 1; + config_get_int(main->Config(), "AdvOut", "TrackIndex"); + bool vodTrackEnabled = + config_get_bool(main->Config(), "AdvOut", "VodTrackEnabled"); + int vodTrackIndex = + config_get_int(main->Config(), "AdvOut", "VodTrackIndex"); if (!useStreamEncoder || (!ffmpegOutput && !obs_output_active(fileOutput))) { @@ -1609,7 +1642,7 @@ bool AdvancedOutput::SetupStreaming(obs_service_t *service) streamAudioEnc = obs_audio_encoder_create( id, "alt_audio_enc", nullptr, - streamTrack, nullptr); + streamTrack - 1, nullptr); if (!streamAudioEnc) return false; @@ -1626,6 +1659,23 @@ bool AdvancedOutput::SetupStreaming(obs_service_t *service) obs_output_set_video_encoder(streamOutput, h264Streaming); obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0); + + const char *id = obs_service_get_id(service); + if (strcmp(id, "rtmp_custom") == 0) { + vodTrackEnabled = false; + } else { + obs_data_t *settings = obs_service_get_settings(service); + const char *service = obs_data_get_string(settings, "service"); + if (!ServiceSupportsVodTrack(service)) + vodTrackEnabled = false; + obs_data_release(settings); + } + + if (vodTrackEnabled && streamTrack != vodTrackIndex) + obs_output_set_audio_encoder(streamOutput, streamArchiveEnc, 1); + else + obs_output_set_audio_encoder(streamOutput, nullptr, 1); + return true; } diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index a77363cdd..5f9f99d33 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1301,6 +1301,7 @@ bool OBSBasic::InitBasicConfigDefaults() true); config_set_default_bool(basicConfig, "AdvOut", "UseRescale", false); config_set_default_uint(basicConfig, "AdvOut", "TrackIndex", 1); + config_set_default_uint(basicConfig, "AdvOut", "VodTrackIndex", 2); config_set_default_string(basicConfig, "AdvOut", "Encoder", "obs_x264"); config_set_default_string(basicConfig, "AdvOut", "RecType", "Standard"); diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index b1586d6e9..071b03d96 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -71,6 +71,8 @@ void OBSBasicSettings::InitStreamPage() SLOT(UpdateServerList())); connect(ui->service, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateKeyLink())); + connect(ui->service, SIGNAL(currentIndexChanged(int)), this, + SLOT(UpdateVodTrackSetting())); connect(ui->customServer, SIGNAL(textChanged(const QString &)), this, SLOT(UpdateKeyLink())); connect(ui->customServer, SIGNAL(editingFinished(const QString &)), @@ -141,6 +143,7 @@ void OBSBasicSettings::LoadStream1Settings() UpdateKeyLink(); UpdateMoreInfoLink(); + UpdateVodTrackSetting(); bool streamActive = obs_frontend_streaming_active(); ui->streamPage->setEnabled(!streamActive); @@ -594,3 +597,56 @@ void OBSBasicSettings::on_useAuth_toggled() ui->authPwLabel->setVisible(use_auth); ui->authPwWidget->setVisible(use_auth); } + +void OBSBasicSettings::UpdateVodTrackSetting() +{ + bool enableVodTrack = ui->service->currentText() == "Twitch"; + bool wasEnabled = !!vodTrackCheckbox; + + if (enableVodTrack == wasEnabled) + return; + + if (!enableVodTrack) { + delete vodTrackCheckbox; + delete vodTrackContainer; + return; + } + + vodTrackCheckbox = new QCheckBox( + QTStr("Basic.Settings.Output.Adv.TwitchVodTrack")); + vodTrackCheckbox->setLayoutDirection(Qt::RightToLeft); + + vodTrackContainer = new QWidget(); + QHBoxLayout *vodTrackLayout = new QHBoxLayout(); + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + vodTrack[i] = new QRadioButton(QString::number(i + 1)); + vodTrackLayout->addWidget(vodTrack[i]); + + HookWidget(vodTrack[i], SIGNAL(clicked(bool)), + SLOT(OutputsChanged())); + } + + HookWidget(vodTrackCheckbox, SIGNAL(clicked(bool)), + SLOT(OutputsChanged())); + + vodTrackLayout->addStretch(); + vodTrackLayout->setContentsMargins(0, 0, 0, 0); + + vodTrackContainer->setLayout(vodTrackLayout); + + ui->advOutTopLayout->insertRow(2, vodTrackCheckbox, vodTrackContainer); + + bool vodTrackEnabled = + config_get_bool(main->Config(), "AdvOut", "VodTrackEnabled"); + vodTrackCheckbox->setChecked(vodTrackEnabled); + vodTrackContainer->setEnabled(vodTrackEnabled); + + connect(vodTrackCheckbox, SIGNAL(clicked(bool)), vodTrackContainer, + SLOT(setEnabled(bool))); + + int trackIndex = + config_get_int(main->Config(), "AdvOut", "VodTrackIndex"); + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + vodTrack[i]->setChecked((i + 1) == trackIndex); + } +} diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 3a3890739..6c2c4290f 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -3386,6 +3386,13 @@ void OBSBasicSettings::SaveOutputSettings() SaveEdit(ui->advOutTrack5Name, "AdvOut", "Track5Name"); SaveEdit(ui->advOutTrack6Name, "AdvOut", "Track6Name"); + if (vodTrackCheckbox) { + SaveCheckBox(vodTrackCheckbox, "AdvOut", "VodTrackEnabled"); + SaveTrackIndex(main->Config(), "AdvOut", "VodTrackIndex", + vodTrack[0], vodTrack[1], vodTrack[2], + vodTrack[3], vodTrack[4], vodTrack[5]); + } + SaveCheckBox(ui->advReplayBuf, "AdvOut", "RecRB"); SaveSpinBox(ui->advRBSecMax, "AdvOut", "RecRBTime"); SaveSpinBox(ui->advRBMegsMax, "AdvOut", "RecRBSize"); diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 89f679506..a8f098086 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -32,6 +32,7 @@ class OBSBasic; class QAbstractButton; +class QRadioButton; class QComboBox; class QCheckBox; class QLabel; @@ -156,6 +157,10 @@ private: uint32_t outputCX = 0; uint32_t outputCY = 0; + QPointer vodTrackCheckbox; + QPointer vodTrackContainer; + QPointer vodTrack[MAX_AUDIO_MIXES]; + void SaveCombo(QComboBox *widget, const char *section, const char *value); void SaveComboData(QComboBox *widget, const char *section, @@ -239,6 +244,7 @@ private: private slots: void UpdateServerList(); void UpdateKeyLink(); + void UpdateVodTrackSetting(); void UpdateMoreInfoLink(); void on_show_clicked(); void on_authPwShow_clicked();