Преглед изворни кода

libobs: Reduce synchronization limit for multiple audio tracks

Since 65eb3c0815929866dde7d398bec7738d21e9b211 we tried to get as close
a sync between audio and video tracks as we could before starting to
send frames to the output.

But in be717dbb2c19aa0e91a164fa52e3d4467b64be9d when multi-track audio
was considered for synchronization it continued to try and line ALL
audio packets up with one video packet. While audio and video packets
are similar size this will work out. But once video packets duration is
smaller than 1/2 audio packet duration this may fail forever. In
practice with AAC's 20-23ms frame duration we can often get tracks
producing frames 5-10ms out of phase. For 60fps video this mostly fine
as 16ms frame duration will cover the gap, but for framerates as low as
100 its possible to fail to synchronize. In practice this is ~50%
for 6 audio tracks and 120fps video on my system, or 100% at 240fps.
Kurt Kartaltepe пре 2 година
родитељ
комит
b7f29ca767
1 измењених фајлова са 25 додато и 1 уклоњено
  1. 25 1
      libobs/obs-output.c

+ 25 - 1
libobs/obs-output.c

@@ -1388,14 +1388,21 @@ static size_t get_interleaved_start_idx(struct obs_output *output)
 	return video_idx < idx ? video_idx : idx;
 }
 
+static int64_t get_encoder_duration(struct obs_encoder *encoder)
+{
+	return (encoder->timebase_num * 1000000LL / encoder->timebase_den) *
+	       encoder->framesize;
+}
+
 static int prune_premature_packets(struct obs_output *output)
 {
 	struct encoder_packet *video;
 	int video_idx;
 	int max_idx;
-	int64_t duration_usec;
+	int64_t duration_usec, max_audio_duration_usec = 0;
 	int64_t max_diff = 0;
 	int64_t diff = 0;
+	int audio_encoders = 0;
 
 	video_idx = find_first_packet_type_idx(output, OBS_ENCODER_VIDEO, 0);
 	if (video_idx == -1) {
@@ -1410,9 +1417,11 @@ static int prune_premature_packets(struct obs_output *output)
 	for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
 		struct encoder_packet *audio;
 		int audio_idx;
+		int64_t audio_duration_usec = 0;
 
 		if (!output->audio_encoders[i])
 			continue;
+		audio_encoders++;
 
 		audio_idx = find_first_packet_type_idx(output,
 						       OBS_ENCODER_AUDIO, i);
@@ -1428,6 +1437,21 @@ static int prune_premature_packets(struct obs_output *output)
 		diff = audio->dts_usec - video->dts_usec;
 		if (diff > max_diff)
 			max_diff = diff;
+
+		audio_duration_usec =
+			get_encoder_duration(output->audio_encoders[i]);
+		if (audio_duration_usec > max_audio_duration_usec)
+			max_audio_duration_usec = audio_duration_usec;
+	}
+
+	/* Once multiple audio encoders are running they are almost always out
+	 * of phase by ~Xms. If users change their video to > 100fps then it
+	 * becomes probable that this phase difference will be larger than the
+	 * video duration preventing us from ever finding a synchronization
+	 * point due to their larger frame duration. Instead give up on a tight
+	 * video sync. */
+	if (audio_encoders > 1 && duration_usec < max_audio_duration_usec) {
+		duration_usec = max_audio_duration_usec;
 	}
 
 	return diff > duration_usec ? max_idx + 1 : 0;