Browse Source

Detect audio timestamp jumps and reset timing

 - Often, timestamps will go "back" in time with certain..  terrible
   devices that no one should use.  When this occurs, timing is now
   reset so that the new audio comes in directly after the old audio
   seamlessly.
jp9000 11 years ago
parent
commit
2a89306420
2 changed files with 34 additions and 4 deletions
  1. 33 4
      libobs/obs-source.c
  2. 1 0
      libobs/obs-source.h

+ 33 - 4
libobs/obs-source.c

@@ -324,8 +324,23 @@ void obs_source_video_tick(obs_source_t source, float seconds)
 		source->callbacks.video_tick(source->data, seconds);
 }
 
+static inline uint64_t conv_frames_to_time(obs_source_t source, size_t frames)
+{
+	const struct audio_info *info = audio_output_getinfo(obs->audio.audio);
+	double sps_to_ns = 1000000000.0 / (double)info->samples_per_sec;
+	return (uint64_t)((double)frames * sps_to_ns);
+}
+
 /* maximum "direct" timestamp variance in nanoseconds */
-#define MAX_VARIANCE 2000000000ULL
+#define MAX_TS_VAR         2000000000ULL
+/* maximum time that timestamp can jump in nanoseconds */
+#define MAX_TIMESTAMP_JUMP 500000000ULL
+
+static inline void reset_audio_timing(obs_source_t source, uint64_t timetamp)
+{
+	source->timing_set    = true;
+	source->timing_adjust = os_gettime_ns() - timetamp;
+}
 
 static void source_output_audio_line(obs_source_t source,
 		const struct audio_data *data)
@@ -333,15 +348,29 @@ static void source_output_audio_line(obs_source_t source,
 	struct audio_data in = *data;
 
 	if (!source->timing_set) {
-		source->timing_set    = true;
-		source->timing_adjust = os_gettime_ns() - in.timestamp;
+		reset_audio_timing(source, in.timestamp);
 
 		/* detects 'directly' set timestamps as long as they're within
 		 * a certain threshold */
-		if ((source->timing_adjust+MAX_VARIANCE) < MAX_VARIANCE*2)
+		if ((source->timing_adjust + MAX_TS_VAR) < MAX_TS_VAR * 2)
 			source->timing_adjust = 0;
+	} else {
+		uint64_t time_diff =
+			data->timestamp - source->next_audio_timestamp_min;
+
+		/* don't need signed because negative will trigger it
+		 * regardless, which is what we want */
+		if (time_diff > MAX_TIMESTAMP_JUMP) {
+			blog(LOG_DEBUG, "Audio timestamp for source '%s' "
+			                "jumped by '%lld', resetting audio "
+			                "timing", source->name, time_diff);
+			reset_audio_timing(source, in.timestamp);
+		}
 	}
 
+	source->next_audio_timestamp_min = in.timestamp +
+		conv_frames_to_time(source, in.frames);
+
 	in.timestamp += source->timing_adjust;
 	in.volume = source->volume;
 	audio_line_output(source->audio_line, &in);

+ 1 - 0
libobs/obs-source.h

@@ -223,6 +223,7 @@ struct obs_source {
 	/* timing (if video is present, is based upon video) */
 	bool                         timing_set;
 	uint64_t                     timing_adjust;
+	uint64_t                     next_audio_timestamp_min;
 	uint64_t                     last_frame_timestamp;
 	uint64_t                     last_sys_timestamp;