瀏覽代碼

UI: Rework recording format handling

derrod 2 年之前
父節點
當前提交
5a375defa8
共有 6 個文件被更改,包括 107 次插入91 次删除
  1. 18 2
      UI/obs-app.cpp
  2. 3 2
      UI/obs-app.hpp
  3. 19 37
      UI/window-basic-main-outputs.cpp
  4. 5 5
      UI/window-basic-main-outputs.hpp
  5. 34 20
      UI/window-basic-main.cpp
  6. 28 25
      UI/window-basic-settings.cpp

+ 18 - 2
UI/obs-app.cpp

@@ -2130,7 +2130,22 @@ string GetFormatString(const char *format, const char *prefix,
 	return f;
 }
 
-string GetOutputFilename(const char *path, const char *ext, bool noSpace,
+string GetFormatExt(const char *container)
+{
+	string ext = container;
+	if (ext == "fragmented_mp4")
+		ext = "mp4";
+	else if (ext == "fragmented_mov")
+		ext = "mov";
+	else if (ext == "hls")
+		ext = "m3u8";
+	else if (ext == "mpegts")
+		ext = "ts";
+
+	return ext;
+}
+
+string GetOutputFilename(const char *path, const char *container, bool noSpace,
 			 bool overwrite, const char *format)
 {
 	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
@@ -2157,7 +2172,8 @@ string GetOutputFilename(const char *path, const char *ext, bool noSpace,
 	if (lastChar != '/' && lastChar != '\\')
 		strPath += "/";
 
-	strPath += GenerateSpecifiedFilename(ext, noSpace, format);
+	string ext = GetFormatExt(container);
+	strPath += GenerateSpecifiedFilename(ext.c_str(), noSpace, format);
 	ensure_directory_exists(strPath);
 	if (!overwrite)
 		FindBestFilename(strPath, noSpace);

+ 3 - 2
UI/obs-app.hpp

@@ -45,8 +45,9 @@ std::string GenerateSpecifiedFilename(const char *extension, bool noSpace,
 				      const char *format);
 std::string GetFormatString(const char *format, const char *prefix,
 			    const char *suffix);
-std::string GetOutputFilename(const char *path, const char *ext, bool noSpace,
-			      bool overwrite, const char *format);
+std::string GetFormatExt(const char *container);
+std::string GetOutputFilename(const char *path, const char *container,
+			      bool noSpace, bool overwrite, const char *format);
 QObject *CreateShortcutFilter();
 
 struct BaseLexer {

+ 19 - 37
UI/window-basic-main-outputs.cpp

@@ -1297,15 +1297,11 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
 	int tracks =
 		config_get_int(main->Config(), "SimpleOutput", "RecTracks");
 
-	bool is_fragmented = strcmp(format, "fmp4") == 0 ||
-			     strcmp(format, "fmov") == 0;
+	bool is_fragmented = strncmp(format, "fragmented", 10) == 0;
 
 	string f;
 	string strPath;
 
-	if (is_fragmented)
-		++format;
-
 	OBSDataAutoRelease settings = obs_data_create();
 	if (updateReplayBuffer) {
 		f = GetFormatString(filenameFormat, rbPrefix, rbSuffix);
@@ -1324,8 +1320,7 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
 		strPath = GetRecordingFilename(path,
 					       ffmpegOutput ? "avi" : format,
 					       noSpace, overwriteIfExists,
-					       f.c_str(), ffmpegOutput,
-					       is_fragmented);
+					       f.c_str(), ffmpegOutput);
 		obs_data_set_string(settings, ffmpegOutput ? "url" : "path",
 				    strPath.c_str());
 		if (ffmpegOutput)
@@ -1782,9 +1777,7 @@ inline void AdvancedOutput::SetupRecording()
 	const char *recFormat =
 		config_get_string(main->Config(), "AdvOut", "RecFormat2");
 
-	bool is_fragmented = strcmp(recFormat, "fmp4") == 0 ||
-			     strcmp(recFormat, "fmov") == 0;
-
+	bool is_fragmented = strncmp(recFormat, "fragmented", 10) == 0;
 	bool flv = strcmp(recFormat, "flv") == 0;
 
 	if (flv)
@@ -1843,14 +1836,14 @@ inline void AdvancedOutput::SetupRecording()
 
 	// Use fragmented MOV/MP4 if user has not already specified custom movflags
 	if (is_fragmented && (!mux || strstr(mux, "movflags") == NULL)) {
-		string mux_fmp4 =
+		string mux_frag =
 			"movflags=frag_keyframe+empty_moov+delay_moov";
 		if (mux) {
-			mux_fmp4 += " ";
-			mux_fmp4 += mux;
+			mux_frag += " ";
+			mux_frag += mux;
 		}
 		obs_data_set_string(settings, "muxer_settings",
-				    mux_fmp4.c_str());
+				    mux_frag.c_str());
 	} else {
 		if (is_fragmented)
 			blog(LOG_WARNING,
@@ -2176,7 +2169,6 @@ bool AdvancedOutput::StartRecording()
 	const char *recFormat;
 	const char *filenameFormat;
 	bool noSpace = false;
-	bool fragmented = false;
 	bool overwriteIfExists = false;
 	bool splitFile;
 	const char *splitFileType;
@@ -2214,16 +2206,10 @@ 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;
-			fragmented = true;
-		}
-
-		string strPath = GetRecordingFilename(
-			path, recFormat, noSpace, overwriteIfExists,
-			filenameFormat, ffmpegRecording, fragmented);
+		string strPath = GetRecordingFilename(path, recFormat, noSpace,
+						      overwriteIfExists,
+						      filenameFormat,
+						      ffmpegRecording);
 
 		OBSDataAutoRelease settings = obs_data_create();
 		obs_data_set_string(settings, ffmpegRecording ? "url" : "path",
@@ -2322,11 +2308,6 @@ bool AdvancedOutput::StartReplayBuffer()
 		rbTime = config_get_int(main->Config(), "AdvOut", "RecRBTime");
 		rbSize = config_get_int(main->Config(), "AdvOut", "RecRBSize");
 
-		/* Skip leading f for fragmented formats. */
-		if (strcmp(recFormat, "fmp4") == 0 ||
-		    strcmp(recFormat, "fmov") == 0)
-			++recFormat;
-
 		string f = GetFormatString(filenameFormat, rbPrefix, rbSuffix);
 		string strPath = GetOutputFilename(
 			path, recFormat, noSpace, overwriteIfExists, f.c_str());
@@ -2400,21 +2381,22 @@ bool AdvancedOutput::ReplayBufferActive() const
 
 /* ------------------------------------------------------------------------ */
 
-void BasicOutputHandler::SetupAutoRemux(const char *&ext, bool is_fragmented)
+void BasicOutputHandler::SetupAutoRemux(const char *&container)
 {
 	bool autoRemux = config_get_bool(main->Config(), "Video", "AutoRemux");
-	if (autoRemux && !is_fragmented && strcmp(ext, "mp4") == 0)
-		ext = "mkv";
+	if (autoRemux && strcmp(container, "mp4") == 0)
+		container = "mkv";
 }
 
 std::string BasicOutputHandler::GetRecordingFilename(
-	const char *path, const char *ext, bool noSpace, bool overwrite,
-	const char *format, bool ffmpeg, bool is_fragmented)
+	const char *path, const char *container, bool noSpace, bool overwrite,
+	const char *format, bool ffmpeg)
 {
 	if (!ffmpeg)
-		SetupAutoRemux(ext, is_fragmented);
+		SetupAutoRemux(container);
 
-	string dst = GetOutputFilename(path, ext, noSpace, overwrite, format);
+	string dst =
+		GetOutputFilename(path, container, noSpace, overwrite, format);
 	lastRecordingPath = dst;
 	return dst;
 }

+ 5 - 5
UI/window-basic-main-outputs.hpp

@@ -72,11 +72,11 @@ struct BasicOutputHandler {
 	}
 
 protected:
-	void SetupAutoRemux(const char *&ext, bool is_fragmented);
-	std::string GetRecordingFilename(const char *path, const char *ext,
-					 bool noSpace, bool overwrite,
-					 const char *format, bool ffmpeg,
-					 bool is_fragmented);
+	void SetupAutoRemux(const char *&container);
+	std::string GetRecordingFilename(const char *path,
+					 const char *container, bool noSpace,
+					 bool overwrite, const char *format,
+					 bool ffmpeg);
 };
 
 BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main);

+ 34 - 20
UI/window-basic-main.cpp

@@ -1374,9 +1374,9 @@ extern void CheckExistingCookieId();
 #if OBS_RELEASE_CANDIDATE == 0 && OBS_BETA == 0
 #define DEFAULT_CONTAINER "mkv"
 #elif defined(__APPLE__)
-#define DEFAULT_CONTAINER "fmov"
+#define DEFAULT_CONTAINER "fragmented_mov"
 #else
-#define DEFAULT_CONTAINER "fmp4"
+#define DEFAULT_CONTAINER "fragmented_mp4"
 #endif
 
 bool OBSBasic::InitBasicConfigDefaults()
@@ -1474,23 +1474,37 @@ bool OBSBasic::InitBasicConfigDefaults()
 
 	/* ----------------------------------------------------- */
 	/* Migrate old container selection (if any) to new key.  */
-	if (!config_has_user_value(basicConfig, "SimpleOutput", "RecFormat2") &&
-	    config_has_user_value(basicConfig, "SimpleOutput", "RecFormat")) {
-		const char *old_format = config_get_string(
-			basicConfig, "SimpleOutput", "RecFormat");
-		config_set_string(basicConfig, "SimpleOutput", "RecFormat2",
-				  old_format);
-		changed = true;
-	}
 
-	if (!config_has_user_value(basicConfig, "AdvOut", "RecFormat2") &&
-	    config_has_user_value(basicConfig, "AdvOut", "RecFormat")) {
-		const char *old_format =
-			config_get_string(basicConfig, "AdvOut", "RecFormat");
-		config_set_string(basicConfig, "AdvOut", "RecFormat2",
-				  old_format);
-		changed = true;
-	}
+	auto MigrateFormat = [&](const char *section) {
+		bool has_old_key = config_has_user_value(basicConfig, section,
+							 "RecFormat");
+		bool has_new_key = config_has_user_value(basicConfig, section,
+							 "RecFormat2");
+		if (!has_new_key && !has_old_key)
+			return;
+
+		string old_format = config_get_string(
+			basicConfig, section,
+			has_new_key ? "RecFormat2" : "RecFormat");
+		string new_format = old_format;
+		if (old_format == "ts")
+			new_format = "mpegts";
+		else if (old_format == "m3u8")
+			new_format = "hls";
+		else if (old_format == "fmp4")
+			new_format = "fragmented_mp4";
+		else if (old_format == "fmov")
+			new_format = "fragmented_mov";
+
+		if (new_format != old_format || !has_new_key) {
+			config_set_string(basicConfig, section, "RecFormat2",
+					  new_format.c_str());
+			changed = true;
+		}
+	};
+
+	MigrateFormat("AdvOut");
+	MigrateFormat("SimpleOutput");
 
 	/* ----------------------------------------------------- */
 
@@ -7382,11 +7396,11 @@ void OBSBasic::AutoRemux(QString input, bool no_show)
 	const obs_encoder_t *videoEncoder =
 		obs_output_get_video_encoder(outputHandler->fileOutput);
 	const char *codecName = obs_encoder_get_codec(videoEncoder);
-	string format = config_get_string(
+	const char *format = config_get_string(
 		config, isSimpleMode ? "SimpleOutput" : "AdvOut", "RecFormat2");
 
 	/* Retain original container for fMP4/fMOV */
-	if (format == "fmp4" || format == "fmov") {
+	if (strncmp(format, "fragmented", 10) == 0) {
 		output += "remuxed." + suffix;
 	} else if (strcmp(codecName, "prores") == 0) {
 		output += "mov";

+ 28 - 25
UI/window-basic-settings.cpp

@@ -1165,18 +1165,18 @@ void OBSBasicSettings::LoadFormats()
 	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->simpleOutRecFormat->addItem(FORMAT_STR("fMP4"), "fragmented_mp4");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("fMOV"), "fragmented_mov");
+	ui->simpleOutRecFormat->addItem(FORMAT_STR("TS"), "mpegts");
 
 	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");
+	ui->advOutRecFormat->addItem(FORMAT_STR("fMP4"), "fragmented_mp4");
+	ui->advOutRecFormat->addItem(FORMAT_STR("fMOV"), "fragmented_mov");
+	ui->advOutRecFormat->addItem(FORMAT_STR("TS"), "mpegts");
+	ui->advOutRecFormat->addItem(FORMAT_STR("HLS"), "hls");
 
 #undef FORMAT_STR
 }
@@ -4983,13 +4983,18 @@ static const unordered_set<string> builtin_codecs = {
 static const unordered_map<string, unordered_set<string>> codec_compat = {
 	// Technically our muxer supports HEVC and AV1 as well, but nothing else does
 	{"flv", {"h264", "aac"}},
-	{"ts", {"h264", "hevc", "aac", "opus"}},
-	{"m3u8",
+	{"mpegts", {"h264", "hevc", "aac", "opus"}},
+	{"hls",
 	 {"h264", "hevc", "aac"}}, // Also using MPEG-TS, but no Opus support
 	{"mov",
 	 {"h264", "hevc", "prores", "aac", "alac", "pcm_s16le", "pcm_s24le",
 	  "pcm_f32le"}},
 	{"mp4", {"h264", "hevc", "av1", "aac", "opus", "alac", "flac"}},
+	{"fragmented_mov",
+	 {"h264", "hevc", "prores", "aac", "alac", "pcm_s16le", "pcm_s24le",
+	  "pcm_f32le"}},
+	{"fragmented_mp4",
+	 {"h264", "hevc", "av1", "aac", "opus", "alac", "flac"}},
 	// MKV supports everything
 	{"mkv", {}},
 };
@@ -5007,12 +5012,12 @@ static bool ContainerSupportsCodec(const string &container, const string &codec)
 	return codecs.count(codec) > 0;
 }
 
-static void DisableIncompatibleCodecs(QComboBox *cbox, const string &format,
+static void DisableIncompatibleCodecs(QComboBox *cbox, const QString &format,
 				      const QString &streamEncoder)
 {
 	QString strEncLabel =
 		QTStr("Basic.Settings.Output.Adv.Recording.UseStreamEncoder");
-	QString formatUpper = QString::fromStdString(format).toUpper();
+	QString formatUpper = format.toUpper();
 	QString recEncoder = cbox->currentData().toString();
 
 	/* Check if selected encoders and output format are compatible, disable incompatible items. */
@@ -5028,11 +5033,14 @@ static void DisableIncompatibleCodecs(QComboBox *cbox, const string &format,
 							   encoderId.c_str());
 		const char *codec = obs_get_encoder_codec(encoderId.c_str());
 
-		bool is_compatible = ContainerSupportsCodec(format, codec);
+		bool is_compatible =
+			ContainerSupportsCodec(format.toStdString(), codec);
 		/* Fall back to FFmpeg check if codec not one of the built-in ones. */
-		if (!is_compatible && !builtin_codecs.count(codec))
-			is_compatible = ff_format_codec_compatible(
-				codec, format.c_str());
+		if (!is_compatible && !builtin_codecs.count(codec)) {
+			string ext = GetFormatExt(QT_TO_UTF8(format));
+			is_compatible =
+				ff_format_codec_compatible(codec, ext.c_str());
+		}
 
 		QStandardItemModel *model =
 			dynamic_cast<QStandardItemModel *>(cbox->model());
@@ -5072,21 +5080,16 @@ void OBSBasicSettings::AdvOutRecCheckCodecs()
 	else
 		ui->advOutRecFormat->setToolTip(nullptr);
 
-	string format = recFormat.toStdString();
-	/* Remove leading "f" for fragmented MP4/MOV */
-	if (format == "fmp4" || format == "fmov")
-		format = format.erase(0, 1);
-
 	QString streamEncoder = ui->advOutEncoder->currentData().toString();
-
 	QString streamAudioEncoder =
 		ui->advOutAEncoder->currentData().toString();
 
 	/* Disable the signals to prevent AdvOutRecCheckWarnings to be called here. */
 	ui->advOutRecEncoder->blockSignals(true);
 	ui->advOutRecAEncoder->blockSignals(true);
-	DisableIncompatibleCodecs(ui->advOutRecEncoder, format, streamEncoder);
-	DisableIncompatibleCodecs(ui->advOutRecAEncoder, format,
+	DisableIncompatibleCodecs(ui->advOutRecEncoder, recFormat,
+				  streamEncoder);
+	DisableIncompatibleCodecs(ui->advOutRecAEncoder, recFormat,
 				  streamAudioEncoder);
 	ui->advOutRecEncoder->blockSignals(false);
 	ui->advOutRecAEncoder->blockSignals(false);
@@ -5722,8 +5725,8 @@ static void DisableIncompatibleSimpleCodecs(QComboBox *cbox,
 			dynamic_cast<QStandardItemModel *>(cbox->model());
 		QStandardItem *item = model->item(idx);
 
-		if (ContainerSupportsCodec(QT_TO_UTF8(format),
-					   QT_TO_UTF8(codec))) {
+		if (ContainerSupportsCodec(format.toStdString(),
+					   codec.toStdString())) {
 			item->setFlags(Qt::ItemIsSelectable |
 				       Qt::ItemIsEnabled);
 		} else {