From da053365db3a98c3f6aa9034e093be6cb48dde8f Mon Sep 17 00:00:00 2001 From: floele Date: Mon, 21 Feb 2022 22:29:12 +0100 Subject: [PATCH 1/2] libobs: Fix corruption of audio data in audio_input_buf Previously, source->next_audio_ts_min was incremented by conv_frames_to_time(sample_rate, in.frames) in each call of source_output_audio_data(), so only by the duration of the audio data itself. However, this does not take the current timestamp into account, which also includes execution time (audio source timestamps are not necessarily continuous). This causes the incoming audio timestamp to gradually deviate further from the expected next_audio_ts_min timestamp, eventually exceeding TS_SMOOTHING_THRESHOLD and then causing a call to source_output_audio_place() which either overwrites existing audio data and causes a glitch or puts data beyond the end of the buffer, inserting a gap and thus causing a dropout. Now use incoming timestamp for calculation of the next minimum timestamp. --- libobs/obs-source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-source.c b/libobs/obs-source.c index d2c25cb1cd453c..3fc979ca65968c 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -1481,7 +1481,7 @@ static void source_output_audio_data(obs_source_t *source, source->last_audio_ts = in.timestamp; source->next_audio_ts_min = - in.timestamp + conv_frames_to_time(sample_rate, in.frames); + data->timestamp + conv_frames_to_time(sample_rate, in.frames); in.timestamp += source->timing_adjust; From 0fb6304ad7f8ce712cf3d29c097146d6970007a1 Mon Sep 17 00:00:00 2001 From: floele Date: Wed, 23 Feb 2022 17:26:48 +0100 Subject: [PATCH 2/2] libobs: Fix audio dropouts in input_and_output() If the audio buffer is not currently sufficiently filled (at least AUDIO_OUTPUT_FRAMES), process_audio_source_tick() does not put any data into audio_output_buf. However, input_and_output() still executes do_audio_output(), taking exactly AUDIO_OUTPUT_FRAMES from the buffer, causing an audio dropout. Now return false in audio_callback() if any audio source is still pending and prevent further processing. Co-Authored-By: Norihiro Kamae --- libobs/media-io/audio-io.c | 34 +++++++++++++++++++++++++--------- libobs/obs-audio.c | 6 +++++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c index dc93bfeaf36653..e923c86394061a 100644 --- a/libobs/media-io/audio-io.c +++ b/libobs/media-io/audio-io.c @@ -153,7 +153,7 @@ static inline void clamp_audio_output(struct audio_output *audio, size_t bytes) } } -static void input_and_output(struct audio_output *audio, uint64_t audio_time, +static bool input_and_output(struct audio_output *audio, uint64_t audio_time, uint64_t prev_time) { size_t bytes = AUDIO_OUTPUT_FRAMES * audio->block_size; @@ -191,7 +191,7 @@ static void input_and_output(struct audio_output *audio, uint64_t audio_time, success = audio->input_cb(audio->input_param, prev_time, audio_time, &new_ts, active_mixes, data); if (!success) - return; + return false; /* clamps audio data to -1.0..1.0 */ clamp_audio_output(audio, bytes); @@ -199,6 +199,8 @@ static void input_and_output(struct audio_output *audio, uint64_t audio_time, /* output */ for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) do_audio_output(audio, i, new_ts, AUDIO_OUTPUT_FRAMES); + + return true; } static void *audio_thread(void *param) @@ -217,6 +219,7 @@ static void *audio_thread(void *param) uint32_t audio_wait_time = (uint32_t)(audio_frames_to_ns(rate, AUDIO_OUTPUT_FRAMES) / 1000000); + int wait_cycles = 0; os_set_thread_name("audio-io: audio thread"); @@ -227,18 +230,31 @@ static void *audio_thread(void *param) while (os_event_try(audio->stop_event) == EAGAIN) { uint64_t cur_time; - os_sleep_ms(audio_wait_time); + if (wait_cycles > 0) { + os_sleep_ms(audio_wait_time / 2); + wait_cycles--; + } else { + os_sleep_ms(audio_wait_time); + } profile_start(audio_thread_name); cur_time = os_gettime_ns(); while (audio_time <= cur_time) { - samples += AUDIO_OUTPUT_FRAMES; - audio_time = - start_time + audio_frames_to_ns(rate, samples); - - input_and_output(audio, audio_time, prev_time); - prev_time = audio_time; + uint64_t new_samples = samples + AUDIO_OUTPUT_FRAMES; + uint64_t new_audio_time = + start_time + + audio_frames_to_ns(rate, new_samples); + + if (input_and_output(audio, new_audio_time, + prev_time)) { + samples = new_samples; + audio_time = new_audio_time; + prev_time = audio_time; + } else { + os_sleep_ms(audio_wait_time / 2); + wait_cycles++; + } } profile_end(audio_thread_name); diff --git a/libobs/obs-audio.c b/libobs/obs-audio.c index e6b107b2b1286e..a787b51803e5a2 100644 --- a/libobs/obs-audio.c +++ b/libobs/obs-audio.c @@ -471,6 +471,7 @@ bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, struct ts_info ts = {start_ts_in, end_ts_in}; size_t audio_size; uint64_t min_ts; + bool audio_pending = false; da_resize(audio->render_order, 0); da_resize(audio->root_nodes, 0); @@ -516,6 +517,9 @@ bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, obs_source_audio_render(source, mixers, channels, sample_rate, audio_size); + if (source->audio_pending && source->audio_ts) + audio_pending = true; + /* 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 && @@ -611,5 +615,5 @@ bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, execute_audio_tasks(); UNUSED_PARAMETER(param); - return true; + return !audio_pending; }