From 6c512892d4691e004c453af5f0c7f3837d87b7fb Mon Sep 17 00:00:00 2001 From: Kevin Mitchell Date: Thu, 13 Nov 2014 01:09:47 -0800 Subject: ao/wasapi: request reset on appropriate events on changes to PKEY_AudioEngine_DeviceFormat, device status, and default device. call ao_reload directly in the change_notify "methods". this requires keeping a device enumerator around for the duration of execution, rather than just for initially querying devices --- audio/out/ao_wasapi.c | 5 -- audio/out/ao_wasapi.h | 16 ++++-- audio/out/ao_wasapi_changenotify.c | 102 ++++++++++++++++++++++++++++++------- audio/out/ao_wasapi_utils.c | 50 ++++++++---------- 4 files changed, 117 insertions(+), 56 deletions(-) diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index 42317f7b0e..6d66e5bdc3 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -36,11 +36,6 @@ #include "osdep/timer.h" #include "osdep/io.h" -#define EXIT_ON_ERROR(hres) \ - do { if (FAILED(hres)) { goto exit_label; } } while(0) -#define SAFE_RELEASE(unk, release) \ - do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0) - static double get_device_delay(struct wasapi_state *state) { UINT64 sample_count = atomic_load(&state->sample_count); UINT64 position, qpc_position; diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h index 43c81a89d2..7e7cc6cd06 100755 --- a/audio/out/ao_wasapi.h +++ b/audio/out/ao_wasapi.h @@ -31,12 +31,18 @@ #include "osdep/atomics.h" typedef struct change_notify { - IMMNotificationClient client; + IMMNotificationClient client; /* this must be first in the structure! */ LPWSTR monitored; /* Monitored device */ + struct ao *ao; } change_notify; -HRESULT wasapi_change_init(struct change_notify *change, IMMDevice *monitor); -void wasapi_change_free(struct change_notify *change); +HRESULT wasapi_change_init(struct ao* ao); +void wasapi_change_uninit(struct ao* ao); + +#define EXIT_ON_ERROR(hres) \ + do { if (FAILED(hres)) { goto exit_label; } } while(0) +#define SAFE_RELEASE(unk, release) \ + do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0) typedef struct wasapi_state { struct mp_log *log; @@ -70,6 +76,8 @@ typedef struct wasapi_state { ISimpleAudioVolume *pAudioVolume; IAudioEndpointVolume *pEndpointVolume; IAudioSessionControl *pSessionControl; + IMMDeviceEnumerator *pEnumerator; + HANDLE hFeed; /* wasapi event */ HANDLE hForceFeed; /* forces writing a buffer (e.g. before audio_resume) */ HANDLE hFeedDone; /* set only after a hForceFeed */ @@ -108,6 +116,8 @@ typedef struct wasapi_state { HANDLE (WINAPI *pAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); WINBOOL (WINAPI *pAvRevertMmThreadCharacteristics)(HANDLE); } VistaBlob; + + change_notify change; } wasapi_state; #endif diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c index 7ff6b16d0d..26bc67d56d 100755 --- a/audio/out/ao_wasapi_changenotify.c +++ b/audio/out/ao_wasapi_changenotify.c @@ -125,13 +125,16 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged( DWORD dwNewState) { change_notify *change = (change_notify *)This; + struct ao *ao = change->ao; - if (!wcscmp(change->monitored, pwstrDeviceId)){ + if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)){ switch (dwNewState) { case DEVICE_STATE_DISABLED: case DEVICE_STATE_NOTPRESENT: case DEVICE_STATE_UNPLUGGED: - + MP_VERBOSE(ao, + "OnDeviceStateChange triggered - requesting ao reload\n"); + ao_request_reload(ao); case DEVICE_STATE_ACTIVE: default: return S_OK; @@ -145,7 +148,11 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded( LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; + struct ao *ao = change->ao; + MP_VERBOSE(ao, "OnDeviceAdded triggered\n"); + if(pwstrDeviceId) + MP_VERBOSE(ao, "New device %S\n",pwstrDeviceId); return S_OK; } @@ -155,9 +162,11 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved( LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; + struct ao *ao = change->ao; - if (!wcscmp(change->monitored, pwstrDeviceId)) { - + if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { + MP_VERBOSE(ao, "OnDeviceRemoved triggered - requesting ao reload\n"); + ao_request_reload(ao); } return S_OK; } @@ -169,10 +178,33 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; + struct ao *ao = change->ao; + struct wasapi_state *state = (struct wasapi_state *)ao->priv; + + MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered for role:%s flow:%s\n", + ERole_to_str(role), EDataFlow_to_str(flow)); + if(pwstrDeviceId) + MP_VERBOSE(ao, "New default device %S\n", pwstrDeviceId); - if (!wcscmp(change->monitored, pwstrDeviceId)) { + /* don't care about "eCapture" or non-"eMultimedia" roles */ + if ( flow == eCapture || + role != eMultimedia ) return S_OK; + /* stay on the device the user specified */ + if (state->opt_device) { + MP_VERBOSE(ao, "Staying on specified device \"%s\"", state->opt_device); + return S_OK; } + + /* don't reload if already on the new default */ + if ( pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId) ){ + MP_VERBOSE(ao, "Already using default device, no reload required\n"); + return S_OK; + } + + /* if we got here, we need to reload */ + ao_request_reload(ao); + MP_VERBOSE(ao, "Requesting ao reload\n"); return S_OK; } @@ -182,9 +214,18 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged( const PROPERTYKEY key) { change_notify *change = (change_notify *)This; - - if (!wcscmp(change->monitored, pwstrDeviceId)) { - + struct ao *ao = change->ao; + + if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { + MP_VERBOSE(ao, "OnPropertyValueChanged triggered\n"); + MP_VERBOSE(ao, "Changed property: "); + if (!PKEY_compare(&PKEY_AudioEngine_DeviceFormat, &key)) { + MP_VERBOSE(change->ao, + "PKEY_AudioEngine_DeviceFormat - requesting ao reload\n"); + ao_request_reload(change->ao); + } else { + MP_VERBOSE(ao, "%s\n", PKEY_to_str(&key)); + } } return S_OK; } @@ -200,21 +241,44 @@ static CONST_VTBL IMMNotificationClientVtbl sIMMDeviceEnumeratorVtbl_vtbl = { .OnPropertyValueChanged = sIMMNotificationClient_OnPropertyValueChanged, }; -HRESULT wasapi_change_init(struct change_notify *change, IMMDevice *monitor) -{ - HRESULT ret; - - if ( ((ret = IMMDevice_GetId(monitor, &change->monitored)) != S_OK) ) { - wasapi_change_free(change); - return ret; - } - ret = S_OK; +HRESULT wasapi_change_init(struct ao *ao) +{ + MP_DBG(ao, "Setting up monitoring on playback device\n"); + struct wasapi_state *state = (struct wasapi_state *)ao->priv; + struct change_notify *change = &state->change; + /* COM voodoo to emulate c++ class */ change->client.lpVtbl = &sIMMDeviceEnumeratorVtbl_vtbl; - return ret; + + /* so the callbacks can access the ao */ + change->ao = ao; + + /* get the device string to compare with the pwstrDeviceId argument in callbacks */ + HRESULT hr = IMMDevice_GetId(state->pDevice, &change->monitored); + EXIT_ON_ERROR(hr); + + /* register the change notification client */ + hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback( + state->pEnumerator, (IMMNotificationClient *)change); + EXIT_ON_ERROR(hr); + + MP_VERBOSE(state, "Monitoring changes in device: %S\n", state->change.monitored); + return hr; +exit_label: + MP_ERR(state, "Error setting up device change monitoring: %s\n", + wasapi_explain_err(hr)); + wasapi_change_uninit(ao); + return hr; } -void wasapi_change_free(struct change_notify *change) +void wasapi_change_uninit(struct ao *ao) { + struct wasapi_state *state = (struct wasapi_state *)ao->priv; + struct change_notify *change = &state->change; + + if( state->pEnumerator && change->client.lpVtbl ) + IMMDeviceEnumerator_UnregisterEndpointNotificationCallback( + state->pEnumerator, (IMMNotificationClient *)change); + if (change->monitored) CoTaskMemFree(change->monitored); } diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c index c3a8eaa2b7..937c144650 100755 --- a/audio/out/ao_wasapi_utils.c +++ b/audio/out/ao_wasapi_utils.c @@ -33,11 +33,6 @@ #define MIXER_DEFAULT_LABEL L"mpv - video player" -#define EXIT_ON_ERROR(hres) \ - do { if (FAILED(hres)) { goto exit_label; } } while(0) -#define SAFE_RELEASE(unk, release) \ - do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0) - #ifndef PKEY_Device_FriendlyName DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, @@ -728,20 +723,12 @@ exit_label: return 1; } -static HRESULT load_default_device(struct ao *ao, IMMDevice **ppDevice) +static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator, + IMMDevice **ppDevice) { - HRESULT hr; - struct wasapi_state *state = (struct wasapi_state *)ao->priv; - - IMMDeviceEnumerator *pEnumerator; - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void**)&pEnumerator); - EXIT_ON_ERROR(hr); - - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, - eRender, eMultimedia, - ppDevice); - SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator)); + HRESULT hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, + eRender, eMultimedia, + ppDevice); EXIT_ON_ERROR(hr); char *id = get_device_id(*ppDevice); @@ -750,16 +737,15 @@ static HRESULT load_default_device(struct ao *ao, IMMDevice **ppDevice) return S_OK; exit_label: - MP_ERR(state, "Error loading default device: %s (0x%"PRIx32")\n", + MP_ERR(ao , "Error loading default device: %s (0x%"PRIx32")\n", wasapi_explain_err(hr), (uint32_t)hr); return hr; } -static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice, - char *search) +static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator, + IMMDevice **ppDevice, char *search) { HRESULT hr; - IMMDeviceEnumerator *pEnumerator = NULL; IMMDeviceCollection *pDevices = NULL; IMMDevice *pTempDevice = NULL; LPWSTR deviceID = NULL; @@ -772,10 +758,6 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice, devid = search; } - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void**)&pEnumerator); - EXIT_ON_ERROR(hr); - int search_err = 0; if (devid == NULL) { @@ -866,7 +848,7 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDevice **ppDevice, exit_label: SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice)); SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices)); - SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator)); + CoTaskMemFree(deviceID); return hr; } @@ -963,14 +945,19 @@ HRESULT wasapi_thread_init(struct ao *ao) MP_DBG(ao, "Init wasapi thread\n"); state->initial_volume = -1.0; + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + &IID_IMMDeviceEnumerator, (void**)&state->pEnumerator); + EXIT_ON_ERROR(hr); + char *device = state->opt_device; if (!device || !device[0]) device = ao->device; + if (!device || !device[0]) - hr = load_default_device(ao, &state->pDevice); + hr = load_default_device(ao, state->pEnumerator, &state->pDevice); else - hr = find_and_load_device(ao, &state->pDevice, device); + hr = find_and_load_device(ao, state->pEnumerator, &state->pDevice, device); EXIT_ON_ERROR(hr); char *name = get_device_name(state->pDevice); @@ -1019,6 +1006,8 @@ HRESULT wasapi_thread_init(struct ao *ao) state->previous_volume = state->initial_volume; + wasapi_change_init(ao); + MP_DBG(ao, "Init wasapi thread done\n"); return S_OK; exit_label: @@ -1036,6 +1025,8 @@ void wasapi_thread_uninit(struct ao *ao) if (state->pAudioClient) IAudioClient_Stop(state->pAudioClient); + wasapi_change_uninit(ao); + if (state->opt_exclusive && state->pEndpointVolume && state->initial_volume > 0 ) @@ -1049,6 +1040,7 @@ void wasapi_thread_uninit(struct ao *ao) SAFE_RELEASE(state->pSessionControl, IAudioSessionControl_Release(state->pSessionControl)); SAFE_RELEASE(state->pAudioClient, IAudioClient_Release(state->pAudioClient)); SAFE_RELEASE(state->pDevice, IMMDevice_Release(state->pDevice)); + SAFE_RELEASE(state->pEnumerator, IMMDeviceEnumerator_Release(state->pEnumerator)); if (state->hTask) state->VistaBlob.pAvRevertMmThreadCharacteristics(state->hTask); -- cgit v1.2.3