Remove automatic audio configuration permanently due to brokenness and allow renderers to request reinitialization on demand

This commit is contained in:
Cameron Gutman
2018-10-02 01:21:42 -07:00
parent a614a693e5
commit 3dc049ee5f
10 changed files with 52 additions and 121 deletions
-1
View File
@@ -194,7 +194,6 @@ StreamCommandLineParser::StreamCommandLineParser()
{"borderless", StreamingPreferences::WM_FULLSCREEN_DESKTOP}, {"borderless", StreamingPreferences::WM_FULLSCREEN_DESKTOP},
}; };
m_AudioConfigMap = { m_AudioConfigMap = {
{"auto", StreamingPreferences::AC_AUTO},
{"stereo", StreamingPreferences::AC_FORCE_STEREO}, {"stereo", StreamingPreferences::AC_FORCE_STEREO},
{"surround", StreamingPreferences::AC_FORCE_SURROUND}, {"surround", StreamingPreferences::AC_FORCE_SURROUND},
}; };
-4
View File
@@ -406,10 +406,6 @@ ScrollView {
textRole: "text" textRole: "text"
model: ListModel { model: ListModel {
id: audioListModel id: audioListModel
ListElement {
text: "Autodetect"
val: StreamingPreferences.AC_AUTO
}
ListElement { ListElement {
text: "Stereo" text: "Stereo"
val: StreamingPreferences.AC_FORCE_STEREO val: StreamingPreferences.AC_FORCE_STEREO
+28 -26
View File
@@ -52,23 +52,6 @@ bool Session::testAudio(int audioConfiguration)
return ret; return ret;
} }
int Session::detectAudioConfiguration()
{
IAudioRenderer* audioRenderer;
audioRenderer = createAudioRenderer();
if (audioRenderer == nullptr) {
// Hope for the best
return AUDIO_CONFIGURATION_STEREO;
}
int audioConfig = audioRenderer->detectAudioConfiguration();
delete audioRenderer;
return audioConfig;
}
int Session::arInit(int /* audioConfiguration */, int Session::arInit(int /* audioConfiguration */,
const POPUS_MULTISTREAM_CONFIGURATION opusConfig, const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
void* /* arContext */, int /* arFlags */) void* /* arContext */, int /* arFlags */)
@@ -92,15 +75,10 @@ int Session::arInit(int /* audioConfiguration */,
} }
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(); s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer();
if (s_ActiveSession->m_AudioRenderer == nullptr) {
opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder);
return -2;
}
if (!s_ActiveSession->m_AudioRenderer->prepareForPlayback(opusConfig)) { if (!s_ActiveSession->m_AudioRenderer->prepareForPlayback(opusConfig)) {
delete s_ActiveSession->m_AudioRenderer; delete s_ActiveSession->m_AudioRenderer;
opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder); opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder);
return -3; return -2;
} }
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
@@ -123,6 +101,9 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
{ {
int samplesDecoded; int samplesDecoded;
s_ActiveSession->m_AudioSampleCount++;
if (s_ActiveSession->m_AudioRenderer != nullptr) {
samplesDecoded = opus_multistream_decode(s_ActiveSession->m_OpusDecoder, samplesDecoded = opus_multistream_decode(s_ActiveSession->m_OpusDecoder,
(unsigned char*)sampleData, (unsigned char*)sampleData,
sampleLength, sampleLength,
@@ -130,9 +111,30 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
SAMPLES_PER_FRAME, SAMPLES_PER_FRAME,
0); 0);
if (samplesDecoded > 0) { if (samplesDecoded > 0) {
s_ActiveSession->m_AudioRenderer->submitAudio(s_ActiveSession->m_OpusDecodeBuffer, if (!s_ActiveSession->m_AudioRenderer->submitAudio(s_ActiveSession->m_OpusDecodeBuffer,
static_cast<int>(sizeof(short) * static_cast<int>(
sizeof(short) *
samplesDecoded * samplesDecoded *
s_ActiveSession->m_AudioConfig.channelCount)); s_ActiveSession->m_AudioConfig.channelCount))) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Reinitializing audio renderer after failure");
delete s_ActiveSession->m_AudioRenderer;
s_ActiveSession->m_AudioRenderer = nullptr;
}
}
}
// Only try to recreate the audio renderer every 200 samples (1 second)
// to avoid thrashing if the audio device is unavailable. It is
// safe to reinitialize here because we can't be torn down while
// the audio decoder/playback thread is still alive.
if (s_ActiveSession->m_AudioRenderer == nullptr && (s_ActiveSession->m_AudioSampleCount % 200) == 0) {
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer();
if (!s_ActiveSession->m_AudioRenderer->prepareForPlayback(&s_ActiveSession->m_AudioConfig)) {
delete s_ActiveSession->m_AudioRenderer;
s_ActiveSession->m_AudioRenderer = nullptr;
}
} }
} }
@@ -74,14 +74,14 @@ bool PortAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
return true; return true;
} }
void PortAudioRenderer::submitAudio(short* audioBuffer, int audioSize) bool PortAudioRenderer::submitAudio(short* audioBuffer, int audioSize)
{ {
SDL_assert(audioSize == SAMPLES_PER_FRAME * m_ChannelCount * 2); SDL_assert(audioSize == SAMPLES_PER_FRAME * m_ChannelCount * 2);
// Check if there is space for this sample in the buffer. Again, this can race // Check if there is space for this sample in the buffer. Again, this can race
// but in the worst case, we'll not see the sample callback having consumed a sample. // but in the worst case, we'll not see the sample callback having consumed a sample.
if (((m_WriteIndex + 1) % CIRCULAR_BUFFER_SIZE) == m_ReadIndex) { if (((m_WriteIndex + 1) % CIRCULAR_BUFFER_SIZE) == m_ReadIndex) {
return; return true;
} }
SDL_memcpy(&m_AudioBuffer[m_WriteIndex * CIRCULAR_BUFFER_STRIDE], audioBuffer, audioSize); SDL_memcpy(&m_AudioBuffer[m_WriteIndex * CIRCULAR_BUFFER_STRIDE], audioBuffer, audioSize);
@@ -102,33 +102,13 @@ void PortAudioRenderer::submitAudio(short* audioBuffer, int audioSize)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Pa_StartStream() failed: %s", "Pa_StartStream() failed: %s",
Pa_GetErrorText(error)); Pa_GetErrorText(error));
return; return false;
} }
m_Started = true; m_Started = true;
} }
}
int PortAudioRenderer::detectAudioConfiguration() const return true;
{
const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice());
if (deviceInfo == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Pa_GetDeviceInfo() failed");
return false;
}
// PulseAudio reports max output channels that don't
// correspond to any output devices (32 channels), so
// only use 5.1 surround sound if the output channel count
// is reasonable. Additionally, PortAudio doesn't do remixing
// for quadraphonic, so only use 5.1 if we have 6 or more channels.
if (deviceInfo->maxOutputChannels == 6 || deviceInfo->maxOutputChannels == 8) {
return AUDIO_CONFIGURATION_51_SURROUND;
}
else {
return AUDIO_CONFIGURATION_STEREO;
}
} }
int PortAudioRenderer::paStreamCallback(const void*, void* output, unsigned long frameCount, const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void* userData) int PortAudioRenderer::paStreamCallback(const void*, void* output, unsigned long frameCount, const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void* userData)
@@ -18,9 +18,7 @@ public:
virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig); virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
virtual void submitAudio(short* audioBuffer, int audioSize); virtual bool submitAudio(short* audioBuffer, int audioSize);
virtual int detectAudioConfiguration() const;
static int paStreamCallback(const void *input, static int paStreamCallback(const void *input,
void *output, void *output,
+2 -3
View File
@@ -12,7 +12,6 @@ public:
virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig) = 0; virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig) = 0;
virtual void submitAudio(short* audioBuffer, int audioSize) = 0; // Return false if an unrecoverable error has occurred and the renderer must be reinitialized
virtual bool submitAudio(short* audioBuffer, int audioSize) = 0;
virtual int detectAudioConfiguration() const = 0;
}; };
+1 -3
View File
@@ -17,9 +17,7 @@ public:
virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig); virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
virtual void submitAudio(short* audioBuffer, int audioSize); virtual bool submitAudio(short* audioBuffer, int audioSize);
virtual int detectAudioConfiguration() const;
private: private:
SDL_AudioDeviceID m_AudioDevice; SDL_AudioDeviceID m_AudioDevice;
+5 -44
View File
@@ -10,47 +10,6 @@
#define STOP_THE_WORLD_LIMIT 20 #define STOP_THE_WORLD_LIMIT 20
#define DROP_RATIO_DENOM 32 #define DROP_RATIO_DENOM 32
// This isn't accurate on macOS and Linux (PulseAudio),
// since they both report supporting a large number of
// channels, regardless of the actual output device.
int SdlAudioRenderer::detectAudioConfiguration() const
{
SDL_AudioSpec want, have;
SDL_AudioDeviceID dev;
SDL_zero(want);
want.freq = 48000;
want.format = AUDIO_S16;
want.channels = 6;
want.samples = 1024;
// Try to open for 5.1 surround sound, but allow SDL to tell us that's
// not available.
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
if (dev == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to open audio device");
// We'll probably have issues during audio stream init, but we'll
// try anyway
return AUDIO_CONFIGURATION_STEREO;
}
SDL_CloseAudioDevice(dev);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio device has %d channels", have.channels);
if (have.channels > 2) {
// We don't support quadraphonic or 7.1 surround, but SDL
// should be able to downmix or upmix better from 5.1 than
// from stereo, so use 5.1 in non-stereo cases.
return AUDIO_CONFIGURATION_51_SURROUND;
}
else {
return AUDIO_CONFIGURATION_STEREO;
}
}
SdlAudioRenderer::SdlAudioRenderer() SdlAudioRenderer::SdlAudioRenderer()
: m_AudioDevice(0), : m_AudioDevice(0),
m_ChannelCount(0), m_ChannelCount(0),
@@ -141,7 +100,7 @@ SdlAudioRenderer::~SdlAudioRenderer()
SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO)); SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO));
} }
void SdlAudioRenderer::submitAudio(short* audioBuffer, int audioSize) bool SdlAudioRenderer::submitAudio(short* audioBuffer, int audioSize)
{ {
m_SampleIndex++; m_SampleIndex++;
@@ -181,13 +140,13 @@ void SdlAudioRenderer::submitAudio(short* audioBuffer, int audioSize)
// Hard drops happen all at once to forcefully // Hard drops happen all at once to forcefully
// resync with the source. // resync with the source.
m_PendingHardDrops--; m_PendingHardDrops--;
return; return true;
} }
else if (m_PendingDrops != 0 && m_SampleIndex % DROP_RATIO_DENOM == 0) { else if (m_PendingDrops != 0 && m_SampleIndex % DROP_RATIO_DENOM == 0) {
// Normal drops are interspersed with the audio data // Normal drops are interspersed with the audio data
// to hide the glitches. // to hide the glitches.
m_PendingDrops--; m_PendingDrops--;
return; return true;
} }
if (SDL_QueueAudio(m_AudioDevice, audioBuffer, audioSize) < 0) { if (SDL_QueueAudio(m_AudioDevice, audioBuffer, audioSize) < 0) {
@@ -195,4 +154,6 @@ void SdlAudioRenderer::submitAudio(short* audioBuffer, int audioSize)
"Failed to queue audio sample: %s", "Failed to queue audio sample: %s",
SDL_GetError()); SDL_GetError());
} }
return true;
} }
+2 -5
View File
@@ -288,7 +288,8 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
m_DisplayOriginY(0), m_DisplayOriginY(0),
m_PendingWindowedTransition(false), m_PendingWindowedTransition(false),
m_OpusDecoder(nullptr), m_OpusDecoder(nullptr),
m_AudioRenderer(nullptr) m_AudioRenderer(nullptr),
m_AudioSampleCount(0)
{ {
} }
@@ -327,10 +328,6 @@ void Session::initialize()
switch (m_Preferences->audioConfig) switch (m_Preferences->audioConfig)
{ {
case StreamingPreferences::AC_AUTO:
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Autodetecting audio configuration");
m_StreamConfig.audioConfiguration = detectAudioConfiguration();
break;
case StreamingPreferences::AC_FORCE_STEREO: case StreamingPreferences::AC_FORCE_STEREO:
m_StreamConfig.audioConfiguration = AUDIO_CONFIGURATION_STEREO; m_StreamConfig.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
break; break;
+1
View File
@@ -124,6 +124,7 @@ private:
short m_OpusDecodeBuffer[MAX_CHANNELS * SAMPLES_PER_FRAME]; short m_OpusDecodeBuffer[MAX_CHANNELS * SAMPLES_PER_FRAME];
IAudioRenderer* m_AudioRenderer; IAudioRenderer* m_AudioRenderer;
OPUS_MULTISTREAM_CONFIGURATION m_AudioConfig; OPUS_MULTISTREAM_CONFIGURATION m_AudioConfig;
int m_AudioSampleCount;
static AUDIO_RENDERER_CALLBACKS k_AudioCallbacks; static AUDIO_RENDERER_CALLBACKS k_AudioCallbacks;
static CONNECTION_LISTENER_CALLBACKS k_ConnCallbacks; static CONNECTION_LISTENER_CALLBACKS k_ConnCallbacks;