diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
index b0698b6cd..526dd0229 100644
--- a/UI/data/locale/en-US.ini
+++ b/UI/data/locale/en-US.ini
@@ -946,6 +946,7 @@ 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"
+Basic.Settings.Output.SplitFile.ResetTimestamps="Reset timestamps at the beginning of each split file"
# Screenshot
Screenshot="Screenshot Output"
diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui
index 1eee870d7..02a8bf258 100644
--- a/UI/forms/OBSBasicSettings.ui
+++ b/UI/forms/OBSBasicSettings.ui
@@ -2487,6 +2487,16 @@
+ -
+
+
+ Basic.Settings.Output.SplitFile.ResetTimestamps
+
+
+ true
+
+
+
-
@@ -5758,6 +5768,7 @@
advOutSplitFileType
advOutSplitFileTime
advOutSplitFileSize
+ advOutSplitFileRstTS
advOutFFType
advOutFFRecPath
advOutFFPathBrowse
diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp
index bd346b949..033062378 100644
--- a/UI/window-basic-main-outputs.cpp
+++ b/UI/window-basic-main-outputs.cpp
@@ -1854,6 +1854,7 @@ bool AdvancedOutput::StartRecording()
const char *splitFileType;
int splitFileTime;
int splitFileSize;
+ bool splitFileResetTimestamps;
if (!useStreamEncoder) {
if (!ffmpegOutput) {
@@ -1910,6 +1911,9 @@ bool AdvancedOutput::StartRecording()
"AdvOut",
"RecSplitFileSize")
: 0;
+ splitFileResetTimestamps =
+ config_get_bool(main->Config(), "AdvOut",
+ "RecSplitFileResetTimestamps");
obs_data_set_string(settings, "directory", path);
obs_data_set_string(settings, "format", filenameFormat);
obs_data_set_string(settings, "extension", recFormat);
@@ -1920,6 +1924,8 @@ bool AdvancedOutput::StartRecording()
splitFileTime);
obs_data_set_int(settings, "max_size_mb",
splitFileSize);
+ obs_data_set_bool(settings, "reset_timestamps",
+ splitFileResetTimestamps);
}
obs_output_update(fileOutput, settings);
diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp
index a006c38fd..f02371f3b 100644
--- a/UI/window-basic-main.cpp
+++ b/UI/window-basic-main.cpp
@@ -1417,6 +1417,8 @@ bool OBSBasic::InitBasicConfigDefaults()
config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileTime", 900);
config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileSize",
2048);
+ config_set_default_bool(basicConfig, "AdvOut",
+ "RecSplitFileResetTimestamps", true);
config_set_default_bool(basicConfig, "AdvOut", "RecRB", false);
config_set_default_uint(basicConfig, "AdvOut", "RecRBTime", 20);
diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
index 523c1c599..8420f6efb 100644
--- a/UI/window-basic-settings.cpp
+++ b/UI/window-basic-settings.cpp
@@ -461,6 +461,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->advOutSplitFileType, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutSplitFileTime, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutSplitFileSize, SCROLL_CHANGED, OUTPUTS_CHANGED);
+ HookWidget(ui->advOutSplitFileRstTS, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
@@ -1918,6 +1919,8 @@ void OBSBasicSettings::LoadAdvOutputRecordingSettings()
config_get_int(main->Config(), "AdvOut", "RecSplitFileTime");
int splitFileSize =
config_get_int(main->Config(), "AdvOut", "RecSplitFileSize");
+ bool splitFileResetTimestamps = config_get_bool(
+ main->Config(), "AdvOut", "RecSplitFileResetTimestamps");
int typeIndex = (astrcmpi(type, "FFmpeg") == 0) ? 1 : 0;
ui->advOutRecType->setCurrentIndex(typeIndex);
@@ -1942,6 +1945,7 @@ void OBSBasicSettings::LoadAdvOutputRecordingSettings()
ui->advOutSplitFileType->setCurrentIndex(idx);
ui->advOutSplitFileTime->setValue(splitFileTime);
ui->advOutSplitFileSize->setValue(splitFileSize);
+ ui->advOutSplitFileRstTS->setChecked(splitFileResetTimestamps);
switch (flvTrack) {
case 1:
@@ -3550,6 +3554,8 @@ void OBSBasicSettings::SaveOutputSettings()
SplitFileTypeFromIdx(ui->advOutSplitFileType->currentIndex()));
SaveSpinBox(ui->advOutSplitFileTime, "AdvOut", "RecSplitFileTime");
SaveSpinBox(ui->advOutSplitFileSize, "AdvOut", "RecSplitFileSize");
+ SaveCheckBox(ui->advOutSplitFileRstTS, "AdvOut",
+ "RecSplitFileResetTimestamps");
config_set_int(
main->Config(), "AdvOut", "RecTracks",
@@ -4462,6 +4468,7 @@ void OBSBasicSettings::AdvOutSplitFileChanged()
ui->advOutSplitFileTime->setVisible(splitFileType == 0);
ui->advOutSplitFileSizeLabel->setVisible(splitFileType == 1);
ui->advOutSplitFileSize->setVisible(splitFileType == 1);
+ ui->advOutSplitFileRstTS->setVisible(splitFile);
}
void OBSBasicSettings::AdvOutRecCheckWarnings()
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c
index 18eae54d7..9bd0f5b0c 100644
--- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c
+++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c
@@ -309,6 +309,35 @@ static void set_file_not_readable_error(struct ffmpeg_muxer *stream,
obs_data_release(settings);
}
+inline static void ts_offset_clear(struct ffmpeg_muxer *stream)
+{
+ stream->found_video = false;
+ stream->video_pts_offset = 0;
+
+ for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
+ stream->found_audio[i] = false;
+ stream->audio_dts_offsets[i] = 0;
+ }
+}
+
+inline static void ts_offset_update(struct ffmpeg_muxer *stream,
+ struct encoder_packet *packet)
+{
+ if (packet->type == OBS_ENCODER_VIDEO) {
+ if (!stream->found_video) {
+ stream->video_pts_offset = packet->pts;
+ stream->found_video = true;
+ }
+ return;
+ }
+
+ if (stream->found_audio[packet->track_idx])
+ return;
+
+ stream->audio_dts_offsets[packet->track_idx] = packet->dts;
+ stream->found_audio[packet->track_idx] = true;
+}
+
static bool ffmpeg_mux_start(void *data)
{
struct ffmpeg_muxer *stream = data;
@@ -328,6 +357,7 @@ static bool ffmpeg_mux_start(void *data)
return false;
path = obs_service_get_url(service);
stream->split_file = false;
+ stream->reset_timestamps = false;
} else {
path = obs_data_get_string(settings, "path");
@@ -337,12 +367,16 @@ static bool ffmpeg_mux_start(void *data)
(1024 * 1024);
stream->split_file = stream->max_time > 0 ||
stream->max_size > 0;
+ stream->reset_timestamps =
+ obs_data_get_bool(settings, "reset_timestamps");
stream->allow_overwrite =
obs_data_get_bool(settings, "allow_overwrite");
stream->cur_size = 0;
stream->sent_headers = false;
}
+ ts_offset_clear(stream);
+
if (!stream->is_network) {
/* ensure output path is writable to avoid generic error
* message.
@@ -545,6 +579,16 @@ bool write_packet(struct ffmpeg_muxer *stream, struct encoder_packet *packet)
: FFM_PACKET_AUDIO,
.keyframe = packet->keyframe};
+ if (stream->split_file && stream->reset_timestamps) {
+ if (is_video) {
+ info.dts -= stream->video_pts_offset;
+ info.pts -= stream->video_pts_offset;
+ } else {
+ info.dts -= stream->audio_dts_offsets[info.index];
+ info.pts -= stream->audio_dts_offsets[info.index];
+ }
+ }
+
ret = os_process_pipe_write(stream->pipe, (const uint8_t *)&info,
sizeof(info));
if (ret != sizeof(info)) {
@@ -682,6 +726,7 @@ static bool prepare_split_file(struct ffmpeg_muxer *stream,
stream->cur_size = 0;
stream->cur_time = packet->dts_usec;
+ ts_offset_clear(stream);
return true;
}
@@ -721,6 +766,9 @@ static void ffmpeg_mux_data(void *data, struct encoder_packet *packet)
}
}
+ if (stream->split_file && stream->reset_timestamps)
+ ts_offset_update(stream, packet);
+
write_packet(stream, packet);
}
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.h b/plugins/obs-ffmpeg/obs-ffmpeg-mux.h
index d0de39272..7d8a55add 100644
--- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.h
+++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.h
@@ -37,6 +37,12 @@ struct ffmpeg_muxer {
volatile bool muxing;
DARRAY(struct encoder_packet) mux_packets;
+ /* split file */
+ bool found_video;
+ bool found_audio[MAX_AUDIO_MIXES];
+ int64_t video_pts_offset;
+ int64_t audio_dts_offsets[MAX_AUDIO_MIXES];
+
/* these are accessed both by replay buffer and by HLS */
pthread_t mux_thread;
bool mux_thread_joinable;
@@ -54,6 +60,7 @@ struct ffmpeg_muxer {
bool is_network;
bool split_file;
+ bool reset_timestamps;
bool allow_overwrite;
};