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.
master
jp9000 2022-04-24 08:43:12 -07:00
parent 090613851e
commit f482111791
5 changed files with 84 additions and 11 deletions

View File

@ -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.

View File

@ -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!");
}

View File

@ -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;

View File

@ -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;

View File

@ -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);