Ansariel 2025-10-31 19:25:58 +01:00
commit 574dc5b65a
10 changed files with 513 additions and 1 deletions

View File

@ -39,6 +39,10 @@
#include "llframetimer.h" #include "llframetimer.h"
#include "llassettype.h" #include "llassettype.h"
#include "llextendedstatus.h" #include "llextendedstatus.h"
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Need to include for audio device mutex shared with other audio/voice systems.
#include "inlinemutexs.h"
// </FS:minerjr> [FIRE-36022]
#include "lllistener.h" #include "lllistener.h"

View File

@ -44,6 +44,9 @@
#include "sound_ids.h" #include "sound_ids.h"
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
using namespace std::chrono_literals; // Needed for shared timed mutex to use time
// </FS:minerjr> [FIRE-36022]
constexpr U32 EXTRA_SOUND_CHANNELS = 10; constexpr U32 EXTRA_SOUND_CHANNELS = 10;
FMOD_RESULT F_CALL windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels); FMOD_RESULT F_CALL windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels);
@ -113,8 +116,14 @@ static void set_device(FMOD::System* system, const LLUUID& device_uuid)
} }
} }
FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata) // <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// According to FMOD, not having this method static is very bad.
//FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata)
static FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata)
{ {
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
{
// </FS:minerjr> [FIRE-36022]
FMOD::System* sys = (FMOD::System*)system; FMOD::System* sys = (FMOD::System*)system;
LLAudioEngine_FMODSTUDIO* audio_engine = (LLAudioEngine_FMODSTUDIO*)userdata; LLAudioEngine_FMODSTUDIO* audio_engine = (LLAudioEngine_FMODSTUDIO*)userdata;
@ -124,6 +133,17 @@ FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE
LL_DEBUGS("FMOD") << "FMOD system callback FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED" << LL_ENDL; LL_DEBUGS("FMOD") << "FMOD system callback FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED" << LL_ENDL;
if (sys && audio_engine) if (sys && audio_engine)
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
return FMOD_OK; // Could be a FMOD_ERR_ALREADY_LOCKED;
}
// </FS:minerjr> [FIRE-36022]
set_device(sys, audio_engine->getSelectedDeviceUUID()); set_device(sys, audio_engine->getSelectedDeviceUUID());
audio_engine->OnOutputDeviceListChanged(audio_engine->getDevices()); audio_engine->OnOutputDeviceListChanged(audio_engine->getDevices());
} }
@ -132,6 +152,34 @@ FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE
break; break;
} }
return FMOD_OK; return FMOD_OK;
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
}
// There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
return FMOD_ERR_ALREADY_LOCKED;
}
else if (e.code() == std::errc::operation_not_permitted)
{
LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
return FMOD_ERR_ALREADY_LOCKED;
}
else
{
LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
}
return FMOD_ERR_ALREADY_LOCKED;
}
catch (const std::exception& e)
{
LL_WARNS() << "Exception FMOD: " << " " << e.what() << LL_ENDL;
return FMOD_ERR_ALREADY_LOCKED;
}
// </FS:minerjr> [FIRE-36022]
} }
LLAudioEngine_FMODSTUDIO::LLAudioEngine_FMODSTUDIO(bool enable_profiler, U32 resample_method) LLAudioEngine_FMODSTUDIO::LLAudioEngine_FMODSTUDIO(bool enable_profiler, U32 resample_method)
@ -311,6 +359,11 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title
LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() FMOD Studio initialized correctly" << LL_ENDL; LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() FMOD Studio initialized correctly" << LL_ENDL;
FMOD_ADVANCEDSETTINGS settings_dump = { }; FMOD_ADVANCEDSETTINGS settings_dump = { };
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// With the FMOD debug library used, turns out this object needs to have a size assigned to it otherwise it will fail.
// So the viewer never got any advanced settings for the info below.
settings_dump.cbSize = sizeof(FMOD_ADVANCEDSETTINGS);
// </FS:minerjr> [FIRE-36022]
mSystem->getAdvancedSettings(&settings_dump); mSystem->getAdvancedSettings(&settings_dump);
LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): resampler=" << settings_dump.resamplerMethod << " bytes" << LL_ENDL; LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): resampler=" << settings_dump.resamplerMethod << " bytes" << LL_ENDL;
@ -373,7 +426,47 @@ LLAudioEngine_FMODSTUDIO::output_device_map_t LLAudioEngine_FMODSTUDIO::getDevic
void LLAudioEngine_FMODSTUDIO::setDevice(const LLUUID& device_uuid) void LLAudioEngine_FMODSTUDIO::setDevice(const LLUUID& device_uuid)
{ {
mSelectedDeviceUUID = device_uuid; mSelectedDeviceUUID = device_uuid;
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
{
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
return;
}
// </FS:minerjr> [FIRE-36022]
set_device(mSystem, device_uuid); set_device(mSystem, device_uuid);
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
}
// There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
}
else if (e.code() == std::errc::operation_not_permitted)
{
LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
}
else
{
LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
}
return;
}
catch (const std::exception& e)
{
LL_WARNS() << "Exception FMOD: " << " " << e.what() << LL_ENDL;
return;
}
// </FS:minerjr> [FIRE-36022]
} }
std::string LLAudioEngine_FMODSTUDIO::getDriverName(bool verbose) std::string LLAudioEngine_FMODSTUDIO::getDriverName(bool verbose)

View File

@ -122,6 +122,7 @@ set(llcommon_HEADER_FILES
fsyspath.h fsyspath.h
function_types.h function_types.h
indra_constants.h indra_constants.h
inlinemutexs.h
lazyeventapi.h lazyeventapi.h
linden_common.h linden_common.h
llalignedarray.h llalignedarray.h

View File

@ -0,0 +1,39 @@
/**
* @file inlinemutexs.h
* @brief Declaration of inline mutexs
* @author minerjr@firestorm
*
* $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2025, Minerjr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#ifndef INLINE_MUTEXS_HEADER
#define INLINE_MUTEXS_HEADER
#include <mutex>
#include <chrono>
#include <atomic>
// Audio device mutex to be shared between audio engine and Voice systems to
// syncronize on when audio hardware accessed for disconnected/connecting hardware
// Uses Timed Mutex so as to not lockup the threads forever.
inline std::timed_mutex gAudioDeviceMutex;
#endif

View File

@ -27,6 +27,11 @@
#include "llwebrtc_impl.h" #include "llwebrtc_impl.h"
#include <algorithm> #include <algorithm>
#include <string.h> #include <string.h>
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Needed for accessing the inline timed mutex for accessing audio hardware.
#include <mutex>
#include <chrono>
// </FS:minerjr> [FIRE-36022]
#include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_decoder_factory.h"
#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/audio_encoder_factory.h"
@ -39,6 +44,14 @@
#include "modules/audio_mixer/audio_mixer_impl.h" #include "modules/audio_mixer/audio_mixer_impl.h"
#include "api/environment/environment_factory.h" #include "api/environment/environment_factory.h"
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Audio device mutex to be shared between audio engine and Voice systems to
// syncronize on when audio hardware accessed for disconnected/connecting hardware
// Uses Timed Mutex so as to not lockup the threads forever.
inline std::timed_mutex gAudioDeviceMutex;
// Need to use to access the 3 second timeout for the lock.
using namespace std::chrono_literals;
// </FS:minerjr> [FIRE-36022]
namespace llwebrtc namespace llwebrtc
{ {
#if WEBRTC_WIN #if WEBRTC_WIN
@ -269,7 +282,11 @@ void LLWebRTCImpl::init()
webrtc::InitializeSSL(); webrtc::InitializeSSL();
// Normal logging is rather spammy, so turn it off. // Normal logging is rather spammy, so turn it off.
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Turn on more verbose logging as we are looking for crashes.
webrtc::LogMessage::LogToDebug(webrtc::LS_NONE); webrtc::LogMessage::LogToDebug(webrtc::LS_NONE);
//webrtc::LogMessage::LogToDebug(webrtc::LS_VERBOSE);
// </FS:minerjr> [FIRE-36022]
webrtc::LogMessage::SetLogToStderr(true); webrtc::LogMessage::SetLogToStderr(true);
webrtc::LogMessage::AddLogToStream(mLogSink, webrtc::LS_VERBOSE); webrtc::LogMessage::AddLogToStream(mLogSink, webrtc::LS_VERBOSE);
@ -457,8 +474,24 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer)
// must be run in the worker thread. // must be run in the worker thread.
void LLWebRTCImpl::workerDeployDevices() void LLWebRTCImpl::workerDeployDevices()
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
{
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
return;
}
// </FS:minerjr> [FIRE-36022]
if (!mDeviceModule) if (!mDeviceModule)
{ {
// <FS:minerjr> [FIRE-36022]
// If the device is not avaiable, then make sure the flag for the WebRTC updated devices flag is turned off for the co-routine
gWebRTCUpdateDevices = false;
// </FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
return; return;
} }
@ -483,6 +516,10 @@ void LLWebRTCImpl::workerDeployDevices()
} }
} }
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Flag the device is being interacted with for the Co-routine in case something goes wrong.
gWebRTCUpdateDevices = true;
// </FS:minerjr> [FIRE-36022]
mDeviceModule->StopPlayout(); mDeviceModule->StopPlayout();
mDeviceModule->ForceStopRecording(); mDeviceModule->ForceStopRecording();
#if WEBRTC_WIN #if WEBRTC_WIN
@ -546,6 +583,10 @@ void LLWebRTCImpl::workerDeployDevices()
{ {
mDeviceModule->StartPlayout(); mDeviceModule->StartPlayout();
} }
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Finally signal to the co-routine everyting is OK.
gWebRTCUpdateDevices = false;
// </FS:minerjr> [FIRE-36022]
mSignalingThread->PostTask( mSignalingThread->PostTask(
[this] [this]
{ {
@ -566,6 +607,38 @@ void LLWebRTCImpl::workerDeployDevices()
mWorkerThread->PostTask([this] { workerDeployDevices(); }); mWorkerThread->PostTask([this] { workerDeployDevices(); });
} }
}); });
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
}
// There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
// Another thead may have alreayd called this method
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else if (e.code() == std::errc::operation_not_permitted)
{
// This should not be reached
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else
{
// Log any other message
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
catch (const std::exception& e)
{
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
// </FS:minerjr> [FIRE-36022]
} }
void LLWebRTCImpl::setCaptureDevice(const std::string &id) void LLWebRTCImpl::setCaptureDevice(const std::string &id)
@ -584,11 +657,27 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)
// updateDevices needs to happen on the worker thread. // updateDevices needs to happen on the worker thread.
void LLWebRTCImpl::updateDevices() void LLWebRTCImpl::updateDevices()
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
{
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
return;
}
// </FS:minerjr> [FIRE-36022]
if (!mDeviceModule) if (!mDeviceModule)
{ {
return; return;
} }
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Flag the device is being interacted with for the Co-routine in case something goes wrong.
gWebRTCUpdateDevices = true;
// </FS:minerjr> [FIRE-36022]
int16_t renderDeviceCount = mDeviceModule->PlayoutDevices(); int16_t renderDeviceCount = mDeviceModule->PlayoutDevices();
mPlayoutDeviceList.clear(); mPlayoutDeviceList.clear();
@ -625,10 +714,46 @@ void LLWebRTCImpl::updateDevices()
mRecordingDeviceList.emplace_back(name, guid); mRecordingDeviceList.emplace_back(name, guid);
} }
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Flag the device is no longer being interacted with for the Co-routine in case something goes wrong.
gWebRTCUpdateDevices = false;
// </FS:minerjr> [FIRE-36022]
for (auto &observer : mVoiceDevicesObserverList) for (auto &observer : mVoiceDevicesObserverList)
{ {
observer->OnDevicesChanged(mPlayoutDeviceList, mRecordingDeviceList); observer->OnDevicesChanged(mPlayoutDeviceList, mRecordingDeviceList);
} }
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
}
// There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
// Another thead may have alreayd called this method
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else if (e.code() == std::errc::operation_not_permitted)
{
// This should not be reached
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else
{
// Log any other message
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
catch (const std::exception& e)
{
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
// </FS:minerjr> [FIRE-36022]
} }
void LLWebRTCImpl::OnDevicesUpdated() void LLWebRTCImpl::OnDevicesUpdated()
@ -726,7 +851,56 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms)
{ {
if (mDeviceModule) if (mDeviceModule)
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is
// invalid
{
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
return;
}
// Flag the device is being interacted with for the Co-routine in case something goes wrong.
gWebRTCUpdateDevices = true;
// </FS:minerjr> [FIRE-36022]
mDeviceModule->ForceStopRecording(); mDeviceModule->ForceStopRecording();
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Finally signal to the co-routine everyting is OK.
gWebRTCUpdateDevices = false;
}
// There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
// Another thead may have alreayd called this method
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else if (e.code() == std::errc::operation_not_permitted)
{
// This should not be reached
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else
{
// Log any other message
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
catch (const std::exception& e)
{
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
// </FS:minerjr> [FIRE-36022]
} }
}, },
webrtc::TimeDelta::Millis(delay_ms)); webrtc::TimeDelta::Millis(delay_ms));
@ -738,8 +912,57 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms)
{ {
if (mDeviceModule) if (mDeviceModule)
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is
// invalid
{
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
return;
}
// Flag the device is being interacted with for the Co-routine in case something goes wrong.
gWebRTCUpdateDevices = true;
// </FS:minerjr> [FIRE-36022]
mDeviceModule->InitRecording(); mDeviceModule->InitRecording();
mDeviceModule->ForceStartRecording(); mDeviceModule->ForceStartRecording();
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Finally signal to the co-routine everyting is OK.
gWebRTCUpdateDevices = false;
}
// There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
// Another thead may have alreayd called this method
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else if (e.code() == std::errc::operation_not_permitted)
{
// This should not be reached
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
else
{
// Log any other message
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
}
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
catch (const std::exception& e)
{
mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
// Device no longer being interacted with
gWebRTCUpdateDevices = false;
return;
}
// </FS:minerjr> [FIRE-36022]
} }
}); });
} }

View File

@ -40,6 +40,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Needed for inline variable for the crash check
#include <atomic>
// </FS:minerjr> [FIRE-36022]
#ifdef LL_MAKEDLL #ifdef LL_MAKEDLL
#ifdef WEBRTC_WIN #ifdef WEBRTC_WIN
@ -53,6 +57,12 @@
#define LLSYMEXPORT /**/ #define LLSYMEXPORT /**/
#endif // LL_MAKEDLL #endif // LL_MAKEDLL
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Create an atomic inline flag that will be shared between the various WebRTC threads and co-routines
// to track of when the audio hardware is being talked to. The co-routine can use it to
// exit if it too many iterations with the hardware locked indicating that the worker thread died.
inline std::atomic<bool> gWebRTCUpdateDevices = false;
// </FS:minerjr> [FIRE-36022]
namespace llwebrtc namespace llwebrtc
{ {

View File

@ -1016,6 +1016,11 @@ bool LLAppViewerWin32::cleanup()
gDXHardware.cleanup(); gDXHardware.cleanup();
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Need to unilitialize connection to COM, otherwise it will be treated as a memroy leak.
CoUninitialize();
// </FS:minerjr> [FIRE-36022]
if (mIsConsoleAllocated) if (mIsConsoleAllocated)
{ {
FreeConsole(); FreeConsole();
@ -1056,6 +1061,19 @@ bool LLAppViewerWin32::initWindow()
LL_WARNS("AppInit") << "Unable to set WindowWidth and WindowHeight for FullScreen mode" << LL_ENDL; LL_WARNS("AppInit") << "Unable to set WindowWidth and WindowHeight for FullScreen mode" << LL_ENDL;
} }
} }
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Acccording to the FMOD spec, you are suppose to initalize COM on the thead that will talk to FMOD. IE the main thread.
// There is a coorisponding CoUninitialize in the shutdown code. Otherwise, FMOD will force the initalize with a warning, but does not clean up COM
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
LL_INFOS() << "WIN32: CoInitializeEx COM as COINIT_APARTMENTTHREADED Successful" << LL_ENDL;
}
else
{
LL_INFOS() << "WIN32: CoInitializeEx COM as COINIT_APARTMENTTHREADED Failed" << LL_ENDL;
}
// </FS:minerjr> [FIRE-36022]
return LLAppViewer::initWindow(); return LLAppViewer::initWindow();
} }

View File

@ -38,6 +38,10 @@ class LLVOAvatar;
#include "llcallingcard.h" // for LLFriendObserver #include "llcallingcard.h" // for LLFriendObserver
#include "llsecapi.h" #include "llsecapi.h"
#include "llcontrol.h" #include "llcontrol.h"
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Need to include for audio device mutex shared with other audio/voice systems.
#include "inlinemutexs.h"
// </FS:minerjr> [FIRE-36022]
// devices // devices

View File

@ -82,6 +82,9 @@ extern void handle_voice_morphing_subscribe();
const std::string VIVOX_VOICE_SERVER_TYPE = "vivox"; const std::string VIVOX_VOICE_SERVER_TYPE = "vivox";
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
using namespace std::chrono_literals; // Needed for shared timed mutex to use time
// </FS:minerjr> [FIRE-36022]
namespace { namespace {
const F32 VOLUME_SCALE_VIVOX = 0.01f; const F32 VOLUME_SCALE_VIVOX = 0.01f;
@ -2413,6 +2416,19 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices()
{ {
if (mCaptureDeviceDirty || mRenderDeviceDirty) if (mCaptureDeviceDirty || mRenderDeviceDirty)
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
{
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
return;
}
// </FS:minerjr> [FIRE-36022]
std::ostringstream stream; std::ostringstream stream;
buildSetCaptureDevice(stream); buildSetCaptureDevice(stream);
@ -2424,6 +2440,35 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices()
} }
llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
}
// There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
// When trying to lock the same lock a second time
LL_WARNS() << "Exception Vinvox: " << e.code() << " " << e.what() << LL_ENDL;
}
else if (e.code() == std::errc::operation_not_permitted)
{
// When the mutex is invalid
LL_WARNS() << "Exception Vinvox: " << e.code() << " " << e.what() << LL_ENDL;
}
else
{
// Everything else
LL_WARNS() << "Exception Vinvox: " << e.code() << " " << e.what() << LL_ENDL;
}
return;
}
catch (const std::exception& e)
{
LL_WARNS() << "Exception Vinvox: " << " " << e.what() << LL_ENDL;
return;
}
// </FS:minerjr> [FIRE-36022]
} }
} }

View File

@ -80,6 +80,9 @@
const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc"; const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc";
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
using namespace std::chrono_literals; // Needed for shared timed mutex to use time
// </FS:minerjr> [FIRE-36022]
namespace { namespace {
const F32 MAX_AUDIO_DIST = 50.0f; const F32 MAX_AUDIO_DIST = 50.0f;
@ -506,6 +509,12 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()
try try
{ {
LLMuteList::getInstance()->addObserver(this); LLMuteList::getInstance()->addObserver(this);
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// Add a counter to check if the main thread locked up
// to prevent this thread/corutine form filling up
// the mMainQueue.
static U32 crash_check = 0;
// </FS:minerjr> [FIRE-36022]
while (!sShuttingDown) while (!sShuttingDown)
{ {
LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("voiceConnectionCoroLoop") LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("voiceConnectionCoroLoop")
@ -591,6 +600,26 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()
// to send position updates. // to send position updates.
updatePosition(); updatePosition();
} }
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// If the device locked, count up by 1
if (gWebRTCUpdateDevices)
{
crash_check++;
}
// Else if the device is not locked, then reset the counter back to 0
else
{
crash_check = 0;
}
// If there are over 10 cycles of the devices being locked, there is a good
// chance that the thread failed due to hardware/audio engine issue.
if (crash_check > 10)
{
LL_WARNS() << "WebRTC detected locked worker thread, will shutdown to prevent total viewer lockup." << LL_ENDL;
// Exit out of the thread and flag WebRTC to shutdown, hopefully clearing the lock and allowing the viewer to continue.
sShuttingDown = true;
}
// </FS:minerjr> [FIRE-36022]
} }
LL::WorkQueue::postMaybe(mMainQueue, LL::WorkQueue::postMaybe(mMainQueue,
[=, this] { [=, this] {
@ -731,6 +760,19 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi
void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices, void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices,
const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices)
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
{
// Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
std::unique_lock lock(gAudioDeviceMutex, 1s);
// If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
// Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
if (!lock.owns_lock())
{
LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
return;
}
// </FS:minerjr> [FIRE-36022]
if (sShuttingDown) if (sShuttingDown)
{ {
return; return;
@ -774,6 +816,34 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi
} }
setDevicesListUpdated(true); setDevicesListUpdated(true);
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
}
catch (const std::system_error& e)
{
if (e.code() == std::errc::resource_deadlock_would_occur)
{
// When trying to lock the same lock a second time
LL_WARNS() << "Exception WebRTC: " << e.code() << " " << e.what() << LL_ENDL;
}
else if (e.code() == std::errc::operation_not_permitted)
{
// When the mutex is invalid
LL_WARNS() << "Exception WebRTC: " << e.code() << " " << e.what() << LL_ENDL;
}
else
{
// Everything else
LL_WARNS() << "Exception WebRTC: " << e.code() << " " << e.what() << LL_ENDL;
}
return;
}
catch (const std::exception& e)
{
LL_WARNS() << "Exception WebRTC: " << " " << e.what() << LL_ENDL;
return;
}
// </FS:minerjr> [FIRE-36022]
} }
void LLWebRTCVoiceClient::clearRenderDevices() void LLWebRTCVoiceClient::clearRenderDevices()
@ -848,6 +918,11 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
float LLWebRTCVoiceClient::tuningGetEnergy(void) float LLWebRTCVoiceClient::tuningGetEnergy(void)
{ {
// <FS:minerjr> [FIRE-36022] - Removing my USB headset crashes entire viewer
// This can cause an error if device interface can be NULL.
if (!mWebRTCDeviceInterface)
return 1.0f;
// </FS:minerjr> [FIRE-36022]
float rms = mWebRTCDeviceInterface->getTuningAudioLevel(); float rms = mWebRTCDeviceInterface->getTuningAudioLevel();
return TUNING_LEVEL_START_POINT - TUNING_LEVEL_SCALE * rms; return TUNING_LEVEL_START_POINT - TUNING_LEVEL_SCALE * rms;
} }