From f29f16663ab5687c8cd84234cb5f26aaa66abb97 Mon Sep 17 00:00:00 2001 From: Jonathan Yong <10walls@gmail.com> Date: Mon, 17 Nov 2014 03:37:51 -0800 Subject: ao/wasapi: new wasapi device monitoring interface Implement skeleton IMMNotificationClient to watch for changes in the sound device. This will make recovery possible from changes shared mode sample rate, bit depth, "enhancements"/effects and even graceful device removal. http://msdn.microsoft.com/en-us/library/windows/desktop/dd371417%28v=vs.85%29.aspx Signed-off-by: Kevin Mitchell --- audio/out/ao_wasapi.h | 8 ++ audio/out/ao_wasapi_changenotify.c | 171 +++++++++++++++++++++++++++++++++++++ wscript_build.py | 1 + 3 files changed, 180 insertions(+) create mode 100755 audio/out/ao_wasapi_changenotify.c diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h index 55cf79d8e0..43c81a89d2 100755 --- a/audio/out/ao_wasapi.h +++ b/audio/out/ao_wasapi.h @@ -30,6 +30,14 @@ #include "osdep/atomics.h" +typedef struct change_notify { + IMMNotificationClient client; + LPWSTR monitored; /* Monitored device */ +} change_notify; + +HRESULT wasapi_change_init(struct change_notify *change, IMMDevice *monitor); +void wasapi_change_free(struct change_notify *change); + typedef struct wasapi_state { struct mp_log *log; HANDLE threadLoop; diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c new file mode 100755 index 0000000000..d5db1a9e89 --- /dev/null +++ b/audio/out/ao_wasapi_changenotify.c @@ -0,0 +1,171 @@ +/* + * This file is part of mpv. + * + * Original author: Jonathan Yong <10walls@gmail.com> + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#define COBJMACROS 1 +#define _WIN32_WINNT 0x600 + +#include +#include +#include +#include +#include +#include + +#include "ao_wasapi.h" +#include "ao_wasapi_utils.h" + +static int GUID_compare(const GUID *l, const GUID *r) +{ + unsigned int i; + if (l->Data1 != r->Data1) return 1; + if (l->Data2 != r->Data2) return 1; + if (l->Data3 != r->Data3) return 1; + for (i = 0; i < 8; i++) { + if (l->Data4[i] != r->Data4[i]) return 1; + } + return 0; +} + +static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface( + IMMNotificationClient* This, REFIID riid, void **ppvObject) +{ + /* Compatible with IMMNotificationClient and IUnknown */ + if (!GUID_compare(&IID_IMMNotificationClient, riid) || + !GUID_compare(&IID_IUnknown, riid)) { + *ppvObject = (void *)This; + return S_OK; + } else { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +/* these are required, but not actually used */ +static ULONG STDMETHODCALLTYPE sIMMNotificationClient_AddRef( + IMMNotificationClient *This) +{ + return 1; +} + +/* MSDN says it should free itself, but we're static */ +static ULONG STDMETHODCALLTYPE sIMMNotificationClient_Release( + IMMNotificationClient *This) +{ + return 1; +} + +static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged( + IMMNotificationClient *This, + LPCWSTR pwstrDeviceId, + DWORD dwNewState) +{ + change_notify *change = (change_notify *)This; + + if (!wcscmp(change->monitored, pwstrDeviceId)){ + switch (dwNewState) { + case DEVICE_STATE_DISABLED: + case DEVICE_STATE_NOTPRESENT: + case DEVICE_STATE_UNPLUGGED: + + case DEVICE_STATE_ACTIVE: + default: + return S_OK; + } + } + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded( + IMMNotificationClient *This, + LPCWSTR pwstrDeviceId) +{ + change_notify *change = (change_notify *)This; + + return S_OK; +} + +/* maybe MPV can go over to the prefered device once it is plugged in? */ +static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved( + IMMNotificationClient *This, + LPCWSTR pwstrDeviceId) +{ + change_notify *change = (change_notify *)This; + + if (!wcscmp(change->monitored, pwstrDeviceId)) { + + } + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( + IMMNotificationClient *This, + EDataFlow flow, + ERole role, + LPCWSTR pwstrDeviceId) +{ + change_notify *change = (change_notify *)This; + + if (!wcscmp(change->monitored, pwstrDeviceId)) { + + } + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged( + IMMNotificationClient *This, + LPCWSTR pwstrDeviceId, + const PROPERTYKEY key) +{ + change_notify *change = (change_notify *)This; + + if (!wcscmp(change->monitored, pwstrDeviceId)) { + + } + return S_OK; +} + +static CONST_VTBL IMMNotificationClientVtbl sIMMDeviceEnumeratorVtbl_vtbl = { + .QueryInterface = sIMMNotificationClient_QueryInterface, + .AddRef = sIMMNotificationClient_AddRef, + .Release = sIMMNotificationClient_Release, + .OnDeviceStateChanged = sIMMNotificationClient_OnDeviceStateChanged, + .OnDeviceAdded = sIMMNotificationClient_OnDeviceAdded, + .OnDeviceRemoved = sIMMNotificationClient_OnDeviceRemoved, + .OnDefaultDeviceChanged = sIMMNotificationClient_OnDefaultDeviceChanged, + .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; + change->client.lpVtbl = &sIMMDeviceEnumeratorVtbl_vtbl; + return ret; +} + +void wasapi_change_free(struct change_notify *change) +{ + if (change->monitored) CoTaskMemFree(change->monitored); +} diff --git a/wscript_build.py b/wscript_build.py index a2c32494aa..4fefb87004 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -153,6 +153,7 @@ def build(ctx): ( "audio/out/ao_sndio.c", "sndio" ), ( "audio/out/ao_wasapi.c", "wasapi" ), ( "audio/out/ao_wasapi_utils.c", "wasapi" ), + ( "audio/out/ao_wasapi_changenotify.c", "wasapi" ), ( "audio/out/pull.c" ), ( "audio/out/push.c" ), -- cgit v1.2.3