From f482111791d5444e900a0918223125d0af3e02fe Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 24 Apr 2022 08:43:12 -0700 Subject: [PATCH] libobs: Add ability to configure audio buffering latency Allows a frontend the ability to set the maximum audio buffering latency, and specify whether that audio buffering is either fixed (to the maximum audio buffering latency), or dynamically increasing from 0. This will be useful if the user wishes to output audio to devices or through a virtual audio device at a guaranteed minimal latency. --- docs/sphinx/reference-core.rst | 30 +++++++++++++++++++++++++ libobs/obs-audio.c | 15 +++++++------ libobs/obs-internal.h | 1 + libobs/obs.c | 40 ++++++++++++++++++++++++++++++---- libobs/obs.h | 9 ++++++++ 5 files changed, 84 insertions(+), 11 deletions(-) diff --git a/docs/sphinx/reference-core.rst b/docs/sphinx/reference-core.rst index 95444ef45..4336a01ad 100644 --- a/docs/sphinx/reference-core.rst +++ b/docs/sphinx/reference-core.rst @@ -144,6 +144,36 @@ Initialization, Shutdown, and Information --------------------- +.. function:: bool obs_reset_audio2(const struct obs_audio_info2 *oai) + + Sets base audio output format/channels/samples/etc. Also allows the + ability to set the maximum audio latency of OBS, and set whether the + audio buffering is fixed or dynamically increasing. + + When using fixed audio buffering, OBS will automatically buffer to + the maximum audio latency on startup. + + Maximum audio latency will clamp to the closest multiple of the audio + output frames (which is typically 1024 audio frames). + + Note: Cannot reset base audio if an output is currently active. + + :return: *true* if successful, *false* otherwise + + Relevant data types used with this function: + +.. code:: cpp + + struct obs_audio_info2 { + uint32_t samples_per_sec; + enum speaker_layout speakers; + + uint32_t max_buffering_ms; + bool fixed_buffering; + }; + +--------------------- + .. function:: bool obs_get_video_info(struct obs_video_info *ovi) Gets the current video settings. diff --git a/libobs/obs-audio.c b/libobs/obs-audio.c index 391b1497c..ba5a1a308 100644 --- a/libobs/obs-audio.c +++ b/libobs/obs-audio.c @@ -26,7 +26,6 @@ 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) { @@ -241,7 +240,8 @@ static inline void discard_audio(struct obs_core_audio *audio, /* ignore_audio should have already run and marked this source * pending, unless we *just* added buffering */ - assert(audio->total_buffering_ticks < MAX_BUFFERING_TICKS || + assert(audio->total_buffering_ticks < + audio->max_buffering_ticks || source->audio_pending || !source->audio_ts || audio->buffering_wait_ticks); #endif @@ -294,7 +294,7 @@ static inline void discard_audio(struct obs_core_audio *audio, static inline bool audio_buffering_maxed(struct obs_core_audio *audio) { - return audio->total_buffering_ticks == MAX_BUFFERING_TICKS; + return audio->total_buffering_ticks == audio->max_buffering_ticks; } static void set_fixed_audio_buffering(struct obs_core_audio *audio, @@ -311,7 +311,7 @@ static void set_fixed_audio_buffering(struct obs_core_audio *audio, if (!audio->buffering_wait_ticks) audio->buffered_ts = ts->start; - ticks = MAX_BUFFERING_TICKS - audio->total_buffering_ticks; + ticks = audio->max_buffering_ticks - audio->total_buffering_ticks; audio->total_buffering_ticks += ticks; ms = ticks * AUDIO_OUTPUT_FRAMES * 1000 / sample_rate; @@ -374,9 +374,10 @@ static void add_audio_buffering(struct obs_core_audio *audio, audio->total_buffering_ticks += ticks; - if (audio->total_buffering_ticks >= MAX_BUFFERING_TICKS) { - ticks -= audio->total_buffering_ticks - MAX_BUFFERING_TICKS; - audio->total_buffering_ticks = MAX_BUFFERING_TICKS; + if (audio->total_buffering_ticks >= audio->max_buffering_ticks) { + ticks -= audio->total_buffering_ticks - + audio->max_buffering_ticks; + audio->total_buffering_ticks = audio->max_buffering_ticks; blog(LOG_WARNING, "Max audio buffering reached!"); } diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 271bbe599..ef019fbfa 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -344,6 +344,7 @@ struct obs_core_audio { struct circlebuf buffered_timestamps; uint64_t buffering_wait_ticks; int total_buffering_ticks; + int max_buffering_ticks; bool fixed_buffer; float user_volume; diff --git a/libobs/obs.c b/libobs/obs.c index 913277fe0..27689fbec 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1395,18 +1395,37 @@ int obs_reset_video(struct obs_video_info *ovi) return obs_init_video(ovi); } -bool obs_reset_audio(const struct obs_audio_info *oai) +#ifndef SEC_TO_MSEC +#define SEC_TO_MSEC 1000 +#endif + +bool obs_reset_audio2(const struct obs_audio_info2 *oai) { + struct obs_core_audio *audio = &obs->audio; struct audio_output_info ai; /* don't allow changing of audio settings if active. */ - if (obs->audio.audio && audio_output_active(obs->audio.audio)) + if (!obs || (audio->audio && audio_output_active(audio->audio))) return false; obs_free_audio(); if (!oai) return true; + if (oai->max_buffering_ms) { + uint32_t max_frames = oai->max_buffering_ms * + oai->samples_per_sec / SEC_TO_MSEC; + max_frames += (AUDIO_OUTPUT_FRAMES - 1); + audio->max_buffering_ticks = max_frames / AUDIO_OUTPUT_FRAMES; + } else { + audio->max_buffering_ticks = 45; + } + audio->fixed_buffer = oai->fixed_buffering; + + int max_buffering_ms = audio->max_buffering_ticks * + AUDIO_OUTPUT_FRAMES * SEC_TO_MSEC / + (int)oai->samples_per_sec; + ai.name = "Audio"; ai.samples_per_sec = oai->samples_per_sec; ai.format = AUDIO_FORMAT_FLOAT_PLANAR; @@ -1417,12 +1436,25 @@ bool obs_reset_audio(const struct obs_audio_info *oai) blog(LOG_INFO, "audio settings reset:\n" "\tsamples per sec: %d\n" - "\tspeakers: %d", - (int)ai.samples_per_sec, (int)ai.speakers); + "\tspeakers: %d\n" + "\tmax buffering: %d milliseconds\n" + "\tbuffering type: %s", + (int)ai.samples_per_sec, (int)ai.speakers, max_buffering_ms, + oai->fixed_buffering ? "fixed" : "dynamically increasing"); return obs_init_audio(&ai); } +bool obs_reset_audio(const struct obs_audio_info *oai) +{ + struct obs_audio_info2 oai2 = { + .samples_per_sec = oai->samples_per_sec, + .speakers = oai->speakers, + }; + + return obs_reset_audio2(&oai2); +} + bool obs_get_video_info(struct obs_video_info *ovi) { struct obs_core_video *video = &obs->video; diff --git a/libobs/obs.h b/libobs/obs.h index 312c853bc..93b1e2450 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -206,6 +206,14 @@ struct obs_audio_info { enum speaker_layout speakers; }; +struct obs_audio_info2 { + uint32_t samples_per_sec; + enum speaker_layout speakers; + + uint32_t max_buffering_ms; + bool fixed_buffering; +}; + /** * Sent to source filters via the filter_audio callback to allow filtering of * audio data @@ -413,6 +421,7 @@ EXPORT int obs_reset_video(struct obs_video_info *ovi); * @note Cannot reset base audio if an output is currently active. */ EXPORT bool obs_reset_audio(const struct obs_audio_info *oai); +EXPORT bool obs_reset_audio2(const struct obs_audio_info2 *oai); /** Gets the current video settings, returns false if no video */ EXPORT bool obs_get_video_info(struct obs_video_info *ovi);