mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2025-01-08 09:18:43 +00:00
Allow audio to recover if no audio devices were present during stream start
This commit is contained in:
parent
93ed985043
commit
6d220a9062
3 changed files with 64 additions and 51 deletions
|
@ -64,6 +64,48 @@ IAudioRenderer* Session::createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATI
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Session::initializeAudioRenderer()
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
SDL_assert(m_OriginalAudioConfig.channelCount > 0);
|
||||||
|
SDL_assert(m_AudioRenderer == nullptr);
|
||||||
|
SDL_assert(m_OpusDecoder == nullptr);
|
||||||
|
|
||||||
|
m_AudioRenderer = createAudioRenderer(&m_OriginalAudioConfig);
|
||||||
|
|
||||||
|
// We may be unable to create an audio renderer right now
|
||||||
|
if (m_AudioRenderer == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow the chosen renderer to remap Opus channels as needed to ensure proper output
|
||||||
|
m_ActiveAudioConfig = m_OriginalAudioConfig;
|
||||||
|
m_AudioRenderer->remapChannels(&m_ActiveAudioConfig);
|
||||||
|
|
||||||
|
// Create the Opus decoder with the renderer's preferred channel mapping
|
||||||
|
m_OpusDecoder =
|
||||||
|
opus_multistream_decoder_create(m_ActiveAudioConfig.sampleRate,
|
||||||
|
m_ActiveAudioConfig.channelCount,
|
||||||
|
m_ActiveAudioConfig.streams,
|
||||||
|
m_ActiveAudioConfig.coupledStreams,
|
||||||
|
m_ActiveAudioConfig.mapping,
|
||||||
|
&error);
|
||||||
|
if (m_OpusDecoder == nullptr) {
|
||||||
|
delete m_AudioRenderer;
|
||||||
|
m_AudioRenderer = nullptr;
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Failed to create decoder: %d",
|
||||||
|
error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Audio stream has %d channels",
|
||||||
|
m_ActiveAudioConfig.channelCount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int Session::getAudioRendererCapabilities(int audioConfiguration)
|
int Session::getAudioRendererCapabilities(int audioConfiguration)
|
||||||
{
|
{
|
||||||
// Build a fake OPUS_MULTISTREAM_CONFIGURATION to give
|
// Build a fake OPUS_MULTISTREAM_CONFIGURATION to give
|
||||||
|
@ -108,39 +150,8 @@ int Session::arInit(int /* audioConfiguration */,
|
||||||
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
||||||
void* /* arContext */, int /* arFlags */)
|
void* /* arContext */, int /* arFlags */)
|
||||||
{
|
{
|
||||||
int error;
|
SDL_memcpy(&s_ActiveSession->m_OriginalAudioConfig, opusConfig, sizeof(*opusConfig));
|
||||||
|
s_ActiveSession->initializeAudioRenderer();
|
||||||
SDL_memcpy(&s_ActiveSession->m_AudioConfig, opusConfig, sizeof(*opusConfig));
|
|
||||||
|
|
||||||
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(&s_ActiveSession->m_AudioConfig);
|
|
||||||
if (s_ActiveSession->m_AudioRenderer == nullptr) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the chosen renderer to remap Opus channels as needed to ensure proper output
|
|
||||||
s_ActiveSession->m_AudioRenderer->remapChannels(&s_ActiveSession->m_AudioConfig);
|
|
||||||
|
|
||||||
// Create the Opus decoder with the renderer's preferred channel mapping
|
|
||||||
s_ActiveSession->m_OpusDecoder =
|
|
||||||
opus_multistream_decoder_create(s_ActiveSession->m_AudioConfig.sampleRate,
|
|
||||||
s_ActiveSession->m_AudioConfig.channelCount,
|
|
||||||
s_ActiveSession->m_AudioConfig.streams,
|
|
||||||
s_ActiveSession->m_AudioConfig.coupledStreams,
|
|
||||||
s_ActiveSession->m_AudioConfig.mapping,
|
|
||||||
&error);
|
|
||||||
if (s_ActiveSession->m_OpusDecoder == NULL) {
|
|
||||||
delete s_ActiveSession->m_AudioRenderer;
|
|
||||||
s_ActiveSession->m_AudioRenderer = nullptr;
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Failed to create decoder: %d",
|
|
||||||
error);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio stream has %d channels",
|
|
||||||
s_ActiveSession->m_AudioConfig.channelCount);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +205,7 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_ActiveSession->m_AudioRenderer != nullptr) {
|
if (s_ActiveSession->m_AudioRenderer != nullptr) {
|
||||||
int desiredSize = sizeof(short) * s_ActiveSession->m_AudioConfig.samplesPerFrame * s_ActiveSession->m_AudioConfig.channelCount;
|
int desiredSize = sizeof(short) * s_ActiveSession->m_ActiveAudioConfig.samplesPerFrame * s_ActiveSession->m_ActiveAudioConfig.channelCount;
|
||||||
void* buffer = s_ActiveSession->m_AudioRenderer->getAudioBuffer(&desiredSize);
|
void* buffer = s_ActiveSession->m_AudioRenderer->getAudioBuffer(&desiredSize);
|
||||||
if (buffer == nullptr) {
|
if (buffer == nullptr) {
|
||||||
return;
|
return;
|
||||||
|
@ -204,13 +215,13 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||||
(unsigned char*)sampleData,
|
(unsigned char*)sampleData,
|
||||||
sampleLength,
|
sampleLength,
|
||||||
(short*)buffer,
|
(short*)buffer,
|
||||||
desiredSize / sizeof(short) / s_ActiveSession->m_AudioConfig.channelCount,
|
desiredSize / sizeof(short) / s_ActiveSession->m_ActiveAudioConfig.channelCount,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
// Update desiredSize with the number of bytes actually populated by the decoding operation
|
// Update desiredSize with the number of bytes actually populated by the decoding operation
|
||||||
if (samplesDecoded > 0) {
|
if (samplesDecoded > 0) {
|
||||||
SDL_assert(desiredSize >= (int)(sizeof(short) * samplesDecoded * s_ActiveSession->m_AudioConfig.channelCount));
|
SDL_assert(desiredSize >= (int)(sizeof(short) * samplesDecoded * s_ActiveSession->m_ActiveAudioConfig.channelCount));
|
||||||
desiredSize = sizeof(short) * samplesDecoded * s_ActiveSession->m_AudioConfig.channelCount;
|
desiredSize = sizeof(short) * samplesDecoded * s_ActiveSession->m_ActiveAudioConfig.channelCount;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
desiredSize = 0;
|
desiredSize = 0;
|
||||||
|
@ -220,6 +231,9 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Reinitializing audio renderer after failure");
|
"Reinitializing audio renderer after failure");
|
||||||
|
|
||||||
|
opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder);
|
||||||
|
s_ActiveSession->m_OpusDecoder = nullptr;
|
||||||
|
|
||||||
delete s_ActiveSession->m_AudioRenderer;
|
delete s_ActiveSession->m_AudioRenderer;
|
||||||
s_ActiveSession->m_AudioRenderer = nullptr;
|
s_ActiveSession->m_AudioRenderer = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -234,14 +248,13 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||||
// to drop samples to account for the time we've spent blocking audio rendering
|
// to drop samples to account for the time we've spent blocking audio rendering
|
||||||
// so we return to real-time playback and don't accumulate latency.
|
// so we return to real-time playback and don't accumulate latency.
|
||||||
Uint32 audioReinitStartTime = SDL_GetTicks();
|
Uint32 audioReinitStartTime = SDL_GetTicks();
|
||||||
|
if (s_ActiveSession->initializeAudioRenderer()) {
|
||||||
|
Uint32 audioReinitStopTime = SDL_GetTicks();
|
||||||
|
|
||||||
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(&s_ActiveSession->m_AudioConfig);
|
s_ActiveSession->m_DropAudioEndTime = audioReinitStopTime + (audioReinitStopTime - audioReinitStartTime);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
Uint32 audioReinitStopTime = SDL_GetTicks();
|
"Audio reinitialization took %d ms - starting drop window",
|
||||||
|
audioReinitStopTime - audioReinitStartTime);
|
||||||
s_ActiveSession->m_DropAudioEndTime = audioReinitStopTime + (audioReinitStopTime - audioReinitStartTime);
|
}
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio reinitialization took %d ms - starting drop window",
|
|
||||||
audioReinitStopTime - audioReinitStartTime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -548,7 +548,6 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
|
||||||
m_Window(nullptr),
|
m_Window(nullptr),
|
||||||
m_VideoDecoder(nullptr),
|
m_VideoDecoder(nullptr),
|
||||||
m_DecoderLock(0),
|
m_DecoderLock(0),
|
||||||
m_AudioDisabled(false),
|
|
||||||
m_AudioMuted(false),
|
m_AudioMuted(false),
|
||||||
m_QtWindow(nullptr),
|
m_QtWindow(nullptr),
|
||||||
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
|
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
|
||||||
|
@ -993,8 +992,7 @@ bool Session::validateLaunch(SDL_Window* testWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If nothing worked, warn the user that audio will not work
|
// If nothing worked, warn the user that audio will not work
|
||||||
m_AudioDisabled = !audioTestPassed;
|
if (!audioTestPassed) {
|
||||||
if (m_AudioDisabled) {
|
|
||||||
emitLaunchWarning(tr("Failed to open audio device. Audio will be unavailable during this session."));
|
emitLaunchWarning(tr("Failed to open audio device. Audio will be unavailable during this session."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1467,8 +1465,7 @@ bool Session::startConnectionAsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = LiStartConnection(&hostInfo, &m_StreamConfig, &k_ConnCallbacks,
|
int err = LiStartConnection(&hostInfo, &m_StreamConfig, &k_ConnCallbacks,
|
||||||
&m_VideoCallbacks,
|
&m_VideoCallbacks, &m_AudioCallbacks,
|
||||||
m_AudioDisabled ? nullptr : &m_AudioCallbacks,
|
|
||||||
NULL, 0, NULL, 0);
|
NULL, 0, NULL, 0);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
// We already displayed an error dialog in the stage failure
|
// We already displayed an error dialog in the stage failure
|
||||||
|
|
|
@ -79,6 +79,8 @@ private:
|
||||||
|
|
||||||
IAudioRenderer* createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATION opusConfig);
|
IAudioRenderer* createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATION opusConfig);
|
||||||
|
|
||||||
|
bool initializeAudioRenderer();
|
||||||
|
|
||||||
bool testAudio(int audioConfiguration);
|
bool testAudio(int audioConfiguration);
|
||||||
|
|
||||||
int getAudioRendererCapabilities(int audioConfiguration);
|
int getAudioRendererCapabilities(int audioConfiguration);
|
||||||
|
@ -184,7 +186,8 @@ private:
|
||||||
|
|
||||||
OpusMSDecoder* m_OpusDecoder;
|
OpusMSDecoder* m_OpusDecoder;
|
||||||
IAudioRenderer* m_AudioRenderer;
|
IAudioRenderer* m_AudioRenderer;
|
||||||
OPUS_MULTISTREAM_CONFIGURATION m_AudioConfig;
|
OPUS_MULTISTREAM_CONFIGURATION m_ActiveAudioConfig;
|
||||||
|
OPUS_MULTISTREAM_CONFIGURATION m_OriginalAudioConfig;
|
||||||
int m_AudioSampleCount;
|
int m_AudioSampleCount;
|
||||||
Uint32 m_DropAudioEndTime;
|
Uint32 m_DropAudioEndTime;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue