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; };