瀏覽代碼

UI: Add VOD track support in advanced output

Adds a VOD track option (specific to Twitch) that allows a user to
specify which audio track to use for their Twitch VODs, which uses a
separate encoder to encode the track. This allows users the ability to
choose what audio goes on their VOD, separately from the live stream.
jp9000 5 年之前
父節點
當前提交
6b81c106d8

+ 1 - 0
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.Track4="Track 4"
 Basic.Settings.Output.Adv.Audio.Track5="Track 5"
 Basic.Settings.Output.Adv.Audio.Track5="Track 5"
 Basic.Settings.Output.Adv.Audio.Track6="Track 6"
 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 mode 'output' settings - advanced section - recording subsection
 Basic.Settings.Output.Adv.Recording="Recording"
 Basic.Settings.Output.Adv.Recording="Recording"

+ 1 - 1
UI/forms/OBSBasicSettings.ui

@@ -1907,7 +1907,7 @@
                             <verstretch>0</verstretch>
                             <verstretch>0</verstretch>
                            </sizepolicy>
                            </sizepolicy>
                           </property>
                           </property>
-                          <layout class="QFormLayout" name="formLayout_7">
+                          <layout class="QFormLayout" name="advOutTopLayout">
                            <property name="fieldGrowthPolicy">
                            <property name="fieldGrowthPolicy">
                             <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
                             <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
                            </property>
                            </property>

+ 54 - 4
UI/window-basic-main-outputs.cpp

@@ -1054,6 +1054,7 @@ bool SimpleOutput::ReplayBufferActive() const
 
 
 struct AdvancedOutput : BasicOutputHandler {
 struct AdvancedOutput : BasicOutputHandler {
 	OBSEncoder streamAudioEnc;
 	OBSEncoder streamAudioEnc;
+	OBSEncoder streamArchiveEnc;
 	OBSEncoder aacTrack[MAX_AUDIO_MIXES];
 	OBSEncoder aacTrack[MAX_AUDIO_MIXES];
 	OBSEncoder h264Streaming;
 	OBSEncoder h264Streaming;
 	OBSEncoder h264Recording;
 	OBSEncoder h264Recording;
@@ -1229,6 +1230,14 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
 		throw "Failed to create streaming audio encoder "
 		throw "Failed to create streaming audio encoder "
 		      "(advanced output)";
 		      "(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),
 	startRecording.Connect(obs_output_get_signal_handler(fileOutput),
 			       "start", OBSStartRecording, this);
 			       "start", OBSStartRecording, this);
 	stopRecording.Connect(obs_output_get_signal_handler(fileOutput), "stop",
 	stopRecording.Connect(obs_output_get_signal_handler(fileOutput), "stop",
@@ -1280,6 +1289,18 @@ void AdvancedOutput::Update()
 	UpdateAudioSettings();
 	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()
 inline void AdvancedOutput::SetupStreaming()
 {
 {
 	bool rescale = config_get_bool(main->Config(), "AdvOut", "Rescale");
 	bool rescale = config_get_bool(main->Config(), "AdvOut", "Rescale");
@@ -1461,6 +1482,8 @@ inline void AdvancedOutput::UpdateAudioSettings()
 						    "ApplyServiceSettings");
 						    "ApplyServiceSettings");
 	int streamTrackIndex =
 	int streamTrackIndex =
 		config_get_int(main->Config(), "AdvOut", "TrackIndex");
 		config_get_int(main->Config(), "AdvOut", "TrackIndex");
+	int vodTrackIndex =
+		config_get_int(main->Config(), "AdvOut", "VodTrackIndex");
 	obs_data_t *settings[MAX_AUDIO_MIXES];
 	obs_data_t *settings[MAX_AUDIO_MIXES];
 
 
 	for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
 	for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
@@ -1481,17 +1504,22 @@ inline void AdvancedOutput::UpdateAudioSettings()
 	}
 	}
 
 
 	for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
 	for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
+		int track = (int)(i + 1);
+
 		obs_encoder_update(aacTrack[i], settings[i]);
 		obs_encoder_update(aacTrack[i], settings[i]);
 
 
-		if ((int)(i + 1) == streamTrackIndex) {
+		if (track == streamTrackIndex || track == vodTrackIndex) {
 			if (applyServiceSettings) {
 			if (applyServiceSettings) {
 				obs_service_apply_encoder_settings(
 				obs_service_apply_encoder_settings(
 					main->GetService(), nullptr,
 					main->GetService(), nullptr,
 					settings[i]);
 					settings[i]);
 			}
 			}
+		}
 
 
+		if (track == streamTrackIndex)
 			obs_encoder_update(streamAudioEnc, settings[i]);
 			obs_encoder_update(streamAudioEnc, settings[i]);
-		}
+		if (track == vodTrackIndex)
+			obs_encoder_update(streamArchiveEnc, settings[i]);
 
 
 		obs_data_release(settings[i]);
 		obs_data_release(settings[i]);
 	}
 	}
@@ -1505,6 +1533,7 @@ void AdvancedOutput::SetupOutputs()
 	for (size_t i = 0; i < MAX_AUDIO_MIXES; i++)
 	for (size_t i = 0; i < MAX_AUDIO_MIXES; i++)
 		obs_encoder_set_audio(aacTrack[i], obs_get_audio());
 		obs_encoder_set_audio(aacTrack[i], obs_get_audio());
 	obs_encoder_set_audio(streamAudioEnc, obs_get_audio());
 	obs_encoder_set_audio(streamAudioEnc, obs_get_audio());
+	obs_encoder_set_audio(streamArchiveEnc, obs_get_audio());
 
 
 	SetupStreaming();
 	SetupStreaming();
 
 
@@ -1527,7 +1556,11 @@ int AdvancedOutput::GetAudioBitrate(size_t i) const
 bool AdvancedOutput::SetupStreaming(obs_service_t *service)
 bool AdvancedOutput::SetupStreaming(obs_service_t *service)
 {
 {
 	int streamTrack =
 	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 ||
 	if (!useStreamEncoder ||
 	    (!ffmpegOutput && !obs_output_active(fileOutput))) {
 	    (!ffmpegOutput && !obs_output_active(fileOutput))) {
@@ -1609,7 +1642,7 @@ bool AdvancedOutput::SetupStreaming(obs_service_t *service)
 
 
 				streamAudioEnc = obs_audio_encoder_create(
 				streamAudioEnc = obs_audio_encoder_create(
 					id, "alt_audio_enc", nullptr,
 					id, "alt_audio_enc", nullptr,
-					streamTrack, nullptr);
+					streamTrack - 1, nullptr);
 
 
 				if (!streamAudioEnc)
 				if (!streamAudioEnc)
 					return false;
 					return false;
@@ -1626,6 +1659,23 @@ bool AdvancedOutput::SetupStreaming(obs_service_t *service)
 
 
 	obs_output_set_video_encoder(streamOutput, h264Streaming);
 	obs_output_set_video_encoder(streamOutput, h264Streaming);
 	obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);
 	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;
 	return true;
 }
 }
 
 

+ 1 - 0
UI/window-basic-main.cpp

@@ -1301,6 +1301,7 @@ bool OBSBasic::InitBasicConfigDefaults()
 				true);
 				true);
 	config_set_default_bool(basicConfig, "AdvOut", "UseRescale", false);
 	config_set_default_bool(basicConfig, "AdvOut", "UseRescale", false);
 	config_set_default_uint(basicConfig, "AdvOut", "TrackIndex", 1);
 	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", "Encoder", "obs_x264");
 
 
 	config_set_default_string(basicConfig, "AdvOut", "RecType", "Standard");
 	config_set_default_string(basicConfig, "AdvOut", "RecType", "Standard");

+ 56 - 0
UI/window-basic-settings-stream.cpp

@@ -71,6 +71,8 @@ void OBSBasicSettings::InitStreamPage()
 		SLOT(UpdateServerList()));
 		SLOT(UpdateServerList()));
 	connect(ui->service, SIGNAL(currentIndexChanged(int)), this,
 	connect(ui->service, SIGNAL(currentIndexChanged(int)), this,
 		SLOT(UpdateKeyLink()));
 		SLOT(UpdateKeyLink()));
+	connect(ui->service, SIGNAL(currentIndexChanged(int)), this,
+		SLOT(UpdateVodTrackSetting()));
 	connect(ui->customServer, SIGNAL(textChanged(const QString &)), this,
 	connect(ui->customServer, SIGNAL(textChanged(const QString &)), this,
 		SLOT(UpdateKeyLink()));
 		SLOT(UpdateKeyLink()));
 	connect(ui->customServer, SIGNAL(editingFinished(const QString &)),
 	connect(ui->customServer, SIGNAL(editingFinished(const QString &)),
@@ -141,6 +143,7 @@ void OBSBasicSettings::LoadStream1Settings()
 
 
 	UpdateKeyLink();
 	UpdateKeyLink();
 	UpdateMoreInfoLink();
 	UpdateMoreInfoLink();
+	UpdateVodTrackSetting();
 
 
 	bool streamActive = obs_frontend_streaming_active();
 	bool streamActive = obs_frontend_streaming_active();
 	ui->streamPage->setEnabled(!streamActive);
 	ui->streamPage->setEnabled(!streamActive);
@@ -594,3 +597,56 @@ void OBSBasicSettings::on_useAuth_toggled()
 	ui->authPwLabel->setVisible(use_auth);
 	ui->authPwLabel->setVisible(use_auth);
 	ui->authPwWidget->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);
+	}
+}

+ 7 - 0
UI/window-basic-settings.cpp

@@ -3386,6 +3386,13 @@ void OBSBasicSettings::SaveOutputSettings()
 	SaveEdit(ui->advOutTrack5Name, "AdvOut", "Track5Name");
 	SaveEdit(ui->advOutTrack5Name, "AdvOut", "Track5Name");
 	SaveEdit(ui->advOutTrack6Name, "AdvOut", "Track6Name");
 	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");
 	SaveCheckBox(ui->advReplayBuf, "AdvOut", "RecRB");
 	SaveSpinBox(ui->advRBSecMax, "AdvOut", "RecRBTime");
 	SaveSpinBox(ui->advRBSecMax, "AdvOut", "RecRBTime");
 	SaveSpinBox(ui->advRBMegsMax, "AdvOut", "RecRBSize");
 	SaveSpinBox(ui->advRBMegsMax, "AdvOut", "RecRBSize");

+ 6 - 0
UI/window-basic-settings.hpp

@@ -32,6 +32,7 @@
 
 
 class OBSBasic;
 class OBSBasic;
 class QAbstractButton;
 class QAbstractButton;
+class QRadioButton;
 class QComboBox;
 class QComboBox;
 class QCheckBox;
 class QCheckBox;
 class QLabel;
 class QLabel;
@@ -156,6 +157,10 @@ private:
 	uint32_t outputCX = 0;
 	uint32_t outputCX = 0;
 	uint32_t outputCY = 0;
 	uint32_t outputCY = 0;
 
 
+	QPointer<QCheckBox> vodTrackCheckbox;
+	QPointer<QWidget> vodTrackContainer;
+	QPointer<QRadioButton> vodTrack[MAX_AUDIO_MIXES];
+
 	void SaveCombo(QComboBox *widget, const char *section,
 	void SaveCombo(QComboBox *widget, const char *section,
 		       const char *value);
 		       const char *value);
 	void SaveComboData(QComboBox *widget, const char *section,
 	void SaveComboData(QComboBox *widget, const char *section,
@@ -239,6 +244,7 @@ private:
 private slots:
 private slots:
 	void UpdateServerList();
 	void UpdateServerList();
 	void UpdateKeyLink();
 	void UpdateKeyLink();
+	void UpdateVodTrackSetting();
 	void UpdateMoreInfoLink();
 	void UpdateMoreInfoLink();
 	void on_show_clicked();
 	void on_show_clicked();
 	void on_authPwShow_clicked();
 	void on_authPwShow_clicked();