diff --git a/app/streaming/audio/audio.cpp b/app/streaming/audio/audio.cpp index 7b650b60..110937ae 100644 --- a/app/streaming/audio/audio.cpp +++ b/app/streaming/audio/audio.cpp @@ -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); + } } } diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 4467d953..74ebba6a 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -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 diff --git a/app/streaming/session.h b/app/streaming/session.h index c42040b8..9fd9d407 100644 --- a/app/streaming/session.h +++ b/app/streaming/session.h @@ -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;