wasapi: Proxy default device change handling to management thread.

This does a ton of work that can deadlock, because several crucial WASAPI
things that we want to do in response to this will block until the
notification callback has returned, so we can't call them from the handler
directly, or we'll be waiting until the thing that called us returns.
main
Ryan C. Gordon 2023-10-26 22:59:02 -04:00
parent c45b5121ce
commit 9bec57309c
No known key found for this signature in database
GPG Key ID: FA148B892AB48044
4 changed files with 26 additions and 5 deletions

View File

@ -647,7 +647,7 @@ static SDL_bool DSOUND_Init(SDL_AudioDriverImpl *impl)
}
#ifdef HAVE_MMDEVICEAPI_H
SupportsIMMDevice = !(SDL_IMMDevice_Init() < 0);
SupportsIMMDevice = !(SDL_IMMDevice_Init(NULL) < 0);
#endif
impl->DetectDevices = DSOUND_DetectDevices;

View File

@ -48,11 +48,22 @@ static SDL_bool immdevice_initialized = SDL_FALSE;
// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
static int mgmtthrtask_DefaultAudioDeviceChanged(void *userdata)
{
SDL_DefaultAudioDeviceChanged((SDL_AudioDevice *) userdata);
return 0;
}
static void WASAPI_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
{
WASAPI_ProxyToManagementThread(mgmtthrtask_DetectDevices, new_default_device, NULL); // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock.
}
int WASAPI_PlatformInit(void)
{
if (FAILED(WIN_CoInitialize())) {
return SDL_SetError("CoInitialize() failed");
} else if (SDL_IMMDevice_Init() < 0) {
} else if (SDL_IMMDevice_Init(WASAPI_DefaultAudioDeviceChanged) < 0) {
return -1; // Error string is set by SDL_IMMDevice_Init
}

View File

@ -37,6 +37,7 @@ static const ERole SDL_IMMDevice_role = eConsole; /* !!! FIXME: should this be e
/* This is global to the WASAPI target, to handle hotplug and default device lookup. */
static IMMDeviceEnumerator *enumerator = NULL;
static SDL_IMMDevice_DefaultAudioDeviceChanged devchangecallback = NULL;
/* PropVariantInit() is an inline function/macro in PropIdl.h that calls the C runtime's memset() directly. Use ours instead, to avoid dependency. */
#ifdef PropVariantInit
@ -204,7 +205,9 @@ static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationCl
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
{
if (role == SDL_IMMDevice_role) {
SDL_DefaultAudioDeviceChanged(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
if (devchangecallback) {
devchangecallback(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
}
}
return S_OK;
}
@ -273,7 +276,7 @@ static const IMMNotificationClientVtbl notification_client_vtbl = {
static SDLMMNotificationClient notification_client = { &notification_client_vtbl, { 1 } };
int SDL_IMMDevice_Init(void)
int SDL_IMMDevice_Init(SDL_IMMDevice_DefaultAudioDeviceChanged devchanged)
{
HRESULT ret;
@ -291,6 +294,9 @@ int SDL_IMMDevice_Init(void)
WIN_CoUninitialize();
return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
}
devchangecallback = devchanged ? devchanged : SDL_DefaultAudioDeviceChanged;
return 0;
}
@ -302,6 +308,8 @@ void SDL_IMMDevice_Quit(void)
enumerator = NULL;
}
devchangecallback = NULL;
WIN_CoUninitialize();
}

View File

@ -28,7 +28,9 @@
typedef struct SDL_AudioDevice SDL_AudioDevice; // this is defined in src/audio/SDL_sysaudio.h
int SDL_IMMDevice_Init(void);
typedef void (*SDL_IMMDevice_DefaultAudioDeviceChanged)(SDL_AudioDevice *new_default_device);
int SDL_IMMDevice_Init(SDL_IMMDevice_DefaultAudioDeviceChanged devchanged);
void SDL_IMMDevice_Quit(void);
int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture);
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);