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
unix:!macx {

View file

@ -386,14 +386,10 @@ ScrollView {
textRole: "text"
model: ListModel {
id: audioListModel
// Detection of audio channels only works on Windows
// 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 {
ListElement {
text: "Autodetect"
val: StreamingPreferences.AC_AUTO
}*/
}
ListElement {
text: "Stereo"
val: StreamingPreferences.AC_FORCE_STEREO

View file

@ -43,7 +43,7 @@ void StreamingPreferences::reload()
enableMdns = settings.value(SER_MDNS, true).toBool();
mouseAcceleration = settings.value(SER_MOUSEACCELERATION, false).toBool();
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,
static_cast<int>(VideoCodecConfig::VCC_AUTO)).toInt());
videoDecoderSelection = static_cast<VideoDecoderSelection>(settings.value(SER_VIDEODEC,

View file

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

View file

@ -3,6 +3,7 @@
#include <Limelight.h>
#include <SDL.h>
#include <QAudioDeviceInfo>
#include <QtGlobal>
#define MIN_QUEUED_FRAMES 2
@ -10,69 +11,31 @@
#define STOP_THE_WORLD_LIMIT 20
#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()
{
SDL_AudioSpec want, have;
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);
int preferredChannelCount = QAudioDeviceInfo::defaultOutputDevice().preferredFormat().channelCount();
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 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.
ret = AUDIO_CONFIGURATION_51_SURROUND;
// We can better downmix 5.1 to quad than we can upmix stereo
if (preferredChannelCount > 2) {
return AUDIO_CONFIGURATION_51_SURROUND;
}
else {
ret = AUDIO_CONFIGURATION_STEREO;
return AUDIO_CONFIGURATION_STEREO;
}
Exit:
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return ret;
}
bool SdlAudioRenderer::testAudio(int audioConfiguration)
{
SDL_AudioSpec want, have;
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);
want.freq = 48000;
@ -88,8 +51,7 @@ bool SdlAudioRenderer::testAudio(int audioConfiguration)
break;
default:
SDL_assert(false);
ret = false;
goto Exit;
return false;
}
// Test audio device for functionality
@ -98,8 +60,7 @@ bool SdlAudioRenderer::testAudio(int audioConfiguration)
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Audio test - Failed to open audio device: %s",
SDL_GetError());
ret = false;
goto Exit;
return false;
}
SDL_CloseAudioDevice(dev);
@ -107,12 +68,7 @@ bool SdlAudioRenderer::testAudio(int audioConfiguration)
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Audio test - Successful with %d channels",
want.channels);
ret = true;
Exit:
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return ret;
return true;
}
SdlAudioRenderer::SdlAudioRenderer()
@ -123,20 +79,18 @@ SdlAudioRenderer::SdlAudioRenderer()
m_SampleIndex(0),
m_BaselinePendingData(0)
{
}
bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig)
{
SDL_AudioSpec want, have;
SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO));
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s",
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);
want.freq = opusConfig->sampleRate;
@ -155,7 +109,7 @@ bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
"Failed to open audio device: %s",
SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return -1;
return false;
}
// SDL counts pending samples in the queued
@ -182,14 +136,16 @@ bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
// Start playback
SDL_PauseAudioDevice(m_AudioDevice, 0);
return 0;
return true;
}
SdlAudioRenderer::~SdlAudioRenderer()
{
// Stop playback
SDL_PauseAudioDevice(m_AudioDevice, 1);
SDL_CloseAudioDevice(m_AudioDevice);
if (m_AudioDevice != 0) {
// Stop playback
SDL_PauseAudioDevice(m_AudioDevice, 1);
SDL_CloseAudioDevice(m_AudioDevice);
}
SDL_QuitSubSystem(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_PendingWindowedTransition(false),
m_OpusDecoder(nullptr),
m_AudioRenderer(nullptr)
m_AudioRenderer(nullptr),
m_AudioRendererLock(0)
{
}
@ -1110,6 +1111,9 @@ DispatchDeferredCleanup:
m_VideoDecoder = nullptr;
SDL_AtomicUnlock(&m_DecoderLock);
// Destroy the audio renderer which must also be done on the main thread
cleanupAudioRendererOnMainThread();
SDL_DestroyWindow(m_Window);
if (iconSurface != nullptr) {
SDL_FreeSurface(iconSurface);

View file

@ -54,6 +54,8 @@ private:
int detectAudioConfiguration();
void cleanupAudioRendererOnMainThread();
bool testAudio(int audioConfiguration);
void getWindowDimensions(int& x, int& y,
@ -124,6 +126,7 @@ private:
short m_OpusDecodeBuffer[MAX_CHANNELS * SAMPLES_PER_FRAME];
IAudioRenderer* m_AudioRenderer;
OPUS_MULTISTREAM_CONFIGURATION m_AudioConfig;
SDL_SpinLock m_AudioRendererLock;
static AUDIO_RENDERER_CALLBACKS k_AudioCallbacks;
static CONNECTION_LISTENER_CALLBACKS k_ConnCallbacks;