Allow audio to recover if no audio devices were present during stream start

This commit is contained in:
Cameron Gutman 2024-05-04 21:13:00 -05:00
parent 93ed985043
commit 6d220a9062
3 changed files with 64 additions and 51 deletions

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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;