]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/mmdevice.c
chorus: remove wrong (and uneeded) dealocations
[vlc] / modules / audio_output / mmdevice.c
index 4a0db0ef05e5f1e152eda738f839bb089c78042d..285e725b79676c7a079b56ad75a99afe68efa356 100644 (file)
@@ -40,7 +40,7 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd,
 #include <vlc_plugin.h>
 #include <vlc_aout.h>
 #include <vlc_charset.h>
-#include "mmdevice.h"
+#include "audio_output/mmdevice.h"
 
 DEFINE_GUID (GUID_VLC_AUD_OUT, 0x4533f59d, 0x59ee, 0x00c6,
    0xad, 0xb2, 0xc6, 0x8b, 0x50, 0x1a, 0x66, 0x55);
@@ -74,23 +74,27 @@ struct aout_sys_t
     audio_output_t *aout;
     aout_stream_t *stream; /**< Underlying audio output stream */
 
+    IMMDevice *dev; /**< Selected output device, NULL if none */
+
+    ISimpleAudioVolume *volume; /**< Volume setter */
+
+#if !VLC_WINSTORE_APP
     IMMDeviceEnumerator *it; /**< Device enumerator, NULL when exiting */
     /*TODO: IMMNotificationClient*/
 
-    IMMDevice *dev; /**< Selected output device, NULL if none */
     IAudioSessionManager *manager; /**< Session for the output device */
     struct IAudioSessionEvents session_events;
-    ISimpleAudioVolume *volume; /**< Volume setter */
 
     LONG refs;
     HANDLE device_changed; /**< Event to reset thread */
     HANDLE device_ready; /**< Event when thread is reset */
     vlc_thread_t thread; /**< Thread for audio session control */
+#endif
 };
 
 /* NOTE: The Core Audio API documentation totally fails to specify the thread
  * safety (or lack thereof) of the interfaces. This code takes the most
- * restrictive assumption, no thread safety: The background thread (MMThread)
+ * restrictive assumption: no thread safety. The background thread (MMThread)
  * only runs at specified times, namely between the device_ready and
  * device_changed events (effectively, a thread barrier but only Windows 8
  * provides thread barriers natively).
@@ -104,7 +108,7 @@ static int vlc_FromHR(audio_output_t *aout, HRESULT hr)
 {
     /* Restart on unplug */
     if (unlikely(hr == AUDCLNT_E_DEVICE_INVALIDATED))
-        var_TriggerCallback(aout, "audio-device");
+        aout_RestartRequest(aout, AOUT_RESTART_OUTPUT);
     return SUCCEEDED(hr) ? 0 : -1;
 }
 
@@ -200,93 +204,7 @@ static int MuteSet(audio_output_t *aout, bool mute)
     return FAILED(hr) ? -1 : 0;
 }
 
-/*** Audio devices ***/
-static int DeviceChanged(vlc_object_t *obj, const char *varname,
-                         vlc_value_t prev, vlc_value_t cur, void *data)
-{
-    /* FIXME: This does not work. sys->dev, ->manager and ->stream must be
-     * recreated. Those pointers are protected by the aout lock, which
-     * serializes accesses to the audio_output_t. Unfortunately,
-     * aout lock cannot be taken from a variable callback.
-     * Solution: add device_change callback to audio_output_t. */
-    aout_ChannelsRestart(obj, varname, prev, cur, data);
-    return VLC_SUCCESS;
-}
-
-static void GetDevices(vlc_object_t *obj, IMMDeviceEnumerator *it)
-{
-    HRESULT hr;
-    vlc_value_t val, text;
-
-    var_Create (obj, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
-    text.psz_string = _("Audio Device");
-    var_Change (obj, "audio-device", VLC_VAR_SETTEXT, &text, NULL);
-
-    /* TODO: implement IMMNotificationClient for hotplug devices */
-    IMMDeviceCollection *devs;
-    hr = IMMDeviceEnumerator_EnumAudioEndpoints(it, eRender,
-                                                DEVICE_STATE_ACTIVE, &devs);
-    if (FAILED(hr))
-    {
-        msg_Warn (obj, "cannot enumerate audio endpoints (error 0x%lx)", hr);
-        return;
-    }
-
-    UINT n;
-    hr = IMMDeviceCollection_GetCount(devs, &n);
-    if (FAILED(hr))
-    {
-        msg_Warn (obj, "cannot count audio endpoints (error 0x%lx)", hr);
-        n = 0;
-    }
-    else
-        msg_Dbg(obj, "Available Windows Audio devices:");
-
-    while (n > 0)
-    {
-        IMMDevice *dev;
-
-        hr = IMMDeviceCollection_Item(devs, --n, &dev);
-        if (FAILED(hr))
-            continue;
-
-        /* Unique device ID */
-        LPWSTR devid;
-        hr = IMMDevice_GetId(dev, &devid);
-        if (FAILED(hr))
-        {
-            IMMDevice_Release(dev);
-            continue;
-        }
-        val.psz_string = FromWide(devid);
-        CoTaskMemFree(devid);
-        text.psz_string = val.psz_string;
-
-        /* 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))
-                text.psz_string = FromWide(v.pwszVal);
-            PropVariantClear(&v);
-            IPropertyStore_Release(props);
-        }
-        IMMDevice_Release(dev);
-
-        msg_Dbg(obj, "%s (%s)", val.psz_string, text.psz_string);
-        var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
-        if (likely(text.psz_string != val.psz_string))
-            free(text.psz_string);
-        free(val.psz_string);
-    }
-    IMMDeviceCollection_Release(devs);
-}
-
+#if !VLC_WINSTORE_APP
 /*** Audio session events ***/
 static inline aout_sys_t *vlc_AudioSessionEvents_sys(IAudioSessionEvents *this)
 {
@@ -435,7 +353,7 @@ vlc_AudioSessionEvents_OnSessionDisconnected(IAudioSessionEvents *this,
             msg_Warn(aout, "session disconnected: unknown reason %d", reason);
             return S_OK;
     }
-    var_TriggerCallback(aout, "audio-device");
+    /* NOTE: audio decoder thread should get invalidated device and restart */
     return S_OK;
 }
 
@@ -559,16 +477,85 @@ static void *MMThread(void *data)
     return NULL;
 }
 
+/*** Audio devices ***/
+static int DevicesEnum(audio_output_t *aout)
+{
+    aout_sys_t *sys = aout->sys;
+    HRESULT hr;
+    IMMDeviceCollection *devs;
+
+    hr = IMMDeviceEnumerator_EnumAudioEndpoints(sys->it, eRender,
+                                                DEVICE_STATE_ACTIVE, &devs);
+    if (FAILED(hr))
+    {
+        msg_Warn(aout, "cannot enumerate audio endpoints (error 0x%lx)", hr);
+        return -1;
+    }
+
+    UINT count;
+    hr = IMMDeviceCollection_GetCount(devs, &count);
+    if (FAILED(hr))
+    {
+        msg_Warn(aout, "cannot count audio endpoints (error 0x%lx)", hr);
+        count = 0;
+    }
+
+    unsigned n = 0;
+
+    for (UINT i = 0; i < count; i++)
+    {
+        IMMDevice *dev;
+        char *id, *name = NULL;
+
+        hr = IMMDeviceCollection_Item(devs, i, &dev);
+        if (FAILED(hr))
+            continue;
+
+        /* Unique device ID */
+        LPWSTR devid;
+        hr = IMMDevice_GetId(dev, &devid);
+        if (FAILED(hr))
+        {
+            IMMDevice_Release(dev);
+            continue;
+        }
+        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);
+        }
+        IMMDevice_Release(dev);
+
+        aout_HotplugReport(aout, id, (name != NULL) ? name : id);
+        free(name);
+        free(id);
+        n++;
+    }
+    IMMDeviceCollection_Release(devs);
+    return n;
+}
+
 /**
  * Opens the selected audio output device.
  */
 static HRESULT OpenDevice(audio_output_t *aout, const char *devid)
 {
     aout_sys_t *sys = aout->sys;
-    HRESULT hr;
-
     assert(sys->dev == NULL);
 
+    HRESULT hr;
     if (devid != NULL) /* Device selected explicitly */
     {
         msg_Dbg(aout, "using selected device %s", devid);
@@ -588,21 +575,36 @@ static HRESULT OpenDevice(audio_output_t *aout, const char *devid)
         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(sys->it, eRender,
                                                          eConsole, &sys->dev);
     }
+    assert(sys->manager == NULL);
+    if (FAILED(hr))
+    {
+        msg_Err(aout, "cannot get device (error 0x%lx)", hr);
+        goto out;
+    }
 
     /* Create session manager (for controls even w/o active audio client) */
+    void *pv;
+    hr = IMMDevice_Activate(sys->dev, &IID_IAudioSessionManager,
+                            CLSCTX_ALL, NULL, &pv);
     if (FAILED(hr))
-        msg_Err(aout, "cannot get device (error 0x%lx)", hr);
+        msg_Err(aout, "cannot activate session manager (error 0x%lx)", hr);
     else
+        sys->manager = pv;
+
+    /* Report actual device */
+    LPWSTR wdevid;
+    hr = IMMDevice_GetId(sys->dev, &wdevid);
+    if (SUCCEEDED(hr))
     {
-        void *pv;
-        hr = IMMDevice_Activate(sys->dev, &IID_IAudioSessionManager,
-                                CLSCTX_ALL, NULL, &pv);
-        if (FAILED(hr))
-            msg_Err(aout, "cannot activate session manager (error 0x%lx)", hr);
-        else
-            sys->manager = pv;
+        char *id = FromWide(wdevid);
+        CoTaskMemFree(wdevid);
+        if (likely(id != NULL))
+        {
+            aout_DeviceReport(aout, id);
+            free(id);
+        }
     }
-
+out:
     SetEvent(sys->device_changed);
     WaitForSingleObject(sys->device_ready, INFINITE);
     return hr;
@@ -616,7 +618,6 @@ static void CloseDevice(audio_output_t *aout)
     aout_sys_t *sys = aout->sys;
 
     assert(sys->dev != NULL);
-
     if (sys->manager != NULL)
     {
         IAudioSessionManager_Release(sys->manager);
@@ -635,17 +636,72 @@ static HRESULT ActivateDevice(void *opaque, REFIID iid, PROPVARIANT *actparms,
                               void **restrict pv)
 {
     IMMDevice *dev = opaque;
-
     return IMMDevice_Activate(dev, iid, CLSCTX_ALL, actparms, pv);
 }
 
+#else /* VLC_WINSTORE_APP */
+
+static HRESULT OpenDevice(audio_output_t *aout, const char *devid)
+{
+    aout_sys_t *sys = aout->sys;
+    assert(sys->dev == NULL);
+
+    (void)devid;
+    assert(!devid);
+    sys->dev = var_InheritAddress(aout, "mmdevice-audioclient");
+    return S_OK;
+}
+
+static void CloseDevice(audio_output_t *aout)
+{
+    aout_sys_t *sys = aout->sys;
+
+    assert(sys->dev != NULL);
+    free(sys->dev);
+    sys->dev = NULL;
+}
+
+static HRESULT ActivateDevice(void *opaque, REFIID iid, PROPVARIANT *actparms,
+                              void **restrict pv)
+{
+    IMMDevice *dev = opaque;
+    (void)iid; (void)actparms;
+    *pv = dev;
+    return S_OK;
+}
+#endif /* !VLC_WINSTORE_APP */
+
+
+static int DeviceSelect(audio_output_t *aout, const char *id)
+{
+    aout_sys_t *sys = aout->sys;
+    HRESULT hr;
+
+    if (TryEnterMTA(aout))
+        return -1;
+
+    if (sys->dev != NULL)
+        CloseDevice(aout);
+
+    hr = OpenDevice(aout, id);
+    while (hr == AUDCLNT_E_DEVICE_INVALIDATED)
+        hr = OpenDevice(aout, NULL); /* Fallback to default device */
+    LeaveMTA();
+
+    if (sys->stream != NULL)
+        /* Request restart of stream with the new device */
+        aout_RestartRequest(aout, AOUT_RESTART_OUTPUT);
+    return FAILED(hr) ? -1 : 0;
+}
+
 static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
 {
     aout_sys_t *sys = aout->sys;
     HRESULT hr;
 
     assert (sys->stream == NULL);
-    if (sys->dev == NULL)
+    /* Open the default device if required (to deal with restarts) */
+    if (sys->dev == NULL && FAILED(DeviceSelect(aout, NULL)))
         return -1;
 
     aout_stream_t *s = vlc_object_create(aout, sizeof (*s));
@@ -683,8 +739,6 @@ static void Stop(audio_output_t *aout)
 static int Open(vlc_object_t *obj)
 {
     audio_output_t *aout = (audio_output_t *)obj;
-    void *pv;
-    HRESULT hr;
 
     if (!aout->b_force && var_InheritBool(aout, "spdif"))
         /* Fallback to other plugin until pass-through is implemented */
@@ -697,8 +751,9 @@ static int Open(vlc_object_t *obj)
     aout->sys = sys;
     sys->aout = aout;
     sys->stream = NULL;
-    sys->it = NULL;
     sys->dev = NULL;
+#if !VLC_WINSTORE_APP
+    sys->it = NULL;
     sys->manager = NULL;
     sys->session_events.lpVtbl = &vlc_AudioSessionEvents;
     sys->refs = 1;
@@ -712,7 +767,8 @@ static int Open(vlc_object_t *obj)
     if (TryEnterMTA(aout))
         goto error;
 
-    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
+    void *pv;
+    HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
                           &IID_IMMDeviceEnumerator, &pv);
     if (FAILED(hr))
     {
@@ -721,16 +777,12 @@ static int Open(vlc_object_t *obj)
         goto error;
     }
     sys->it = pv;
-    GetDevices(obj, sys->it);
-
     if (vlc_clone(&sys->thread, MMThread, aout, VLC_THREAD_PRIORITY_LOW))
         goto error;
     WaitForSingleObject(sys->device_ready, INFINITE);
+#endif
 
-    /* Get a device to start with */
-    do
-        hr = OpenDevice(aout, NULL);
-    while (hr == AUDCLNT_E_DEVICE_INVALIDATED);
+    DeviceSelect(aout, NULL); /* Get a device to start with */
     LeaveMTA(); /* leave MTA after thread has entered MTA */
 
     aout->start = Start;
@@ -741,7 +793,12 @@ static int Open(vlc_object_t *obj)
     aout->flush = Flush;
     aout->volume_set = VolumeSet;
     aout->mute_set = MuteSet;
-    var_AddCallback (aout, "audio-device", DeviceChanged, NULL);
+#if VLC_WINSTORE_APP
+    aout->device_select = NULL;
+    return VLC_SUCCESS;
+#else
+    aout->device_select = DeviceSelect;
+    DevicesEnum(aout);
     return VLC_SUCCESS;
 
 error:
@@ -756,6 +813,7 @@ error:
         CloseHandle(sys->device_changed);
     free(sys);
     return VLC_EGENERIC;
+#endif
 }
 
 static void Close(vlc_object_t *obj)
@@ -763,9 +821,7 @@ static void Close(vlc_object_t *obj)
     audio_output_t *aout = (audio_output_t *)obj;
     aout_sys_t *sys = aout->sys;
 
-    var_DelCallback (aout, "audio-device", DeviceChanged, NULL);
-    var_Destroy (aout, "audio-device");
-
+#if !VLC_WINSTORE_APP
     EnterMTA(); /* enter MTA before thread leaves MTA */
     if (sys->dev != NULL)
         CloseDevice(aout);
@@ -775,17 +831,26 @@ static void Close(vlc_object_t *obj)
 
     SetEvent(sys->device_changed);
     vlc_join(sys->thread, NULL);
+
     LeaveMTA();
 
     CloseHandle(sys->device_ready);
     CloseHandle(sys->device_changed);
+#else
+    if (sys->dev != NULL)
+        CloseDevice(aout);
+#endif
     free(sys);
 }
 
 vlc_module_begin()
     set_shortname("MMDevice")
     set_description(N_("Windows Multimedia Device output"))
-    set_capability("audio output", 150)
+    set_capability("audio output", /*150*/0)
+#if VLC_WINSTORE_APP
+    /* Pointer to the activated AudioClient* */
+    add_integer("mmdevice-audioclient", 0x0, NULL, NULL, true);
+#endif
     set_category(CAT_AUDIO)
     set_subcategory(SUBCAT_AUDIO_AOUT)
     add_shortcut("wasapi")