From a6bf38bcadafd3f7b08b63d8ea1642a9f351f6e6 Mon Sep 17 00:00:00 2001 From: Kevin Mitchell Date: Tue, 31 Mar 2015 01:56:17 -0700 Subject: ao/wasapi: add ao hotplug Create a second copy of the change_notify structure for the hotplug ao. change_notify->is_hotplug distinguishes the hotplug version from the regular one monitoring the currently playing ao. Also make the change notification less verbose now that there might be two of them around. --- audio/out/ao_wasapi.c | 20 ++++++ audio/out/ao_wasapi.h | 4 +- audio/out/ao_wasapi_changenotify.c | 129 +++++++++++++++++++------------------ audio/out/ao_wasapi_utils.c | 26 +++++++- audio/out/ao_wasapi_utils.h | 3 + 5 files changed, 118 insertions(+), 64 deletions(-) diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index fb045d1858..8a8d910469 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -391,6 +391,24 @@ static void audio_resume(struct ao *ao) SetEvent(state->hResume); } +static void hotplug_uninit(struct ao *ao) +{ + MP_DBG(ao, "Hotplug uninit\n"); + wasapi_hotplug_uninit(ao); + CoUninitialize(); +} + +static int hotplug_init(struct ao *ao) +{ + MP_DBG(ao, "Hotplug init\n"); + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (wasapi_hotplug_init(ao) != S_OK) { + hotplug_uninit(ao); + return -1; + } + return 0; +} + static void list_devs(struct ao *ao, struct ao_device_list *list) { CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); @@ -410,6 +428,8 @@ const struct ao_driver audio_out_wasapi = { .reset = audio_reset, .resume = audio_resume, .list_devs = list_devs, + .hotplug_init = hotplug_init, + .hotplug_uninit = hotplug_uninit, .priv_size = sizeof(wasapi_state), .options = (const struct m_option[]) { OPT_FLAG("exclusive", opt_exclusive, 0), diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h index 2fd266eab0..3ae0816a8d 100755 --- a/audio/out/ao_wasapi.h +++ b/audio/out/ao_wasapi.h @@ -20,6 +20,7 @@ #ifndef MP_AO_WASAPI_H_ #define MP_AO_WASAPI_H_ +#include #include #include #include @@ -30,10 +31,11 @@ typedef struct change_notify { IMMNotificationClient client; /* this must be first in the structure! */ LPWSTR monitored; /* Monitored device */ + bool is_hotplug; struct ao *ao; } change_notify; -HRESULT wasapi_change_init(struct ao* ao); +HRESULT wasapi_change_init(struct ao* ao, bool is_hotplug); void wasapi_change_uninit(struct ao* ao); #define EXIT_ON_ERROR(hres) \ diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c index 6f2263a3ec..26c3c7ed0d 100755 --- a/audio/out/ao_wasapi_changenotify.c +++ b/audio/out/ao_wasapi_changenotify.c @@ -27,26 +27,6 @@ #include "ao_wasapi.h" #include "ao_wasapi_utils.h" -static char* ERole_to_str(ERole role) -{ - switch (role) { - case eConsole: return "console"; - case eMultimedia: return "multimedia"; - case eCommunications: return "communications"; - default: return ""; - } -} - -static char* EDataFlow_to_str(EDataFlow flow) -{ - switch (flow) { - case eRender: return "render"; - case eCapture: return "capture"; - case eAll: return "all"; - default: return ""; - } -} - static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface( IMMNotificationClient* This, REFIID riid, void **ppvObject) { @@ -84,19 +64,23 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged( change_notify *change = (change_notify *)This; struct ao *ao = change->ao; - if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { + if (change->is_hotplug) { + MP_VERBOSE(ao, "OnDeviceStateChanged triggered: sending hotplug event\n"); + ao_hotplug_event(ao); + } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { switch (dwNewState) { case DEVICE_STATE_DISABLED: case DEVICE_STATE_NOTPRESENT: case DEVICE_STATE_UNPLUGGED: - MP_VERBOSE(ao, - "OnDeviceStateChange triggered - requesting ao reload\n"); + MP_VERBOSE(ao, "OnDeviceStateChanged triggered on device %S: " + "requesting ao reload\n", pwstrDeviceId); ao_request_reload(ao); + break; case DEVICE_STATE_ACTIVE: - default: - return S_OK; + break; } } + return S_OK; } @@ -107,9 +91,11 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded( 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); + if (change->is_hotplug) { + MP_VERBOSE(ao, "OnDeviceAdded triggered: sending hotplug event\n"); + ao_hotplug_event(ao); + } + return S_OK; } @@ -121,10 +107,15 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved( change_notify *change = (change_notify *)This; struct ao *ao = change->ao; - if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { - MP_VERBOSE(ao, "OnDeviceRemoved triggered - requesting ao reload\n"); + if (change->is_hotplug) { + MP_VERBOSE(ao, "OnDeviceRemoved triggered: sending hotplug event\n"); + ao_hotplug_event(ao); + } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { + MP_VERBOSE(ao, "OnDeviceRemoved triggered for device %S: " + "requesting ao reload\n", pwstrDeviceId); ao_request_reload(ao); } + return S_OK; } @@ -138,29 +129,32 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( 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); - /* 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; - } + if (change->is_hotplug) { + MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n"); + ao_hotplug_event(ao); + } else { + /* stay on the device the user specified */ + if (state->opt_device) { + MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " + "staying on specified device %s\n", 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; + /* don't reload if already on the new default */ + if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { + MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " + "already using default device, no reload required\n"); + return S_OK; + } + + /* if we got here, we need to reload */ + MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n"); + ao_request_reload(ao); } - /* if we got here, we need to reload */ - ao_request_reload(ao); - MP_VERBOSE(ao, "Requesting ao reload\n"); return S_OK; } @@ -172,17 +166,21 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged( change_notify *change = (change_notify *)This; struct ao *ao = change->ao; - if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) { - MP_VERBOSE(ao, "OnPropertyValueChanged triggered\n"); - MP_VERBOSE(ao, "Changed property: "); + if (!change->is_hotplug && pwstrDeviceId && + !wcscmp(pwstrDeviceId, change->monitored)) + { + MP_VERBOSE(ao, "OnPropertyValueChanged triggered on device %S\n", + pwstrDeviceId); if (IsEqualPropertyKey(PKEY_AudioEngine_DeviceFormat, key)) { MP_VERBOSE(change->ao, - "PKEY_AudioEngine_DeviceFormat - requesting ao reload\n"); + "Changed property: PKEY_AudioEngine_DeviceFormat " + "- requesting ao reload\n"); ao_request_reload(change->ao); } else { - MP_VERBOSE(ao, "%s\n", mp_PKEY_to_str(&key)); + MP_VERBOSE(ao, "Changed property: %s\n", mp_PKEY_to_str(&key)); } } + return S_OK; } @@ -198,27 +196,34 @@ static CONST_VTBL IMMNotificationClientVtbl sIMMDeviceEnumeratorVtbl_vtbl = { }; -HRESULT wasapi_change_init(struct ao *ao) +HRESULT wasapi_change_init(struct ao *ao, bool is_hotplug) { - 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; + HRESULT hr; /* COM voodoo to emulate c++ class */ change->client.lpVtbl = &sIMMDeviceEnumeratorVtbl_vtbl; - /* 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); + /* so the callbacks can access the ao */ + change->ao = ao; + + /* whether or not this is the hotplug instance */ + change->is_hotplug = is_hotplug; + + if (is_hotplug) { + MP_DBG(ao, "Monitoring for hotplug events\n"); + } else { + /* Get the device string to compare with the pwstrDeviceId */ + hr = IMMDevice_GetId(state->pDevice, &change->monitored); + EXIT_ON_ERROR(hr); + MP_VERBOSE(ao, "Monitoring changes in device %S\n", change->monitored); + } + return hr; exit_label: MP_ERR(state, "Error setting up device change monitoring: %s\n", diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c index 9e2ffe0dff..6ecc6c82a2 100755 --- a/audio/out/ao_wasapi_utils.c +++ b/audio/out/ao_wasapi_utils.c @@ -1211,7 +1211,7 @@ retry: } state->previous_volume = state->initial_volume; - wasapi_change_init(ao); + wasapi_change_init(ao, false); MP_DBG(ao, "Init wasapi thread done\n"); return S_OK; @@ -1221,6 +1221,30 @@ exit_label: return hr; } +HRESULT wasapi_hotplug_init(struct ao *ao) +{ + struct wasapi_state *state = (struct wasapi_state *)ao->priv; + HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + &IID_IMMDeviceEnumerator, (void **)&state->pEnumerator); + EXIT_ON_ERROR(hr); + + hr = wasapi_change_init(ao, true); + EXIT_ON_ERROR(hr); + + return S_OK; +exit_label: + MP_ERR(state, "Error setting up audio hotplug: %s (0x%"PRIx32")\n", + wasapi_explain_err(hr), (uint32_t) hr); + return hr; +} + +void wasapi_hotplug_uninit(struct ao *ao) +{ + struct wasapi_state *state = (struct wasapi_state *)ao->priv; + wasapi_change_uninit(ao); + SAFE_RELEASE(state->pEnumerator, IMMDeviceEnumerator_Release(state->pEnumerator)); +} + void wasapi_thread_uninit(struct ao *ao) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; diff --git a/audio/out/ao_wasapi_utils.h b/audio/out/ao_wasapi_utils.h index a44cd4d45a..7327d2f350 100755 --- a/audio/out/ao_wasapi_utils.h +++ b/audio/out/ao_wasapi_utils.h @@ -47,6 +47,9 @@ void wasapi_dispatch(void); HRESULT wasapi_thread_init(struct ao *ao); void wasapi_thread_uninit(struct ao *ao); +HRESULT wasapi_hotplug_init(struct ao *ao); +void wasapi_hotplug_uninit(struct ao *ao); + HRESULT wasapi_setup_proxies(wasapi_state *state); void wasapi_release_proxies(wasapi_state *state); -- cgit v1.2.3