浏览代码

obs-ffmpeg, UI: Reset timestamps at splitting file

This commit adds a setting to reset timestamps when splitting files.
Some NLEs cannot handle video files whose starting timestamp is not
zero. Default is enabed.
Norihiro Kamae 3 年之前
父节点
当前提交
550b1331ac

+ 1 - 0
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.TypeSize="Split by Size"
 Basic.Settings.Output.SplitFile.Time="Split Time"
 Basic.Settings.Output.SplitFile.Time="Split Time"
 Basic.Settings.Output.SplitFile.Size="Split Size"
 Basic.Settings.Output.SplitFile.Size="Split Size"
+Basic.Settings.Output.SplitFile.ResetTimestamps="Reset timestamps at the beginning of each split file"
 
 
 # Screenshot
 # Screenshot
 Screenshot="Screenshot Output"
 Screenshot="Screenshot Output"

+ 11 - 0
UI/forms/OBSBasicSettings.ui

@@ -2487,6 +2487,16 @@
                               </property>
                               </property>
                              </widget>
                              </widget>
                             </item>
                             </item>
+                            <item row="10" column="1">
+                             <widget class="QCheckBox" name="advOutSplitFileRstTS">
+                              <property name="text">
+                               <string>Basic.Settings.Output.SplitFile.ResetTimestamps</string>
+                              </property>
+                              <property name="checked">
+                               <bool>true</bool>
+                              </property>
+                             </widget>
+                            </item>
                             <item row="3" column="1">
                             <item row="3" column="1">
                              <widget class="QStackedWidget" name="advRecTrackWidget">
                              <widget class="QStackedWidget" name="advRecTrackWidget">
                               <property name="sizePolicy">
                               <property name="sizePolicy">
@@ -5758,6 +5768,7 @@
   <tabstop>advOutSplitFileType</tabstop>
   <tabstop>advOutSplitFileType</tabstop>
   <tabstop>advOutSplitFileTime</tabstop>
   <tabstop>advOutSplitFileTime</tabstop>
   <tabstop>advOutSplitFileSize</tabstop>
   <tabstop>advOutSplitFileSize</tabstop>
+  <tabstop>advOutSplitFileRstTS</tabstop>
   <tabstop>advOutFFType</tabstop>
   <tabstop>advOutFFType</tabstop>
   <tabstop>advOutFFRecPath</tabstop>
   <tabstop>advOutFFRecPath</tabstop>
   <tabstop>advOutFFPathBrowse</tabstop>
   <tabstop>advOutFFPathBrowse</tabstop>

+ 6 - 0
UI/window-basic-main-outputs.cpp

@@ -1854,6 +1854,7 @@ bool AdvancedOutput::StartRecording()
 	const char *splitFileType;
 	const char *splitFileType;
 	int splitFileTime;
 	int splitFileTime;
 	int splitFileSize;
 	int splitFileSize;
+	bool splitFileResetTimestamps;
 
 
 	if (!useStreamEncoder) {
 	if (!useStreamEncoder) {
 		if (!ffmpegOutput) {
 		if (!ffmpegOutput) {
@@ -1910,6 +1911,9 @@ bool AdvancedOutput::StartRecording()
 							 "AdvOut",
 							 "AdvOut",
 							 "RecSplitFileSize")
 							 "RecSplitFileSize")
 					: 0;
 					: 0;
+			splitFileResetTimestamps =
+				config_get_bool(main->Config(), "AdvOut",
+						"RecSplitFileResetTimestamps");
 			obs_data_set_string(settings, "directory", path);
 			obs_data_set_string(settings, "directory", path);
 			obs_data_set_string(settings, "format", filenameFormat);
 			obs_data_set_string(settings, "format", filenameFormat);
 			obs_data_set_string(settings, "extension", recFormat);
 			obs_data_set_string(settings, "extension", recFormat);
@@ -1920,6 +1924,8 @@ bool AdvancedOutput::StartRecording()
 					 splitFileTime);
 					 splitFileTime);
 			obs_data_set_int(settings, "max_size_mb",
 			obs_data_set_int(settings, "max_size_mb",
 					 splitFileSize);
 					 splitFileSize);
+			obs_data_set_bool(settings, "reset_timestamps",
+					  splitFileResetTimestamps);
 		}
 		}
 
 
 		obs_output_update(fileOutput, settings);
 		obs_output_update(fileOutput, settings);

+ 2 - 0
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", "RecSplitFileTime", 900);
 	config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileSize",
 	config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileSize",
 				2048);
 				2048);
+	config_set_default_bool(basicConfig, "AdvOut",
+				"RecSplitFileResetTimestamps", true);
 
 
 	config_set_default_bool(basicConfig, "AdvOut", "RecRB", false);
 	config_set_default_bool(basicConfig, "AdvOut", "RecRB", false);
 	config_set_default_uint(basicConfig, "AdvOut", "RecRBTime", 20);
 	config_set_default_uint(basicConfig, "AdvOut", "RecRBTime", 20);

+ 7 - 0
UI/window-basic-settings.cpp

@@ -461,6 +461,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
 	HookWidget(ui->advOutSplitFileType,  COMBO_CHANGED,  OUTPUTS_CHANGED);
 	HookWidget(ui->advOutSplitFileType,  COMBO_CHANGED,  OUTPUTS_CHANGED);
 	HookWidget(ui->advOutSplitFileTime,  SCROLL_CHANGED, OUTPUTS_CHANGED);
 	HookWidget(ui->advOutSplitFileTime,  SCROLL_CHANGED, OUTPUTS_CHANGED);
 	HookWidget(ui->advOutSplitFileSize,  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->advOutRecTrack1,      CHECK_CHANGED,  OUTPUTS_CHANGED);
 	HookWidget(ui->advOutRecTrack2,      CHECK_CHANGED,  OUTPUTS_CHANGED);
 	HookWidget(ui->advOutRecTrack2,      CHECK_CHANGED,  OUTPUTS_CHANGED);
 	HookWidget(ui->advOutRecTrack3,      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");
 		config_get_int(main->Config(), "AdvOut", "RecSplitFileTime");
 	int splitFileSize =
 	int splitFileSize =
 		config_get_int(main->Config(), "AdvOut", "RecSplitFileSize");
 		config_get_int(main->Config(), "AdvOut", "RecSplitFileSize");
+	bool splitFileResetTimestamps = config_get_bool(
+		main->Config(), "AdvOut", "RecSplitFileResetTimestamps");
 
 
 	int typeIndex = (astrcmpi(type, "FFmpeg") == 0) ? 1 : 0;
 	int typeIndex = (astrcmpi(type, "FFmpeg") == 0) ? 1 : 0;
 	ui->advOutRecType->setCurrentIndex(typeIndex);
 	ui->advOutRecType->setCurrentIndex(typeIndex);
@@ -1942,6 +1945,7 @@ void OBSBasicSettings::LoadAdvOutputRecordingSettings()
 	ui->advOutSplitFileType->setCurrentIndex(idx);
 	ui->advOutSplitFileType->setCurrentIndex(idx);
 	ui->advOutSplitFileTime->setValue(splitFileTime);
 	ui->advOutSplitFileTime->setValue(splitFileTime);
 	ui->advOutSplitFileSize->setValue(splitFileSize);
 	ui->advOutSplitFileSize->setValue(splitFileSize);
+	ui->advOutSplitFileRstTS->setChecked(splitFileResetTimestamps);
 
 
 	switch (flvTrack) {
 	switch (flvTrack) {
 	case 1:
 	case 1:
@@ -3550,6 +3554,8 @@ void OBSBasicSettings::SaveOutputSettings()
 		SplitFileTypeFromIdx(ui->advOutSplitFileType->currentIndex()));
 		SplitFileTypeFromIdx(ui->advOutSplitFileType->currentIndex()));
 	SaveSpinBox(ui->advOutSplitFileTime, "AdvOut", "RecSplitFileTime");
 	SaveSpinBox(ui->advOutSplitFileTime, "AdvOut", "RecSplitFileTime");
 	SaveSpinBox(ui->advOutSplitFileSize, "AdvOut", "RecSplitFileSize");
 	SaveSpinBox(ui->advOutSplitFileSize, "AdvOut", "RecSplitFileSize");
+	SaveCheckBox(ui->advOutSplitFileRstTS, "AdvOut",
+		     "RecSplitFileResetTimestamps");
 
 
 	config_set_int(
 	config_set_int(
 		main->Config(), "AdvOut", "RecTracks",
 		main->Config(), "AdvOut", "RecTracks",
@@ -4462,6 +4468,7 @@ void OBSBasicSettings::AdvOutSplitFileChanged()
 	ui->advOutSplitFileTime->setVisible(splitFileType == 0);
 	ui->advOutSplitFileTime->setVisible(splitFileType == 0);
 	ui->advOutSplitFileSizeLabel->setVisible(splitFileType == 1);
 	ui->advOutSplitFileSizeLabel->setVisible(splitFileType == 1);
 	ui->advOutSplitFileSize->setVisible(splitFileType == 1);
 	ui->advOutSplitFileSize->setVisible(splitFileType == 1);
+	ui->advOutSplitFileRstTS->setVisible(splitFile);
 }
 }
 
 
 void OBSBasicSettings::AdvOutRecCheckWarnings()
 void OBSBasicSettings::AdvOutRecCheckWarnings()

+ 48 - 0
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);
 	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)
 static bool ffmpeg_mux_start(void *data)
 {
 {
 	struct ffmpeg_muxer *stream = data;
 	struct ffmpeg_muxer *stream = data;
@@ -328,6 +357,7 @@ static bool ffmpeg_mux_start(void *data)
 			return false;
 			return false;
 		path = obs_service_get_url(service);
 		path = obs_service_get_url(service);
 		stream->split_file = false;
 		stream->split_file = false;
+		stream->reset_timestamps = false;
 	} else {
 	} else {
 		path = obs_data_get_string(settings, "path");
 		path = obs_data_get_string(settings, "path");
 
 
@@ -337,12 +367,16 @@ static bool ffmpeg_mux_start(void *data)
 				   (1024 * 1024);
 				   (1024 * 1024);
 		stream->split_file = stream->max_time > 0 ||
 		stream->split_file = stream->max_time > 0 ||
 				     stream->max_size > 0;
 				     stream->max_size > 0;
+		stream->reset_timestamps =
+			obs_data_get_bool(settings, "reset_timestamps");
 		stream->allow_overwrite =
 		stream->allow_overwrite =
 			obs_data_get_bool(settings, "allow_overwrite");
 			obs_data_get_bool(settings, "allow_overwrite");
 		stream->cur_size = 0;
 		stream->cur_size = 0;
 		stream->sent_headers = false;
 		stream->sent_headers = false;
 	}
 	}
 
 
+	ts_offset_clear(stream);
+
 	if (!stream->is_network) {
 	if (!stream->is_network) {
 		/* ensure output path is writable to avoid generic error
 		/* ensure output path is writable to avoid generic error
 		 * message.
 		 * message.
@@ -545,6 +579,16 @@ bool write_packet(struct ffmpeg_muxer *stream, struct encoder_packet *packet)
 							: FFM_PACKET_AUDIO,
 							: FFM_PACKET_AUDIO,
 				       .keyframe = packet->keyframe};
 				       .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,
 	ret = os_process_pipe_write(stream->pipe, (const uint8_t *)&info,
 				    sizeof(info));
 				    sizeof(info));
 	if (ret != 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_size = 0;
 	stream->cur_time = packet->dts_usec;
 	stream->cur_time = packet->dts_usec;
+	ts_offset_clear(stream);
 
 
 	return true;
 	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);
 	write_packet(stream, packet);
 }
 }
 
 

+ 7 - 0
plugins/obs-ffmpeg/obs-ffmpeg-mux.h

@@ -37,6 +37,12 @@ struct ffmpeg_muxer {
 	volatile bool muxing;
 	volatile bool muxing;
 	DARRAY(struct encoder_packet) mux_packets;
 	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 */
 	/* these are accessed both by replay buffer and by HLS */
 	pthread_t mux_thread;
 	pthread_t mux_thread;
 	bool mux_thread_joinable;
 	bool mux_thread_joinable;
@@ -54,6 +60,7 @@ struct ffmpeg_muxer {
 
 
 	bool is_network;
 	bool is_network;
 	bool split_file;
 	bool split_file;
+	bool reset_timestamps;
 	bool allow_overwrite;
 	bool allow_overwrite;
 };
 };