Răsfoiți Sursa

UI: Add fragmented MP4/MOV formats

derrod 2 ani în urmă
părinte
comite
705173a0c3

+ 9 - 0
UI/data/locale/en-US.ini

@@ -931,6 +931,15 @@ Basic.Settings.Stream.Recommended.MaxFPS="Maximum FPS: %1"
 # basic mode 'output' settings
 Basic.Settings.Output="Output"
 Basic.Settings.Output.Format="Recording Format"
+Basic.Settings.Output.Format.MKV="Matroska Video (.mkv)"
+Basic.Settings.Output.Format.FLV="Flash Video (.flv)"
+Basic.Settings.Output.Format.MP4="MPEG-4 (.mp4)"
+Basic.Settings.Output.Format.MOV="QuickTime (.mov)"
+Basic.Settings.Output.Format.TS="MPEG-TS (.ts)"
+Basic.Settings.Output.Format.HLS="HLS (.m3u8 + .ts)"
+Basic.Settings.Output.Format.fMP4="Fragmented MP4 (.mp4)"
+Basic.Settings.Output.Format.fMOV="Fragmented MOV (.mov)"
+Basic.Settings.Output.Format.TT="Fragmented MP4/MOV writes the recording in chunks and does not require the same finalization as traditional MP4/MOV files.\nThis ensures the file remains playable even if writing to disk is interrupted, for example, as a result of a BSOD or power loss.\n\nThis may not be compatible with all players and editors. Use File → Remux Recordings to convert the file into a more compatible format if necessary."
 Basic.Settings.Output.Encoder="Encoder"
 Basic.Settings.Output.SelectDirectory="Select Recording Directory"
 Basic.Settings.Output.SelectFile="Select Recording File"

+ 18 - 60
UI/forms/OBSBasicSettings.ui

@@ -1888,36 +1888,15 @@
                        </item>
                        <item row="3" column="1">
                         <widget class="QComboBox" name="simpleOutRecFormat">
-                         <item>
-                          <property name="text">
-                           <string notr="true">flv</string>
-                          </property>
-                         </item>
-                         <item>
-                          <property name="text">
-                           <string notr="true">mp4</string>
-                          </property>
-                         </item>
-                         <item>
-                          <property name="text">
-                           <string notr="true">mov</string>
-                          </property>
-                         </item>
-                         <item>
-                          <property name="text">
-                           <string notr="true">mkv</string>
-                          </property>
-                         </item>
-                         <item>
-                          <property name="text">
-                           <string notr="true">ts</string>
-                          </property>
-                         </item>
-                         <item>
-                          <property name="text">
-                           <string notr="true">m3u8</string>
-                          </property>
-                         </item>
+                         <property name="toolTip">
+                          <string>Basic.Settings.Output.Format.TT</string>
+                         </property>
+                         <property name="editable">
+                          <bool>false</bool>
+                         </property>
+                         <property name="currentText">
+                          <string/>
+                         </property>
                         </widget>
                        </item>
                        <item row="4" column="0">
@@ -2652,42 +2631,21 @@
                                </item>
                                <item row="2" column="1">
                                 <widget class="QComboBox" name="advOutRecFormat">
+                                 <property name="toolTip">
+                                  <string>Basic.Settings.Output.Format.TT</string>
+                                 </property>
                                  <property name="sizePolicy">
                                   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
                                    <horstretch>0</horstretch>
                                    <verstretch>0</verstretch>
                                   </sizepolicy>
                                  </property>
-                                 <item>
-                                  <property name="text">
-                                   <string notr="true">flv</string>
-                                  </property>
-                                 </item>
-                                 <item>
-                                  <property name="text">
-                                   <string notr="true">mp4</string>
-                                  </property>
-                                 </item>
-                                 <item>
-                                  <property name="text">
-                                   <string notr="true">mov</string>
-                                  </property>
-                                 </item>
-                                 <item>
-                                  <property name="text">
-                                   <string notr="true">mkv</string>
-                                  </property>
-                                 </item>
-                                 <item>
-                                  <property name="text">
-                                   <string notr="true">ts</string>
-                                  </property>
-                                 </item>
-                                 <item>
-                                  <property name="text">
-                                   <string notr="true">m3u8</string>
-                                  </property>
-                                 </item>
+                                 <property name="editable">
+                                  <bool>false</bool>
+                                 </property>
+                                 <property name="currentText">
+                                  <string/>
+                                 </property>
                                 </widget>
                                </item>
                                <item row="3" column="0">

+ 49 - 2
UI/window-basic-main-outputs.cpp

@@ -1149,9 +1149,15 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
 	int rbSize =
 		config_get_int(main->Config(), "SimpleOutput", "RecRBSize");
 
+	bool is_fragmented = strcmp(format, "fmp4") == 0 ||
+			     strcmp(format, "fmov") == 0;
+
 	string f;
 	string strPath;
 
+	if (is_fragmented)
+		++format;
+
 	OBSDataAutoRelease settings = obs_data_create();
 	if (updateReplayBuffer) {
 		f = GetFormatString(filenameFormat, rbPrefix, rbSuffix);
@@ -1175,7 +1181,23 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
 				    strPath.c_str());
 	}
 
-	obs_data_set_string(settings, "muxer_settings", mux);
+	// Use fragmented MOV/MP4 if user has not already specified custom movflags
+	if (is_fragmented && (!mux || strstr(mux, "movflags") == NULL)) {
+		string mux_frag =
+			"movflags=frag_keyframe+empty_moov+delay_moov";
+		if (mux) {
+			mux_frag += " ";
+			mux_frag += mux;
+		}
+		obs_data_set_string(settings, "muxer_settings",
+				    mux_frag.c_str());
+	} else {
+		if (is_fragmented)
+			blog(LOG_WARNING,
+			     "User enabled fragmented recording, "
+			     "but custom muxer settings contained movflags.");
+		obs_data_set_string(settings, "muxer_settings", mux);
+	}
 
 	if (updateReplayBuffer)
 		obs_output_update(replayBuffer, settings);
@@ -1592,6 +1614,9 @@ inline void AdvancedOutput::SetupRecording()
 	const char *recFormat =
 		config_get_string(main->Config(), "AdvOut", "RecFormat");
 
+	bool is_fragmented = strcmp(recFormat, "fmp4") == 0 ||
+			     strcmp(recFormat, "fmov") == 0;
+
 	bool flv = strcmp(recFormat, "flv") == 0;
 
 	if (flv)
@@ -1647,8 +1672,25 @@ inline void AdvancedOutput::SetupRecording()
 						     aacTrack[tracks - 1], idx);
 	}
 
+	// Use fragmented MOV/MP4 if user has not already specified custom movflags
+	if (is_fragmented && (!mux || strstr(mux, "movflags") == NULL)) {
+		string mux_fmp4 =
+			"movflags=frag_keyframe+empty_moov+delay_moov";
+		if (mux) {
+			mux_fmp4 += " ";
+			mux_fmp4 += mux;
+		}
+		obs_data_set_string(settings, "muxer_settings",
+				    mux_fmp4.c_str());
+	} else {
+		if (is_fragmented)
+			blog(LOG_WARNING,
+			     "User enabled fragmented recording, "
+			     "but custom muxer settings contained movflags.");
+		obs_data_set_string(settings, "muxer_settings", mux);
+	}
+
 	obs_data_set_string(settings, "path", path);
-	obs_data_set_string(settings, "muxer_settings", mux);
 	obs_output_update(fileOutput, settings);
 	if (replayBuffer)
 		obs_output_update(replayBuffer, settings);
@@ -2039,6 +2081,11 @@ bool AdvancedOutput::StartRecording()
 		splitFile = config_get_bool(main->Config(), "AdvOut",
 					    "RecSplitFile");
 
+		// Strip leading "f" in case fragmented format was selected
+		if (strcmp(recFormat, "fmp4") == 0 ||
+		    strcmp(recFormat, "fmov") == 0)
+			++recFormat;
+
 		string strPath = GetRecordingFilename(path, recFormat, noSpace,
 						      overwriteIfExists,
 						      filenameFormat,

+ 36 - 18
UI/window-basic-settings.cpp

@@ -1031,6 +1031,7 @@ void OBSBasicSettings::LoadColorFormats()
 
 void OBSBasicSettings::LoadFormats()
 {
+#define FORMAT_STR(str) QTStr("Basic.Settings.Output.Format." str)
 	ui->advOutFFFormat->blockSignals(true);
 
 	formats.reset(ff_format_supported());
@@ -1059,6 +1060,25 @@ void OBSBasicSettings::LoadFormats()
 	ui->advOutFFFormat->insertItem(0, AV_FORMAT_DEFAULT_STR);
 
 	ui->advOutFFFormat->blockSignals(false);
+
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("FLV"), "flv");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("MKV"), "mkv");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("MP4"), "mp4");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("MOV"), "mov");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("fMP4"), "fmp4");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("fMOV"), "fmov");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("TS"), "ts");
+
+	ui->advOutRecFormat->addItem(FORMAT_STR("FLV"), "flv");
+	ui->advOutRecFormat->addItem(FORMAT_STR("MKV"), "mkv");
+	ui->advOutRecFormat->addItem(FORMAT_STR("MP4"), "mp4");
+	ui->advOutRecFormat->addItem(FORMAT_STR("MOV"), "mov");
+	ui->advOutRecFormat->addItem(FORMAT_STR("fMP4"), "fmp4");
+	ui->advOutRecFormat->addItem(FORMAT_STR("fMOV"), "fmov");
+	ui->advOutRecFormat->addItem(FORMAT_STR("TS"), "ts");
+	ui->advOutRecFormat->addItem(FORMAT_STR("HLS"), "m3u8");
+
+#undef FORMAT_STR
 }
 
 static void AddCodec(QComboBox *combo, const ff_codec_desc *codec_desc)
@@ -1844,7 +1864,7 @@ void OBSBasicSettings::LoadSimpleOutputSettings()
 	ui->simpleNoSpace->setChecked(noSpace);
 	ui->simpleOutputVBitrate->setValue(videoBitrate);
 
-	int idx = ui->simpleOutRecFormat->findText(format);
+	int idx = ui->simpleOutRecFormat->findData(format);
 	ui->simpleOutRecFormat->setCurrentIndex(idx);
 
 	const char *speakers =
@@ -2022,7 +2042,7 @@ void OBSBasicSettings::LoadAdvOutputRecordingSettings()
 	ui->advOutRecRescale->setCurrentText(rescaleRes);
 	ui->advOutMuxCustom->setText(muxCustom);
 
-	int idx = ui->advOutRecFormat->findText(format);
+	int idx = ui->advOutRecFormat->findData(format);
 	ui->advOutRecFormat->setCurrentIndex(idx);
 
 	ui->advOutRecTrack1->setChecked(tracks & (1 << 0));
@@ -3621,7 +3641,7 @@ void OBSBasicSettings::SaveOutputSettings()
 	SaveCombo(ui->simpleOutputABitrate, "SimpleOutput", "ABitrate");
 	SaveEdit(ui->simpleOutputPath, "SimpleOutput", "FilePath");
 	SaveCheckBox(ui->simpleNoSpace, "SimpleOutput", "FileNameWithoutSpace");
-	SaveCombo(ui->simpleOutRecFormat, "SimpleOutput", "RecFormat");
+	SaveComboData(ui->simpleOutRecFormat, "SimpleOutput", "RecFormat");
 	SaveCheckBox(ui->simpleOutAdvanced, "SimpleOutput", "UseAdvanced");
 	SaveComboData(ui->simpleOutPreset, "SimpleOutput", presetType);
 	SaveEdit(ui->simpleOutCustom, "SimpleOutput", "x264Settings");
@@ -3648,7 +3668,7 @@ void OBSBasicSettings::SaveOutputSettings()
 
 	SaveEdit(ui->advOutRecPath, "AdvOut", "RecFilePath");
 	SaveCheckBox(ui->advOutNoSpace, "AdvOut", "RecFileNameWithoutSpace");
-	SaveCombo(ui->advOutRecFormat, "AdvOut", "RecFormat");
+	SaveComboData(ui->advOutRecFormat, "AdvOut", "RecFormat");
 	SaveComboData(ui->advOutRecEncoder, "AdvOut", "RecEncoder");
 	SaveCheckBox(ui->advOutRecUseRescale, "AdvOut", "RecRescale");
 	SaveCombo(ui->advOutRecRescale, "AdvOut", "RecRescaleRes");
@@ -4709,7 +4729,9 @@ void OBSBasicSettings::AdvOutRecCheckWarnings()
 		warningMsg += QTStr("OutputWarnings.CannotPause");
 	}
 
-	if (ui->advOutRecFormat->currentText().compare("flv") == 0) {
+	QString recFormat = ui->advOutRecFormat->currentData().toString();
+
+	if (recFormat == "flv") {
 		ui->advRecTrackWidget->setCurrentWidget(ui->flvTracks);
 	} else {
 		ui->advRecTrackWidget->setCurrentWidget(ui->recTracks);
@@ -4718,20 +4740,18 @@ void OBSBasicSettings::AdvOutRecCheckWarnings()
 			errorMsg = QTStr("OutputWarnings.NoTracksSelected");
 	}
 
-	QString recFormat = ui->advOutRecFormat->currentText();
 	QString recEncoder = ui->advOutRecEncoder->currentText();
 
 	if (recEncoder.contains("ProRes")) {
-		if (recFormat.compare("mkv") == 0) {
+		if (recFormat == "mkv") {
 			ui->autoRemux->setText(
 				QTStr("Basic.Settings.Advanced.AutoRemux")
 					.arg("mov"));
-		} else if (recFormat.compare("mov") == 0) {
-			if (!warningMsg.isEmpty()) {
+		} else if (recFormat == "mov") {
+			if (!warningMsg.isEmpty())
 				warningMsg += "\n\n";
-			}
-
 			warningMsg += QTStr("OutputWarnings.MP4Recording");
+
 			ui->autoRemux->setText(
 				QTStr("Basic.Settings.Advanced.AutoRemux")
 					.arg("mov") +
@@ -4746,11 +4766,9 @@ void OBSBasicSettings::AdvOutRecCheckWarnings()
 					    .arg(recFormat);
 		}
 	} else {
-		if (recFormat.compare("mp4") == 0 ||
-		    recFormat.compare("mov") == 0) {
-			if (!warningMsg.isEmpty()) {
+		if (recFormat == "mp4" || recFormat == "mov") {
+			if (!warningMsg.isEmpty())
 				warningMsg += "\n\n";
-			}
 
 			warningMsg += QTStr("OutputWarnings.MP4Recording");
 			ui->autoRemux->setText(
@@ -5317,9 +5335,9 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged()
 		warning += SIMPLE_OUTPUT_WARNING("CannotPause");
 	}
 
-	if (qual != "Lossless" &&
-	    (ui->simpleOutRecFormat->currentText().compare("mp4") == 0 ||
-	     ui->simpleOutRecFormat->currentText().compare("mov") == 0)) {
+	QString format = ui->simpleOutRecFormat->currentData().toString();
+
+	if (qual != "Lossless" && (format == "mp4" || format == "mov")) {
 		if (!warning.isEmpty())
 			warning += "\n\n";
 		warning += QTStr("OutputWarnings.MP4Recording");