mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2025-01-07 08:48:45 +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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// Build a fake OPUS_MULTISTREAM_CONFIGURATION to give
|
||||
|
@ -108,39 +150,8 @@ int Session::arInit(int /* audioConfiguration */,
|
|||
const POPUS_MULTISTREAM_CONFIGURATION opusConfig,
|
||||
void* /* arContext */, int /* arFlags */)
|
||||
{
|
||||
int error;
|
||||
|
||||
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);
|
||||
|
||||
SDL_memcpy(&s_ActiveSession->m_OriginalAudioConfig, opusConfig, sizeof(*opusConfig));
|
||||
s_ActiveSession->initializeAudioRenderer();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -194,7 +205,7 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||
}
|
||||
|
||||
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);
|
||||
if (buffer == nullptr) {
|
||||
return;
|
||||
|
@ -204,13 +215,13 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||
(unsigned char*)sampleData,
|
||||
sampleLength,
|
||||
(short*)buffer,
|
||||
desiredSize / sizeof(short) / s_ActiveSession->m_AudioConfig.channelCount,
|
||||
desiredSize / sizeof(short) / s_ActiveSession->m_ActiveAudioConfig.channelCount,
|
||||
0);
|
||||
|
||||
// Update desiredSize with the number of bytes actually populated by the decoding operation
|
||||
if (samplesDecoded > 0) {
|
||||
SDL_assert(desiredSize >= (int)(sizeof(short) * samplesDecoded * s_ActiveSession->m_AudioConfig.channelCount));
|
||||
desiredSize = 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_ActiveAudioConfig.channelCount;
|
||||
}
|
||||
else {
|
||||
desiredSize = 0;
|
||||
|
@ -220,6 +231,9 @@ void Session::arDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Reinitializing audio renderer after failure");
|
||||
|
||||
opus_multistream_decoder_destroy(s_ActiveSession->m_OpusDecoder);
|
||||
s_ActiveSession->m_OpusDecoder = nullptr;
|
||||
|
||||
delete s_ActiveSession->m_AudioRenderer;
|
||||
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
|
||||
// so we return to real-time playback and don't accumulate latency.
|
||||
Uint32 audioReinitStartTime = SDL_GetTicks();
|
||||
if (s_ActiveSession->initializeAudioRenderer()) {
|
||||
Uint32 audioReinitStopTime = SDL_GetTicks();
|
||||
|
||||
s_ActiveSession->m_AudioRenderer = s_ActiveSession->createAudioRenderer(&s_ActiveSession->m_AudioConfig);
|
||||
|
||||
Uint32 audioReinitStopTime = SDL_GetTicks();
|
||||
|
||||
s_ActiveSession->m_DropAudioEndTime = audioReinitStopTime + (audioReinitStopTime - audioReinitStartTime);
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"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_VideoDecoder(nullptr),
|
||||
m_DecoderLock(0),
|
||||
m_AudioDisabled(false),
|
||||
m_AudioMuted(false),
|
||||
m_QtWindow(nullptr),
|
||||
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
|
||||
m_AudioDisabled = !audioTestPassed;
|
||||
if (m_AudioDisabled) {
|
||||
if (!audioTestPassed) {
|
||||
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,
|
||||
&m_VideoCallbacks,
|
||||
m_AudioDisabled ? nullptr : &m_AudioCallbacks,
|
||||
&m_VideoCallbacks, &m_AudioCallbacks,
|
||||
NULL, 0, NULL, 0);
|
||||
if (err != 0) {
|
||||
// We already displayed an error dialog in the stage failure
|
||||
|
|
|
@ -79,6 +79,8 @@ private:
|
|||
|
||||
IAudioRenderer* createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATION opusConfig);
|
||||
|
||||
bool initializeAudioRenderer();
|
||||
|
||||
bool testAudio(int audioConfiguration);
|
||||
|
||||
int getAudioRendererCapabilities(int audioConfiguration);
|
||||
|
@ -184,7 +186,8 @@ private:
|
|||
|
||||
OpusMSDecoder* m_OpusDecoder;
|
||||
IAudioRenderer* m_AudioRenderer;
|
||||
OPUS_MULTISTREAM_CONFIGURATION m_AudioConfig;
|
||||
OPUS_MULTISTREAM_CONFIGURATION m_ActiveAudioConfig;
|
||||
OPUS_MULTISTREAM_CONFIGURATION m_OriginalAudioConfig;
|
||||
int m_AudioSampleCount;
|
||||
Uint32 m_DropAudioEndTime;
|
||||
|
||||
|
|
Loading…
Reference in a new issue