[vlc-commits] mmdevice: implement device notification client (fixes #7202)

Rémi Denis-Courmont git at videolan.org
Wed Jan 29 20:08:56 CET 2014


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Wed Jan 29 21:02:57 2014 +0200| [f945150eb86180aa68f74c2c33bed18182687531] | committer: Rémi Denis-Courmont

mmdevice: implement device notification client (fixes #7202)

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=f945150eb86180aa68f74c2c33bed18182687531
---

 modules/audio_output/mmdevice.c |  220 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 204 insertions(+), 16 deletions(-)

diff --git a/modules/audio_output/mmdevice.c b/modules/audio_output/mmdevice.c
index dea9cd6..06f1892 100644
--- a/modules/audio_output/mmdevice.c
+++ b/modules/audio_output/mmdevice.c
@@ -77,9 +77,9 @@ struct aout_sys_t
 #if !VLC_WINSTORE_APP
     audio_output_t *aout;
     IMMDeviceEnumerator *it; /**< Device enumerator, NULL when exiting */
-    /*TODO: IMMNotificationClient*/
     IMMDevice *dev; /**< Selected output device, NULL if none */
 
+    struct IMMNotificationClient device_events;
     struct IAudioSessionEvents session_events;
 
     LONG refs;
@@ -365,6 +365,203 @@ static const struct IAudioSessionEventsVtbl vlc_AudioSessionEvents =
 };
 
 /*** Audio devices ***/
+
+/** Gets the user-readable device name */
+static char *DeviceName(IMMDevice *dev)
+{
+    IPropertyStore *props;
+    char *name = NULL;
+    PROPVARIANT v;
+    HRESULT hr;
+
+    hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &props);
+    if (FAILED(hr))
+        return NULL;
+
+    PropVariantInit(&v);
+    hr = IPropertyStore_GetValue(props, &PKEY_Device_FriendlyName, &v);
+    if (SUCCEEDED(hr))
+    {
+        name = FromWide(v.pwszVal);
+        PropVariantClear(&v);
+    }
+    IPropertyStore_Release(props);
+    return name;
+}
+
+/** Checks that a device is an output device */
+static bool DeviceIsRender(IMMDevice *dev)
+{
+    void *pv;
+
+    if (FAILED(IMMDevice_QueryInterface(dev, &IID_IMMEndpoint, &pv)))
+        return false;
+
+    IMMEndpoint *ep = pv;
+    EDataFlow flow;
+
+    if (FAILED(IMMEndpoint_GetDataFlow(ep, &flow)))
+        flow = eCapture;
+
+    IMMEndpoint_Release(ep);
+    return flow == eRender;
+}
+
+static HRESULT DeviceUpdated(audio_output_t *aout, LPCWSTR wid)
+{
+    aout_sys_t *sys = aout->sys;
+    HRESULT hr;
+
+    IMMDevice *dev;
+    hr = IMMDeviceEnumerator_GetDevice(sys->it, wid, &dev);
+    if (FAILED(hr))
+        return hr;
+
+    if (!DeviceIsRender(dev))
+    {
+        IMMDevice_Release(dev);
+        return S_OK;
+    }
+
+    char *id = FromWide(wid);
+    if (unlikely(id == NULL))
+    {
+        IMMDevice_Release(dev);
+        return E_OUTOFMEMORY;
+    }
+
+    char *name = DeviceName(dev);
+    IMMDevice_Release(dev);
+
+    aout_HotplugReport(aout, id, (name != NULL) ? name : id);
+    free(name);
+    free(id);
+    return S_OK;
+}
+
+static inline aout_sys_t *vlc_MMNotificationClient_sys(IMMNotificationClient *this)
+{
+    return (void *)(((char *)this) - offsetof(aout_sys_t, device_events));
+}
+
+static STDMETHODIMP
+vlc_MMNotificationClient_QueryInterface(IMMNotificationClient *this,
+                                        REFIID riid, void **ppv)
+{
+    if (IsEqualIID(riid, &IID_IUnknown)
+     || IsEqualIID(riid, &IID_IMMNotificationClient))
+    {
+        *ppv = this;
+        IUnknown_AddRef(this);
+        return S_OK;
+    }
+    else
+    {
+       *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+static STDMETHODIMP_(ULONG)
+vlc_MMNotificationClient_AddRef(IMMNotificationClient *this)
+{
+    aout_sys_t *sys = vlc_MMNotificationClient_sys(this);
+    return InterlockedIncrement(&sys->refs);
+}
+
+static STDMETHODIMP_(ULONG)
+vlc_MMNotificationClient_Release(IMMNotificationClient *this)
+{
+    aout_sys_t *sys = vlc_MMNotificationClient_sys(this);
+    return InterlockedDecrement(&sys->refs);
+}
+
+static STDMETHODIMP
+vlc_MMNotificationClient_OnDefaultDeviceChange(IMMNotificationClient *this,
+                                               EDataFlow flow, ERole role,
+                                               LPCWSTR wid)
+{
+    aout_sys_t *sys = vlc_MMNotificationClient_sys(this);
+    audio_output_t *aout = sys->aout;
+
+    if (flow != eRender)
+        return S_OK;
+    if (role != eConsole) /* FIXME? use eMultimedia instead */
+        return S_OK;
+
+    msg_Dbg(aout, "default device changed: %ls", wid); /* TODO? migrate */
+    return S_OK;
+}
+
+static STDMETHODIMP
+vlc_MMNotificationClient_OnDeviceAdded(IMMNotificationClient *this,
+                                       LPCWSTR wid)
+{
+    aout_sys_t *sys = vlc_MMNotificationClient_sys(this);
+    audio_output_t *aout = sys->aout;
+
+    msg_Dbg(aout, "device %ls added", wid);
+    return DeviceUpdated(aout, wid);
+}
+
+static STDMETHODIMP
+vlc_MMNotificationClient_OnDeviceRemoved(IMMNotificationClient *this,
+                                         LPCWSTR wid)
+{
+    aout_sys_t *sys = vlc_MMNotificationClient_sys(this);
+    audio_output_t *aout = sys->aout;
+    char *id = FromWide(wid);
+
+    msg_Dbg(aout, "device %ls removed", wid);
+    if (unlikely(id == NULL))
+        return E_OUTOFMEMORY;
+
+    aout_HotplugReport(aout, id, NULL);
+    free(id);
+    return S_OK;
+}
+
+static STDMETHODIMP
+vlc_MMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *this,
+                                              LPCWSTR wid, DWORD state)
+{
+    aout_sys_t *sys = vlc_MMNotificationClient_sys(this);
+    audio_output_t *aout = sys->aout;
+
+    /* TODO: show device state / ignore missing devices */
+    msg_Dbg(aout, "device %ls state changed %08lx", wid, state);
+    return S_OK;
+}
+
+static STDMETHODIMP
+vlc_MMNotificationClient_OnDevicePropertyChanged(IMMNotificationClient *this,
+                                                 LPCWSTR wid,
+                                                 const PROPERTYKEY key)
+{
+    aout_sys_t *sys = vlc_MMNotificationClient_sys(this);
+    audio_output_t *aout = sys->aout;
+
+    if (key.pid == PKEY_Device_FriendlyName.pid)
+    {
+        msg_Dbg(aout, "device %ls name changed", wid);
+        return DeviceUpdated(aout, wid);
+    }
+    return S_OK;
+}
+
+static const struct IMMNotificationClientVtbl vlc_MMNotificationClient =
+{
+    vlc_MMNotificationClient_QueryInterface,
+    vlc_MMNotificationClient_AddRef,
+    vlc_MMNotificationClient_Release,
+
+    vlc_MMNotificationClient_OnDeviceStateChanged,
+    vlc_MMNotificationClient_OnDeviceAdded,
+    vlc_MMNotificationClient_OnDeviceRemoved,
+    vlc_MMNotificationClient_OnDefaultDeviceChange,
+    vlc_MMNotificationClient_OnDevicePropertyChanged,
+};
+
 static int DevicesEnum(audio_output_t *aout, IMMDeviceEnumerator *it)
 {
     HRESULT hr;
@@ -391,7 +588,7 @@ static int DevicesEnum(audio_output_t *aout, IMMDeviceEnumerator *it)
     for (UINT i = 0; i < count; i++)
     {
         IMMDevice *dev;
-        char *id, *name = NULL;
+        char *id, *name;
 
         hr = IMMDeviceCollection_Item(devs, i, &dev);
         if (FAILED(hr))
@@ -408,20 +605,7 @@ static int DevicesEnum(audio_output_t *aout, IMMDeviceEnumerator *it)
         id = FromWide(devid);
         CoTaskMemFree(devid);
 
-        /* User-readable device name */
-        IPropertyStore *props;
-        hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &props);
-        if (SUCCEEDED(hr))
-        {
-            PROPVARIANT v;
-
-            PropVariantInit(&v);
-            hr = IPropertyStore_GetValue(props, &PKEY_Device_FriendlyName, &v);
-            if (SUCCEEDED(hr))
-                name = FromWide(v.pwszVal);
-            PropVariantClear(&v);
-            IPropertyStore_Release(props);
-        }
+        name = DeviceName(dev);
         IMMDevice_Release(dev);
 
         aout_HotplugReport(aout, id, (name != NULL) ? name : id);
@@ -785,6 +969,8 @@ static int Open(vlc_object_t *obj)
     aout->mute_set = MuteSet;
     aout->device_select = DeviceSelect;
     EnterMTA();
+    IMMDeviceEnumerator_RegisterEndpointNotificationCallback(sys->it,
+                                                          &sys->device_events);
     DevicesEnum(aout, sys->it);
     LeaveMTA();
     return VLC_SUCCESS;
@@ -812,6 +998,8 @@ static void Close(vlc_object_t *obj)
     WakeConditionVariable(&sys->work);
     LeaveCriticalSection(&sys->lock);
 
+    IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(it,
+                                                          &sys->device_events);
     IMMDeviceEnumerator_Release(it);
     LeaveMTA();
 



More information about the vlc-commits mailing list