From 7f8d4c88c19fd64b2a26da828d86f510f575687c Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 15 Jul 2018 20:17:08 -0700 Subject: [PATCH] Implement audio latency mitigations --- app/streaming/audio.cpp | 33 ++++++++++++++++++++++++++++++++- app/streaming/session.hpp | 2 ++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/streaming/audio.cpp b/app/streaming/audio.cpp index 578bccb0..00a431a6 100644 --- a/app/streaming/audio.cpp +++ b/app/streaming/audio.cpp @@ -5,11 +5,16 @@ #define MAX_CHANNELS 6 #define SAMPLES_PER_FRAME 240 +#define MIN_QUEUED_FRAMES 2 +#define MAX_QUEUED_FRAMES 4 +#define DROP_RATIO_DENOM 32 SDL_AudioDeviceID Session::s_AudioDevice; OpusMSDecoder* Session::s_OpusDecoder; short Session::s_OpusDecodeBuffer[MAX_CHANNELS * SAMPLES_PER_FRAME]; int Session::s_ChannelCount; +int Session::s_PendingDrops; +unsigned int Session::s_SampleIndex; int Session::sdlDetermineAudioConfiguration() { @@ -60,7 +65,12 @@ int Session::sdlAudioInit(int /* audioConfiguration */, want.freq = opusConfig->sampleRate; want.format = AUDIO_S16; want.channels = opusConfig->channelCount; - want.samples = 1024; + + // This is supposed to be a power of 2, but our + // frames contain a non-power of 2 number of samples, + // so the slop would require buffering another full frame. + // Specifying non-Po2 seems to work for our supported platforms. + want.samples = SAMPLES_PER_FRAME; s_AudioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); if (s_AudioDevice == 0) { @@ -86,6 +96,8 @@ int Session::sdlAudioInit(int /* audioConfiguration */, } s_ChannelCount = opusConfig->channelCount; + s_SampleIndex = 0; + s_PendingDrops = 0; return 0; } @@ -115,6 +127,25 @@ void Session::sdlAudioDecodeAndPlaySample(char* sampleData, int sampleLength) { int samplesDecoded; + s_SampleIndex++; + + Uint32 queuedAudio = SDL_GetQueuedAudioSize(s_AudioDevice); + Uint32 framesQueued = queuedAudio / (SAMPLES_PER_FRAME * s_ChannelCount * sizeof(short)); + + if (framesQueued - s_PendingDrops > MAX_QUEUED_FRAMES) { + // Pend enough drops to get us back to MIN_QUEUED_FRAMES + s_PendingDrops += (framesQueued - MIN_QUEUED_FRAMES); + } + + // Determine if this frame should be dropped + if (framesQueued <= MIN_QUEUED_FRAMES) { + s_PendingDrops = 0; + } + else if (s_PendingDrops != 0 && s_SampleIndex % DROP_RATIO_DENOM == 0) { + s_PendingDrops--; + return; + } + samplesDecoded = opus_multistream_decode(s_OpusDecoder, (unsigned char*)sampleData, sampleLength, diff --git a/app/streaming/session.hpp b/app/streaming/session.hpp index f1819a2f..baa50b62 100644 --- a/app/streaming/session.hpp +++ b/app/streaming/session.hpp @@ -122,6 +122,8 @@ private: static OpusMSDecoder* s_OpusDecoder; static short s_OpusDecodeBuffer[]; static int s_ChannelCount; + static int s_PendingDrops; + static unsigned int s_SampleIndex; static AUDIO_RENDERER_CALLBACKS k_AudioCallbacks; static CONNECTION_LISTENER_CALLBACKS k_ConnCallbacks;