Fixes to SDL audio renderer and autodetection support

This commit is contained in:
Cameron Gutman 2018-09-13 07:46:01 -07:00
parent 12496e4432
commit 64bc189010
7 changed files with 59 additions and 88 deletions

View File

@ -1,4 +1,4 @@
QT += core quick network quickcontrols2 svg QT += core quick network quickcontrols2 svg multimedia
CONFIG += c++11 CONFIG += c++11
unix:!macx { unix:!macx {

View File

@ -386,14 +386,10 @@ ScrollView {
textRole: "text" textRole: "text"
model: ListModel { model: ListModel {
id: audioListModel id: audioListModel
// Detection of audio channels only works on Windows ListElement {
// and even so, it's still unreliable because audio devices
// lie about how many channels they can support. Hide the
// option to autodetect until this is resolved.
/*ListElement {
text: "Autodetect" text: "Autodetect"
val: StreamingPreferences.AC_AUTO val: StreamingPreferences.AC_AUTO
}*/ }
ListElement { ListElement {
text: "Stereo" text: "Stereo"
val: StreamingPreferences.AC_FORCE_STEREO val: StreamingPreferences.AC_FORCE_STEREO

View File

@ -43,7 +43,7 @@ void StreamingPreferences::reload()
enableMdns = settings.value(SER_MDNS, true).toBool(); enableMdns = settings.value(SER_MDNS, true).toBool();
mouseAcceleration = settings.value(SER_MOUSEACCELERATION, false).toBool(); mouseAcceleration = settings.value(SER_MOUSEACCELERATION, false).toBool();
audioConfig = static_cast<AudioConfig>(settings.value(SER_AUDIOCFG, audioConfig = static_cast<AudioConfig>(settings.value(SER_AUDIOCFG,
static_cast<int>(AudioConfig::AC_FORCE_STEREO)).toInt()); static_cast<int>(AudioConfig::AC_AUTO)).toInt());
videoCodecConfig = static_cast<VideoCodecConfig>(settings.value(SER_VIDEOCFG, videoCodecConfig = static_cast<VideoCodecConfig>(settings.value(SER_VIDEOCFG,
static_cast<int>(VideoCodecConfig::VCC_AUTO)).toInt()); static_cast<int>(VideoCodecConfig::VCC_AUTO)).toInt());
videoDecoderSelection = static_cast<VideoDecoderSelection>(settings.value(SER_VIDEODEC, videoDecoderSelection = static_cast<VideoDecoderSelection>(settings.value(SER_VIDEODEC,

View File

@ -70,8 +70,7 @@ int Session::arInit(int /* audioConfiguration */,
return -2; return -2;
} }
s_ActiveSession->m_AudioRenderer->prepareForPlayback(opusConfig); if (!s_ActiveSession->m_AudioRenderer->prepareForPlayback(opusConfig)) {
if (s_ActiveSession->m_AudioRenderer == nullptr) {
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 -3;
@ -86,12 +85,21 @@ int Session::arInit(int /* audioConfiguration */,
void Session::arCleanup() void Session::arCleanup()
{ {
delete s_ActiveSession->m_AudioRenderer; // m_AudioRenderer is deleted in cleanupAudioRenderer()
opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder); opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder);
s_ActiveSession->m_OpusDecoder = nullptr; s_ActiveSession->m_OpusDecoder = nullptr;
} }
// This is called on the main thread
void Session::cleanupAudioRendererOnMainThread()
{
SDL_AtomicLock(&m_AudioRendererLock);
delete m_AudioRenderer;
m_AudioRenderer = nullptr;
SDL_AtomicUnlock(&m_AudioRendererLock);
}
void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength) void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
{ {
int samplesDecoded; int samplesDecoded;
@ -103,9 +111,13 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
SAMPLES_PER_FRAME, SAMPLES_PER_FRAME,
0); 0);
if (samplesDecoded > 0) { if (samplesDecoded > 0) {
SDL_AtomicLock(&s_ActiveSession->m_AudioRendererLock);
if (s_ActiveSession->m_AudioRenderer != nullptr) {
s_ActiveSession->m_AudioRenderer->submitAudio(s_ActiveSession->m_OpusDecodeBuffer, 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_AtomicUnlock(&s_ActiveSession->m_AudioRendererLock);
}
} }

View File

@ -3,6 +3,7 @@
#include <Limelight.h> #include <Limelight.h>
#include <SDL.h> #include <SDL.h>
#include <QAudioDeviceInfo>
#include <QtGlobal> #include <QtGlobal>
#define MIN_QUEUED_FRAMES 2 #define MIN_QUEUED_FRAMES 2
@ -10,69 +11,31 @@
#define STOP_THE_WORLD_LIMIT 20 #define STOP_THE_WORLD_LIMIT 20
#define DROP_RATIO_DENOM 32 #define DROP_RATIO_DENOM 32
// Detecting this with SDL is quite problematic, so we'll use Qt's
// multimedia framework to do so. It appears to be actually
// accurate on Linux and macOS, unlike using SDL and relying
// on a channel change in the format received.
int SdlAudioRenderer::detectAudioConfiguration() int SdlAudioRenderer::detectAudioConfiguration()
{ {
SDL_AudioSpec want, have; int preferredChannelCount = QAudioDeviceInfo::defaultOutputDevice().preferredFormat().channelCount();
SDL_AudioDeviceID dev;
int ret;
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s",
SDL_GetError());
return AUDIO_CONFIGURATION_STEREO;
}
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
ret = AUDIO_CONFIGURATION_STEREO;
goto Exit;
}
SDL_CloseAudioDevice(dev);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio device has %d channels", have.channels); "Audio output device prefers %d channel configuration",
preferredChannelCount);
if (have.channels > 2) { // We can better downmix 5.1 to quad than we can upmix stereo
// We don't support quadraphonic or 7.1 surround, but SDL if (preferredChannelCount > 2) {
// should be able to downmix or upmix better from 5.1 than return AUDIO_CONFIGURATION_51_SURROUND;
// from stereo, so use 5.1 in non-stereo cases.
ret = AUDIO_CONFIGURATION_51_SURROUND;
} }
else { else {
ret = AUDIO_CONFIGURATION_STEREO; return AUDIO_CONFIGURATION_STEREO;
} }
Exit:
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return ret;
} }
bool SdlAudioRenderer::testAudio(int audioConfiguration) bool SdlAudioRenderer::testAudio(int audioConfiguration)
{ {
SDL_AudioSpec want, have; SDL_AudioSpec want, have;
SDL_AudioDeviceID dev; SDL_AudioDeviceID dev;
bool ret;
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Audio test - SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s",
SDL_GetError());
return false;
}
SDL_zero(want); SDL_zero(want);
want.freq = 48000; want.freq = 48000;
@ -88,8 +51,7 @@ bool SdlAudioRenderer::testAudio(int audioConfiguration)
break; break;
default: default:
SDL_assert(false); SDL_assert(false);
ret = false; return false;
goto Exit;
} }
// Test audio device for functionality // Test audio device for functionality
@ -98,8 +60,7 @@ bool SdlAudioRenderer::testAudio(int audioConfiguration)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Audio test - Failed to open audio device: %s", "Audio test - Failed to open audio device: %s",
SDL_GetError()); SDL_GetError());
ret = false; return false;
goto Exit;
} }
SDL_CloseAudioDevice(dev); SDL_CloseAudioDevice(dev);
@ -107,12 +68,7 @@ bool SdlAudioRenderer::testAudio(int audioConfiguration)
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio test - Successful with %d channels", "Audio test - Successful with %d channels",
want.channels); want.channels);
return true;
ret = true;
Exit:
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return ret;
} }
SdlAudioRenderer::SdlAudioRenderer() SdlAudioRenderer::SdlAudioRenderer()
@ -123,20 +79,18 @@ SdlAudioRenderer::SdlAudioRenderer()
m_SampleIndex(0), m_SampleIndex(0),
m_BaselinePendingData(0) m_BaselinePendingData(0)
{ {
}
bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig)
{
SDL_AudioSpec want, have;
SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO)); SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO));
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s", "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s",
SDL_GetError()); SDL_GetError());
return -1; SDL_assert(SDL_WasInit(SDL_INIT_AUDIO));
} }
}
bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig)
{
SDL_AudioSpec want, have;
SDL_zero(want); SDL_zero(want);
want.freq = opusConfig->sampleRate; want.freq = opusConfig->sampleRate;
@ -155,7 +109,7 @@ bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
"Failed to open audio device: %s", "Failed to open audio device: %s",
SDL_GetError()); SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
return -1; return false;
} }
// SDL counts pending samples in the queued // SDL counts pending samples in the queued
@ -182,14 +136,16 @@ bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
// Start playback // Start playback
SDL_PauseAudioDevice(m_AudioDevice, 0); SDL_PauseAudioDevice(m_AudioDevice, 0);
return 0; return true;
} }
SdlAudioRenderer::~SdlAudioRenderer() SdlAudioRenderer::~SdlAudioRenderer()
{ {
if (m_AudioDevice != 0) {
// Stop playback // Stop playback
SDL_PauseAudioDevice(m_AudioDevice, 1); SDL_PauseAudioDevice(m_AudioDevice, 1);
SDL_CloseAudioDevice(m_AudioDevice); SDL_CloseAudioDevice(m_AudioDevice);
}
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO)); SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO));

View File

@ -279,7 +279,8 @@ Session::Session(NvComputer* computer, NvApp& app)
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_AudioRendererLock(0)
{ {
} }
@ -1110,6 +1111,9 @@ DispatchDeferredCleanup:
m_VideoDecoder = nullptr; m_VideoDecoder = nullptr;
SDL_AtomicUnlock(&m_DecoderLock); SDL_AtomicUnlock(&m_DecoderLock);
// Destroy the audio renderer which must also be done on the main thread
cleanupAudioRendererOnMainThread();
SDL_DestroyWindow(m_Window); SDL_DestroyWindow(m_Window);
if (iconSurface != nullptr) { if (iconSurface != nullptr) {
SDL_FreeSurface(iconSurface); SDL_FreeSurface(iconSurface);

View File

@ -54,6 +54,8 @@ private:
int detectAudioConfiguration(); int detectAudioConfiguration();
void cleanupAudioRendererOnMainThread();
bool testAudio(int audioConfiguration); bool testAudio(int audioConfiguration);
void getWindowDimensions(int& x, int& y, void getWindowDimensions(int& x, int& y,
@ -124,6 +126,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;
SDL_SpinLock m_AudioRendererLock;
static AUDIO_RENDERER_CALLBACKS k_AudioCallbacks; static AUDIO_RENDERER_CALLBACKS k_AudioCallbacks;
static CONNECTION_LISTENER_CALLBACKS k_ConnCallbacks; static CONNECTION_LISTENER_CALLBACKS k_ConnCallbacks;