mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-04 00:36:36 +00:00
Fixes to SDL audio renderer and autodetection support
This commit is contained in:
parent
12496e4432
commit
64bc189010
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user