Просмотр исходного кода

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 <[email protected]>
pkv 3 лет назад
Родитель
Сommit
d18b38e784

+ 1 - 0
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"

+ 219 - 2
UI/forms/OBSBasicSettings.ui

@@ -1939,6 +1939,217 @@
                         <widget class="QComboBox" name="simpleOutRecAEncoder"/>
                        </item>
                        <item row="6" column="0">
+                        <widget class="QLabel" name="simplerectrack_label">
+                         <property name="text">
+                          <string>Basic.Settings.Output.Simple.RecAudioTrack</string>
+                         </property>
+                        </widget>
+                       </item>
+                       <item row="6" column="1">
+                        <widget class="QStackedWidget" name="simpleRecTrackWidget">
+                         <property name="sizePolicy">
+                          <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+                           <horstretch>0</horstretch>
+                           <verstretch>0</verstretch>
+                          </sizepolicy>
+                         </property>
+                         <property name="currentIndex">
+                          <number>0</number>
+                         </property>
+                         <widget class="QWidget" name="simpleRecTracks">
+                          <layout class="QHBoxLayout" name="horizontalLayoutSimpleTracks">
+                           <property name="leftMargin">
+                            <number>0</number>
+                           </property>
+                           <property name="topMargin">
+                            <number>0</number>
+                           </property>
+                           <property name="rightMargin">
+                            <number>0</number>
+                           </property>
+                           <property name="bottomMargin">
+                            <number>0</number>
+                           </property>
+                           <item>
+                            <widget class="QCheckBox" name="simpleOutRecTrack1">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string notr="true">1</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QCheckBox" name="simpleOutRecTrack2">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string notr="true">2</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QCheckBox" name="simpleOutRecTrack3">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string notr="true">3</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QCheckBox" name="simpleOutRecTrack4">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string notr="true">4</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QCheckBox" name="simpleOutRecTrack5">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string notr="true">5</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QCheckBox" name="simpleOutRecTrack6">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string notr="true">6</string>
+                             </property>
+                            </widget>
+                           </item>
+                          </layout>
+                         </widget>
+                         <widget class="QWidget" name="simpleFlvTracks">
+                          <layout class="QHBoxLayout" name="horizontalLayoutSimpleFLVTracks">
+                           <property name="leftMargin">
+                            <number>0</number>
+                           </property>
+                           <property name="topMargin">
+                            <number>0</number>
+                           </property>
+                           <property name="rightMargin">
+                            <number>0</number>
+                           </property>
+                           <property name="bottomMargin">
+                            <number>0</number>
+                           </property>
+                           <item>
+                            <widget class="QRadioButton" name="simpleFlvTrack1">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string>1</string>
+                             </property>
+                             <property name="checked">
+                              <bool>true</bool>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QRadioButton" name="simpleFlvTrack2">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string>2</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QRadioButton" name="simpleFlvTrack3">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string>3</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QRadioButton" name="simpleFlvTrack4">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string>4</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QRadioButton" name="simpleFlvTrack5">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string>5</string>
+                             </property>
+                            </widget>
+                           </item>
+                           <item>
+                            <widget class="QRadioButton" name="simpleFlvTrack6">
+                             <property name="sizePolicy">
+                              <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                               <horstretch>0</horstretch>
+                               <verstretch>0</verstretch>
+                              </sizepolicy>
+                             </property>
+                             <property name="text">
+                              <string>6</string>
+                             </property>
+                            </widget>
+                           </item>
+                          </layout>
+                         </widget>
+                        </widget>
+                       </item>
+                       <item row="7" column="0">
                         <widget class="QLabel" name="label_420">
                          <property name="text">
                           <string>Basic.Settings.Output.CustomMuxerSettings</string>
@@ -1948,10 +2159,10 @@
                          </property>
                         </widget>
                        </item>
-                       <item row="6" column="1">
+                       <item row="7" column="1">
                         <widget class="QLineEdit" name="simpleOutMuxCustom"/>
                        </item>
-                       <item row="7" column="1">
+                       <item row="8" column="1">
                         <widget class="QCheckBox" name="simpleReplayBuf">
                          <property name="text">
                           <string>Basic.Settings.Output.UseReplayBuffer</string>
@@ -7621,6 +7832,12 @@
   <tabstop>simpleOutPreset</tabstop>
   <tabstop>simpleOutAdvanced</tabstop>
   <tabstop>simpleOutCustom</tabstop>
+  <tabstop>simpleOutRecTrack1</tabstop>
+  <tabstop>simpleOutRecTrack2</tabstop>
+  <tabstop>simpleOutRecTrack3</tabstop>
+  <tabstop>simpleOutRecTrack4</tabstop>
+  <tabstop>simpleOutRecTrack5</tabstop>
+  <tabstop>simpleOutRecTrack6</tabstop>
   <tabstop>simpleOutputPath</tabstop>
   <tabstop>simpleOutputBrowse</tabstop>
   <tabstop>simpleNoSpace</tabstop>

+ 97 - 6
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

+ 2 - 0
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);

+ 59 - 1
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;