diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
index 536504507..b0698b6cd 100644
--- a/UI/data/locale/en-US.ini
+++ b/UI/data/locale/en-US.ini
@@ -941,6 +941,11 @@ Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Audio Encoder Settings (if an
Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxer Settings (if any)"
Basic.Settings.Output.Adv.FFmpeg.GOPSize="Keyframe interval (frames)"
Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Show all codecs (even if potentially incompatible)"
+Basic.Settings.Output.EnableSplitFile="Automatic File Splitting"
+Basic.Settings.Output.SplitFile.TypeTime="Split by Time"
+Basic.Settings.Output.SplitFile.TypeSize="Split by Size"
+Basic.Settings.Output.SplitFile.Time="Split Time"
+Basic.Settings.Output.SplitFile.Size="Split Size"
# Screenshot
Screenshot="Screenshot Output"
diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui
index 4b562a174..1eee870d7 100644
--- a/UI/forms/OBSBasicSettings.ui
+++ b/UI/forms/OBSBasicSettings.ui
@@ -2408,6 +2408,85 @@
-
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::RightToLeft
+
+
+ Basic.Settings.Output.EnableSplitFile
+
+
+
+ -
+
+
+ false
+
+
-
+
+ Basic.Settings.Output.SplitFile.TypeTime
+
+
+ -
+
+ Basic.Settings.Output.SplitFile.TypeSize
+
+
+
+
+ -
+
+
+ Basic.Settings.Output.SplitFile.Time
+
+
+
+ -
+
+
+ s
+
+
+ 5
+
+
+ 21600
+
+
+ 900
+
+
+
+ -
+
+
+ Basic.Settings.Output.SplitFile.Size
+
+
+
+ -
+
+
+ MB
+
+
+ 20
+
+
+ 8192
+
+
+ 2048
+
+
+
-
@@ -5675,6 +5754,10 @@
advOutRecUseRescale
advOutRecRescale
advOutMuxCustom
+ advOutSplitFile
+ advOutSplitFileType
+ advOutSplitFileTime
+ advOutSplitFileSize
advOutFFType
advOutFFRecPath
advOutFFPathBrowse
@@ -6198,5 +6281,21 @@
+
+ advOutSplitFile
+ toggled(bool)
+ advOutSplitFileType
+ setEnabled(bool)
+
+
+ 327
+ 355
+
+
+ 701
+ 355
+
+
+
diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp
index e8eb6f7c4..bd346b949 100644
--- a/UI/window-basic-main-outputs.cpp
+++ b/UI/window-basic-main-outputs.cpp
@@ -110,6 +110,20 @@ static void OBSRecordStopping(void *data, calldata_t *params)
UNUSED_PARAMETER(params);
}
+static void OBSRecordFileChanged(void *data, calldata_t *params)
+{
+ BasicOutputHandler *output = static_cast(data);
+ const char *next_file = calldata_string(params, "next_file");
+
+ QString arg_last_file =
+ QString::fromUtf8(output->lastRecordingPath.c_str());
+
+ QMetaObject::invokeMethod(output->main, "RecordingFileChanged",
+ Q_ARG(QString, arg_last_file));
+
+ output->lastRecordingPath = next_file;
+}
+
static void OBSStartReplayBuffer(void *data, calldata_t *params)
{
BasicOutputHandler *output = static_cast(data);
@@ -1311,6 +1325,8 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
OBSStopRecording, this);
recordStopping.Connect(obs_output_get_signal_handler(fileOutput),
"stopping", OBSRecordStopping, this);
+ recordFileChanged.Connect(obs_output_get_signal_handler(fileOutput),
+ "file_changed", OBSRecordFileChanged, this);
}
void AdvancedOutput::UpdateStreamSettings()
@@ -1834,6 +1850,10 @@ bool AdvancedOutput::StartRecording()
const char *filenameFormat;
bool noSpace = false;
bool overwriteIfExists = false;
+ bool splitFile;
+ const char *splitFileType;
+ int splitFileTime;
+ int splitFileSize;
if (!useStreamEncoder) {
if (!ffmpegOutput) {
@@ -1863,6 +1883,8 @@ bool AdvancedOutput::StartRecording()
ffmpegRecording
? "FFFileNameWithoutSpace"
: "RecFileNameWithoutSpace");
+ splitFile = config_get_bool(main->Config(), "AdvOut",
+ "RecSplitFile");
string strPath = GetRecordingFilename(path, recFormat, noSpace,
overwriteIfExists,
@@ -1873,6 +1895,33 @@ bool AdvancedOutput::StartRecording()
obs_data_set_string(settings, ffmpegRecording ? "url" : "path",
strPath.c_str());
+ if (splitFile) {
+ splitFileType = config_get_string(
+ main->Config(), "AdvOut", "RecSplitFileType");
+ splitFileTime =
+ (astrcmpi(splitFileType, "Time") == 0)
+ ? config_get_int(main->Config(),
+ "AdvOut",
+ "RecSplitFileTime")
+ : 0;
+ splitFileSize =
+ (astrcmpi(splitFileType, "Size") == 0)
+ ? config_get_int(main->Config(),
+ "AdvOut",
+ "RecSplitFileSize")
+ : 0;
+ obs_data_set_string(settings, "directory", path);
+ obs_data_set_string(settings, "format", filenameFormat);
+ obs_data_set_string(settings, "extension", recFormat);
+ obs_data_set_bool(settings, "allow_spaces", !noSpace);
+ obs_data_set_bool(settings, "allow_overwrite",
+ overwriteIfExists);
+ obs_data_set_int(settings, "max_time_sec",
+ splitFileTime);
+ obs_data_set_int(settings, "max_size_mb",
+ splitFileSize);
+ }
+
obs_output_update(fileOutput, settings);
}
diff --git a/UI/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp
index 242b26434..c94fcfbf3 100644
--- a/UI/window-basic-main-outputs.hpp
+++ b/UI/window-basic-main-outputs.hpp
@@ -32,6 +32,7 @@ struct BasicOutputHandler {
OBSSignal streamDelayStarting;
OBSSignal streamStopping;
OBSSignal recordStopping;
+ OBSSignal recordFileChanged;
OBSSignal replayBufferStopping;
OBSSignal replayBufferSaved;
diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp
index 0517ae255..a006c38fd 100644
--- a/UI/window-basic-main.cpp
+++ b/UI/window-basic-main.cpp
@@ -1414,6 +1414,10 @@ bool OBSBasic::InitBasicConfigDefaults()
config_set_default_uint(basicConfig, "AdvOut", "Track5Bitrate", 160);
config_set_default_uint(basicConfig, "AdvOut", "Track6Bitrate", 160);
+ config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileTime", 900);
+ config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileSize",
+ 2048);
+
config_set_default_bool(basicConfig, "AdvOut", "RecRB", false);
config_set_default_uint(basicConfig, "AdvOut", "RecRBTime", 20);
config_set_default_int(basicConfig, "AdvOut", "RecRBSize", 512);
@@ -6947,7 +6951,7 @@ void OBSBasic::StreamingStop(int code, QString last_error)
SetBroadcastFlowEnabled(auth && auth->broadcastFlow());
}
-void OBSBasic::AutoRemux(QString input)
+void OBSBasic::AutoRemux(QString input, bool no_show)
{
bool autoRemux = config_get_bool(Config(), "Video", "AutoRemux");
@@ -6979,7 +6983,8 @@ void OBSBasic::AutoRemux(QString input)
output += "mp4";
OBSRemux *remux = new OBSRemux(QT_TO_UTF8(path), this, true);
- remux->show();
+ if (!no_show)
+ remux->show();
remux->AutoRemux(input, output);
}
@@ -7132,6 +7137,14 @@ void OBSBasic::RecordingStop(int code, QString last_error)
UpdatePause(false);
}
+void OBSBasic::RecordingFileChanged(QString lastRecordingPath)
+{
+ QString str = QTStr("Basic.StatusBar.RecordingSavedTo");
+ ShowStatusBarMessage(str.arg(lastRecordingPath));
+
+ AutoRemux(lastRecordingPath, true);
+}
+
void OBSBasic::ShowReplayBufferPauseWarning()
{
auto msgBox = []() {
diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp
index 9b5b2db2d..2f984d021 100644
--- a/UI/window-basic-main.hpp
+++ b/UI/window-basic-main.hpp
@@ -630,6 +630,7 @@ public slots:
void RecordingStart();
void RecordStopping();
void RecordingStop(int code, QString last_error);
+ void RecordingFileChanged(QString lastRecordingPath);
void ShowReplayBufferPauseWarning();
void StartReplayBuffer();
@@ -797,7 +798,7 @@ private:
static void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed);
- void AutoRemux(QString input);
+ void AutoRemux(QString input, bool no_show = false);
void UpdatePause(bool activate = true);
void UpdateReplayBuffer(bool activate = true);
diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
index 2a34e6283..523c1c599 100644
--- a/UI/window-basic-settings.cpp
+++ b/UI/window-basic-settings.cpp
@@ -457,6 +457,10 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->advOutRecUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutMuxCustom, EDIT_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutSplitFile, CHECK_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutSplitFileType, COMBO_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutSplitFileTime, SCROLL_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutSplitFileSize, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
@@ -769,6 +773,10 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
this, SLOT(SimpleReplayBufferChanged()));
connect(ui->simpleRBSecMax, SIGNAL(valueChanged(int)), this,
SLOT(SimpleReplayBufferChanged()));
+ connect(ui->advOutSplitFile, SIGNAL(stateChanged(int)), this,
+ SLOT(AdvOutSplitFileChanged()));
+ connect(ui->advOutSplitFileType, SIGNAL(currentIndexChanged(int)), this,
+ SLOT(AdvOutSplitFileChanged()));
connect(ui->advReplayBuf, SIGNAL(toggled(bool)), this,
SLOT(AdvReplayBufferChanged()));
connect(ui->advOutRecTrack1, SIGNAL(toggled(bool)), this,
@@ -895,6 +903,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
ui->buttonBox->button(QDialogButtonBox::Cancel)->setIcon(QIcon());
SimpleRecordingQualityChanged();
+ AdvOutSplitFileChanged();
UpdateAutomaticReplayBufferCheckboxes();
@@ -1901,6 +1910,14 @@ void OBSBasicSettings::LoadAdvOutputRecordingSettings()
config_get_string(main->Config(), "AdvOut", "RecMuxerCustom");
int tracks = config_get_int(main->Config(), "AdvOut", "RecTracks");
int flvTrack = config_get_int(main->Config(), "AdvOut", "FLVTrack");
+ bool splitFile =
+ config_get_bool(main->Config(), "AdvOut", "RecSplitFile");
+ const char *splitFileType =
+ config_get_string(main->Config(), "AdvOut", "RecSplitFileType");
+ int splitFileTime =
+ config_get_int(main->Config(), "AdvOut", "RecSplitFileTime");
+ int splitFileSize =
+ config_get_int(main->Config(), "AdvOut", "RecSplitFileSize");
int typeIndex = (astrcmpi(type, "FFmpeg") == 0) ? 1 : 0;
ui->advOutRecType->setCurrentIndex(typeIndex);
@@ -1920,6 +1937,12 @@ void OBSBasicSettings::LoadAdvOutputRecordingSettings()
ui->advOutRecTrack5->setChecked(tracks & (1 << 4));
ui->advOutRecTrack6->setChecked(tracks & (1 << 5));
+ idx = (astrcmpi(splitFileType, "Size") == 0) ? 1 : 0;
+ ui->advOutSplitFile->setChecked(splitFile);
+ ui->advOutSplitFileType->setCurrentIndex(idx);
+ ui->advOutSplitFileTime->setValue(splitFileTime);
+ ui->advOutSplitFileSize->setValue(splitFileSize);
+
switch (flvTrack) {
case 1:
ui->flvTrack1->setChecked(true);
@@ -3378,6 +3401,14 @@ static inline const char *RecTypeFromIdx(int idx)
return "Standard";
}
+static inline const char *SplitFileTypeFromIdx(int idx)
+{
+ if (idx == 1)
+ return "Size";
+ else
+ return "Time";
+}
+
static void WriteJsonData(OBSPropertiesView *view, const char *path)
{
char full_path[512];
@@ -3513,6 +3544,12 @@ void OBSBasicSettings::SaveOutputSettings()
SaveCheckBox(ui->advOutRecUseRescale, "AdvOut", "RecRescale");
SaveCombo(ui->advOutRecRescale, "AdvOut", "RecRescaleRes");
SaveEdit(ui->advOutMuxCustom, "AdvOut", "RecMuxerCustom");
+ SaveCheckBox(ui->advOutSplitFile, "AdvOut", "RecSplitFile");
+ config_set_string(
+ main->Config(), "AdvOut", "RecSplitFileType",
+ SplitFileTypeFromIdx(ui->advOutSplitFileType->currentIndex()));
+ SaveSpinBox(ui->advOutSplitFileTime, "AdvOut", "RecSplitFileTime");
+ SaveSpinBox(ui->advOutSplitFileSize, "AdvOut", "RecSplitFileSize");
config_set_int(
main->Config(), "AdvOut", "RecTracks",
@@ -4414,6 +4451,19 @@ void OBSBasicSettings::AdvancedChanged()
}
}
+void OBSBasicSettings::AdvOutSplitFileChanged()
+{
+ bool splitFile = ui->advOutSplitFile->isChecked();
+ int splitFileType = splitFile ? ui->advOutSplitFileType->currentIndex()
+ : -1;
+
+ ui->advOutSplitFileType->setEnabled(splitFile);
+ ui->advOutSplitFileTimeLabel->setVisible(splitFileType == 0);
+ ui->advOutSplitFileTime->setVisible(splitFileType == 0);
+ ui->advOutSplitFileSizeLabel->setVisible(splitFileType == 1);
+ ui->advOutSplitFileSize->setVisible(splitFileType == 1);
+}
+
void OBSBasicSettings::AdvOutRecCheckWarnings()
{
auto Checked = [](QCheckBox *box) { return box->isChecked() ? 1 : 0; };
diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp
index b27c9e143..73610d9ce 100644
--- a/UI/window-basic-settings.hpp
+++ b/UI/window-basic-settings.hpp
@@ -381,6 +381,7 @@ private slots:
void UpdateAutomaticReplayBufferCheckboxes();
+ void AdvOutSplitFileChanged();
void AdvOutRecCheckWarnings();
void SimpleRecordingQualityChanged();