UI: Add automatic file splitting
This commit implements a new feature to split recordings in split files in Advanced output mode. These basic settings are implemented. - Enable/disable the feature. Default is disabled. - Select a type of the limit, time or size. - Specifies the limit in seconds or MiB.master
parent
ce92f441b5
commit
0e81c66f6e
|
@ -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"
|
||||
|
|
|
@ -2408,6 +2408,85 @@
|
|||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="advOutMuxCustom"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="advOutSplitFile">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Basic.Settings.Output.EnableSplitFile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QComboBox" name="advOutSplitFileType">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Basic.Settings.Output.SplitFile.TypeTime</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Basic.Settings.Output.SplitFile.TypeSize</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="advOutSplitFileTimeLabel">
|
||||
<property name="text">
|
||||
<string>Basic.Settings.Output.SplitFile.Time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QSpinBox" name="advOutSplitFileTime">
|
||||
<property name="suffix">
|
||||
<string> s</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>21600</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>900</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="advOutSplitFileSizeLabel">
|
||||
<property name="text">
|
||||
<string>Basic.Settings.Output.SplitFile.Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QSpinBox" name="advOutSplitFileSize">
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>8192</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2048</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QStackedWidget" name="advRecTrackWidget">
|
||||
<property name="sizePolicy">
|
||||
|
@ -5675,6 +5754,10 @@
|
|||
<tabstop>advOutRecUseRescale</tabstop>
|
||||
<tabstop>advOutRecRescale</tabstop>
|
||||
<tabstop>advOutMuxCustom</tabstop>
|
||||
<tabstop>advOutSplitFile</tabstop>
|
||||
<tabstop>advOutSplitFileType</tabstop>
|
||||
<tabstop>advOutSplitFileTime</tabstop>
|
||||
<tabstop>advOutSplitFileSize</tabstop>
|
||||
<tabstop>advOutFFType</tabstop>
|
||||
<tabstop>advOutFFRecPath</tabstop>
|
||||
<tabstop>advOutFFPathBrowse</tabstop>
|
||||
|
@ -6198,5 +6281,21 @@
|
|||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>advOutSplitFile</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>advOutSplitFileType</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>327</x>
|
||||
<y>355</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>701</x>
|
||||
<y>355</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
|
@ -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<BasicOutputHandler *>(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<BasicOutputHandler *>(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);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ struct BasicOutputHandler {
|
|||
OBSSignal streamDelayStarting;
|
||||
OBSSignal streamStopping;
|
||||
OBSSignal recordStopping;
|
||||
OBSSignal recordFileChanged;
|
||||
OBSSignal replayBufferStopping;
|
||||
OBSSignal replayBufferSaved;
|
||||
|
||||
|
|
|
@ -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,6 +6983,7 @@ void OBSBasic::AutoRemux(QString input)
|
|||
output += "mp4";
|
||||
|
||||
OBSRemux *remux = new OBSRemux(QT_TO_UTF8(path), this, true);
|
||||
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 = []() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -381,6 +381,7 @@ private slots:
|
|||
|
||||
void UpdateAutomaticReplayBufferCheckboxes();
|
||||
|
||||
void AdvOutSplitFileChanged();
|
||||
void AdvOutRecCheckWarnings();
|
||||
|
||||
void SimpleRecordingQualityChanged();
|
||||
|
|
Loading…
Reference in New Issue