|
@@ -25,6 +25,7 @@ struct ts_info {
|
|
|
};
|
|
|
|
|
|
#define DEBUG_AUDIO 0
|
|
|
+#define DEBUG_LAGGED_AUDIO 0
|
|
|
#define MAX_BUFFERING_TICKS 45
|
|
|
|
|
|
static void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p)
|
|
@@ -80,20 +81,74 @@ static inline void mix_audio(struct audio_output_data *mixes,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void ignore_audio(obs_source_t *source, size_t channels,
|
|
|
- size_t sample_rate)
|
|
|
+static bool ignore_audio(obs_source_t *source, size_t channels,
|
|
|
+ size_t sample_rate, uint64_t start_ts)
|
|
|
{
|
|
|
size_t num_floats = source->audio_input_buf[0].size / sizeof(float);
|
|
|
+ const char *name = obs_source_get_name(source);
|
|
|
+
|
|
|
+ if (!source->audio_ts && num_floats) {
|
|
|
+#if DEBUG_LAGGED_AUDIO == 1
|
|
|
+ blog(LOG_DEBUG, "[src: %s] no timestamp, but audio available?",
|
|
|
+ name);
|
|
|
+#endif
|
|
|
+ for (size_t ch = 0; ch < channels; ch++)
|
|
|
+ circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
|
|
|
+ source->audio_input_buf[0].size);
|
|
|
+ source->last_audio_input_buf_size = 0;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
if (num_floats) {
|
|
|
+ /* round up the number of samples to drop */
|
|
|
+ size_t drop = util_mul_div64(start_ts - source->audio_ts - 1,
|
|
|
+ sample_rate, 1000000000ULL) +
|
|
|
+ 1;
|
|
|
+ if (drop > num_floats)
|
|
|
+ drop = num_floats;
|
|
|
+
|
|
|
+#if DEBUG_LAGGED_AUDIO == 1
|
|
|
+ blog(LOG_DEBUG,
|
|
|
+ "[src: %s] ignored %" PRIu64 "/%" PRIu64 " samples", name,
|
|
|
+ (uint64_t)drop, (uint64_t)num_floats);
|
|
|
+#endif
|
|
|
for (size_t ch = 0; ch < channels; ch++)
|
|
|
circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
|
|
|
- source->audio_input_buf[ch].size);
|
|
|
+ drop * sizeof(float));
|
|
|
|
|
|
source->last_audio_input_buf_size = 0;
|
|
|
source->audio_ts +=
|
|
|
- util_mul_div64(num_floats, 1000000000ULL, sample_rate);
|
|
|
+ util_mul_div64(drop, 1000000000ULL, sample_rate);
|
|
|
+ blog(LOG_DEBUG, "[src: %s] ts lag after ignoring: %" PRIu64,
|
|
|
+ name, start_ts - source->audio_ts);
|
|
|
+
|
|
|
+ /* rounding error, adjust */
|
|
|
+ if (source->audio_ts == (start_ts - 1))
|
|
|
+ source->audio_ts = start_ts;
|
|
|
+
|
|
|
+ /* source is back in sync */
|
|
|
+ if (source->audio_ts >= start_ts)
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+#if DEBUG_LAGGED_AUDIO == 1
|
|
|
+ blog(LOG_DEBUG, "[src: %s] no samples to ignore! ts = %" PRIu64,
|
|
|
+ name, source->audio_ts);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!source->audio_pending || num_floats) {
|
|
|
+ blog(LOG_WARNING,
|
|
|
+ "Source %s audio is lagging (over by %.02f ms) "
|
|
|
+ "at max audio buffering. Restarting source audio.",
|
|
|
+ name, (start_ts - source->audio_ts) / 1000000.);
|
|
|
}
|
|
|
+
|
|
|
+ source->audio_pending = true;
|
|
|
+ source->audio_ts = 0;
|
|
|
+ /* tell the timestamp adjustment code in source_output_audio_data to
|
|
|
+ * reset everything, and hopefully fix the timestamps */
|
|
|
+ source->timing_set = false;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
static bool discard_if_stopped(obs_source_t *source, size_t channels)
|
|
@@ -145,6 +200,8 @@ static inline void discard_audio(struct obs_core_audio *audio,
|
|
|
{
|
|
|
size_t total_floats = AUDIO_OUTPUT_FRAMES;
|
|
|
size_t size;
|
|
|
+ /* debug assert only */
|
|
|
+ UNUSED_PARAMETER(audio);
|
|
|
|
|
|
#if DEBUG_AUDIO == 1
|
|
|
bool is_audio_source = source->info.output_flags & OBS_SOURCE_AUDIO;
|
|
@@ -180,9 +237,13 @@ static inline void discard_audio(struct obs_core_audio *audio,
|
|
|
"start timestamp (%" PRIu64 ")",
|
|
|
source->audio_ts, ts->start);
|
|
|
}
|
|
|
+
|
|
|
+ /* ignore_audio should have already run and marked this source
|
|
|
+ * pending, unless we *just* added buffering */
|
|
|
+ assert(audio->total_buffering_ticks < MAX_BUFFERING_TICKS ||
|
|
|
+ source->audio_pending || !source->audio_ts ||
|
|
|
+ audio->buffering_wait_ticks);
|
|
|
#endif
|
|
|
- if (audio->total_buffering_ticks == MAX_BUFFERING_TICKS)
|
|
|
- ignore_audio(source, channels, sample_rate);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -436,6 +497,38 @@ bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in,
|
|
|
obs_source_t *source = audio->render_order.array[i];
|
|
|
obs_source_audio_render(source, mixers, channels, sample_rate,
|
|
|
audio_size);
|
|
|
+
|
|
|
+ /* if a source has gone backward in time and we can no
|
|
|
+ * longer buffer, drop some or all of its audio */
|
|
|
+ if (audio->total_buffering_ticks == MAX_BUFFERING_TICKS &&
|
|
|
+ source->audio_ts < ts.start) {
|
|
|
+ if (source->info.audio_render) {
|
|
|
+ blog(LOG_DEBUG,
|
|
|
+ "render audio source %s timestamp has "
|
|
|
+ "gone backwards",
|
|
|
+ obs_source_get_name(source));
|
|
|
+
|
|
|
+ /* just avoid further damage */
|
|
|
+ source->audio_pending = true;
|
|
|
+#if DEBUG_AUDIO == 1
|
|
|
+ /* this should really be fixed */
|
|
|
+ assert(false);
|
|
|
+#endif
|
|
|
+ } else {
|
|
|
+ pthread_mutex_lock(&source->audio_buf_mutex);
|
|
|
+ bool rerender = ignore_audio(source, channels,
|
|
|
+ sample_rate,
|
|
|
+ ts.start);
|
|
|
+ pthread_mutex_unlock(&source->audio_buf_mutex);
|
|
|
+
|
|
|
+ /* if we (potentially) recovered, re-render */
|
|
|
+ if (rerender)
|
|
|
+ obs_source_audio_render(source, mixers,
|
|
|
+ channels,
|
|
|
+ sample_rate,
|
|
|
+ audio_size);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------ */
|