1 /*****************************************************************************
2 * wasapi.c : Windows Audio Session API output plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2012 RĂ©mi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
31 #include <audioclient.h>
32 #include <audiopolicy.h>
33 #include <mmdeviceapi.h>
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
38 #include <vlc_charset.h>
40 DEFINE_GUID (GUID_VLC_AUD_OUT, 0x4533f59d, 0x59ee, 0x00c6,
41 0xad, 0xb2, 0xc6, 0x8b, 0x50, 0x1a, 0x66, 0x55);
43 DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd,
44 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
46 static int Open(vlc_object_t *);
47 static void Close(vlc_object_t *);
50 set_shortname("WASAPI")
51 set_description(N_("Windows Audio Session output") )
52 set_capability("audio output", 150)
53 set_category(CAT_AUDIO)
54 set_subcategory(SUBCAT_AUDIO_AOUT)
55 add_shortcut("was", "audioclient")
56 set_callbacks(Open, Close)
59 static LARGE_INTEGER freq; /* performance counters frequency */
61 BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID); /* avoid warning */
63 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved)
70 case DLL_PROCESS_ATTACH:
71 if (!QueryPerformanceFrequency(&freq))
78 static UINT64 GetQPC(void)
80 LARGE_INTEGER counter;
82 if (!QueryPerformanceCounter(&counter))
85 lldiv_t d = lldiv(counter.QuadPart, freq.QuadPart);
86 return (d.quot * 10000000) + ((d.rem * 10000000) / freq.QuadPart);
89 static int TryEnter(vlc_object_t *obj)
91 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
92 if (unlikely(FAILED(hr)))
94 msg_Err (obj, "cannot initialize COM (error 0x%lx)", hr);
99 #define TryEnter(o) TryEnter(VLC_OBJECT(o))
101 static void Enter(void)
103 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
104 if (unlikely(FAILED(hr)))
108 static void Leave(void)
115 audio_output_t *aout;
116 IMMDeviceEnumerator *it;
117 IAudioClient *client;
118 IAudioRenderClient *render;
121 IAudioSessionControl *control;
122 struct IAudioSessionEvents events;
125 uint8_t chans_table[AOUT_CHAN_MAX];
126 uint8_t chans_to_reorder;
127 uint8_t bits; /**< Bits per sample */
128 unsigned rate; /**< Sample rate */
129 unsigned bytes_per_frame;
130 UINT32 written; /**< Frames written to the buffer */
131 UINT32 frames; /**< Total buffer size (frames) */
133 float volume_hack; /**< Deferred volume request */
134 int mute_hack; /**< Deferred mute request */
136 HANDLE ready; /**< Semaphore from MTA thread */
137 HANDLE done; /**< Semaphore to MTA thread */
141 /*** VLC audio output callbacks ***/
142 static int TimeGet(audio_output_t *aout, mtime_t *restrict delay)
144 aout_sys_t *sys = aout->sys;
148 if (sys->clock == NULL)
152 hr = IAudioClock_GetPosition(sys->clock, &pos, &qpcpos);
156 msg_Err(aout, "cannot get position (error 0x%lx)", hr);
162 *delay = sys->written * CLOCK_FREQ / sys->rate;
163 msg_Dbg(aout, "extrapolating position: still propagating buffers");
167 *delay = ((GetQPC() - qpcpos) / (10000000 / CLOCK_FREQ));
168 static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
172 static void CheckVolumeHack(audio_output_t *aout)
174 aout_sys_t *sys = aout->sys;
176 if (unlikely(sys->volume_hack >= 0.f))
177 { /* Apply volume now, if it failed earlier */
178 aout->volume_set(aout, sys->volume_hack);
179 sys->volume_hack = -1.f;
181 if (unlikely(sys->mute_hack >= 0))
182 { /* Apply volume now, if it failed earlier */
183 aout->mute_set(aout, sys->mute_hack);
188 static void Play(audio_output_t *aout, block_t *block)
190 aout_sys_t *sys = aout->sys;
193 CheckVolumeHack(aout);
195 if (sys->chans_to_reorder)
196 aout_ChannelReorder(block->p_buffer, block->i_buffer,
197 sys->chans_to_reorder, sys->chans_table, sys->bits);
203 hr = IAudioClient_GetCurrentPadding(sys->client, &frames);
206 msg_Err(aout, "cannot get current padding (error 0x%lx)", hr);
210 assert(frames <= sys->frames);
211 frames = sys->frames - frames;
212 if (frames > block->i_nb_samples)
213 frames = block->i_nb_samples;
216 hr = IAudioRenderClient_GetBuffer(sys->render, frames, &dst);
219 msg_Err(aout, "cannot get buffer (error 0x%lx)", hr);
223 const size_t copy = frames * sys->bytes_per_frame;
225 memcpy(dst, block->p_buffer, copy);
226 hr = IAudioRenderClient_ReleaseBuffer(sys->render, frames, 0);
229 msg_Err(aout, "cannot release buffer (error 0x%lx)", hr);
232 IAudioClient_Start(sys->client);
234 block->p_buffer += copy;
235 block->i_buffer -= copy;
236 block->i_nb_samples -= frames;
237 sys->written += frames;
238 if (block->i_nb_samples == 0)
241 /* Out of buffer space, sleep */
242 msleep(AOUT_MIN_PREPARE_TIME
243 + block->i_nb_samples * CLOCK_FREQ / sys->rate);
247 block_Release(block);
249 /* Restart on unplug */
250 if (unlikely(hr == AUDCLNT_E_DEVICE_INVALIDATED))
251 var_TriggerCallback(aout, "audio-device");
254 static void Pause(audio_output_t *aout, bool paused, mtime_t date)
256 aout_sys_t *sys = aout->sys;
259 CheckVolumeHack(aout);
263 hr = IAudioClient_Stop(sys->client);
265 hr = IAudioClient_Start(sys->client);
267 msg_Warn(aout, "cannot %s stream (error 0x%lx)",
268 paused ? "stop" : "start", hr);
274 static void Flush(audio_output_t *aout, bool wait)
276 aout_sys_t *sys = aout->sys;
279 CheckVolumeHack(aout);
282 return; /* Drain not implemented */
285 IAudioClient_Stop(sys->client);
286 hr = IAudioClient_Reset(sys->client);
290 msg_Warn(aout, "cannot reset stream (error 0x%lx)", hr);
295 static int SimpleVolumeSet(audio_output_t *aout, float vol)
297 aout_sys_t *sys = aout->sys;
298 ISimpleAudioVolume *simple;
303 hr = IAudioClient_GetService(sys->client, &IID_ISimpleAudioVolume,
307 hr = ISimpleAudioVolume_SetMasterVolume(simple, vol, NULL);
308 ISimpleAudioVolume_Release(simple);
314 msg_Err(aout, "cannot set volume (error 0x%lx)", hr);
315 sys->volume_hack = vol;
318 sys->volume_hack = -1.f;
322 static int SimpleMuteSet(audio_output_t *aout, bool mute)
324 aout_sys_t *sys = aout->sys;
325 ISimpleAudioVolume *simple;
330 hr = IAudioClient_GetService(sys->client, &IID_ISimpleAudioVolume,
334 hr = ISimpleAudioVolume_SetMute(simple, mute, NULL);
335 ISimpleAudioVolume_Release(simple);
341 msg_Err(aout, "cannot set mute (error 0x%lx)", hr);
342 sys->mute_hack = mute;
350 /*** Audio devices ***/
351 static int DeviceChanged(vlc_object_t *obj, const char *varname,
352 vlc_value_t prev, vlc_value_t cur, void *data)
354 aout_ChannelsRestart(obj, varname, prev, cur, data);
356 if (!var_Type (obj, "wasapi-audio-device"))
357 var_Create (obj, "wasapi-audio-device", VLC_VAR_STRING);
358 var_SetString (obj, "wasapi-audio-device", cur.psz_string);
362 static void GetDevices(vlc_object_t *obj, IMMDeviceEnumerator *it)
365 vlc_value_t val, text;
367 var_Create (obj, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
368 text.psz_string = _("Audio Device");
369 var_Change (obj, "audio-device", VLC_VAR_SETTEXT, &text, NULL);
371 IMMDeviceCollection *devs;
372 hr = IMMDeviceEnumerator_EnumAudioEndpoints(it, eRender,
373 DEVICE_STATE_ACTIVE, &devs);
376 msg_Warn (obj, "cannot enumerate audio endpoints (error 0x%lx)", hr);
381 hr = IMMDeviceCollection_GetCount(devs, &n);
384 msg_Warn (obj, "cannot count audio endpoints (error 0x%lx)", hr);
388 msg_Dbg(obj, "Available Windows Audio devices:");
394 hr = IMMDeviceCollection_Item(devs, --n, &dev);
398 /* Unique device ID */
400 hr = IMMDevice_GetId(dev, &devid);
403 IMMDevice_Release(dev);
406 val.psz_string = FromWide(devid);
407 CoTaskMemFree(devid);
408 text.psz_string = val.psz_string;
410 /* User-readable device name */
411 IPropertyStore *props;
412 hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &props);
418 hr = IPropertyStore_GetValue(props, &PKEY_Device_FriendlyName, &v);
420 text.psz_string = FromWide(v.pwszVal);
421 PropVariantClear(&v);
422 IPropertyStore_Release(props);
424 IMMDevice_Release(dev);
426 msg_Dbg(obj, "%s (%s)", val.psz_string, text.psz_string);
427 var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
428 if (likely(text.psz_string != val.psz_string))
429 free(text.psz_string);
430 free(val.psz_string);
432 IMMDeviceCollection_Release(devs);
436 /*** Audio session events ***/
437 static inline aout_sys_t *vlc_AudioSessionEvents_sys(IAudioSessionEvents *this)
439 return (aout_sys_t *)(((char *)this) - offsetof(aout_sys_t, events));
443 vlc_AudioSessionEvents_QueryInterface(IAudioSessionEvents *this, REFIID riid,
446 if (IsEqualIID(riid, &IID_IUnknown)
447 || IsEqualIID(riid, &IID_IAudioSessionEvents))
450 IUnknown_AddRef(this);
456 return E_NOINTERFACE;
460 static STDMETHODIMP_(ULONG)
461 vlc_AudioSessionEvents_AddRef(IAudioSessionEvents *this)
463 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
464 return InterlockedIncrement(&sys->refs);
467 static STDMETHODIMP_(ULONG)
468 vlc_AudioSessionEvents_Release(IAudioSessionEvents *this)
470 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
471 return InterlockedDecrement(&sys->refs);
475 vlc_AudioSessionEvents_OnDisplayNameChanged(IAudioSessionEvents *this,
476 LPCWSTR wname, LPCGUID ctx)
478 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
479 audio_output_t *aout = sys->aout;
481 msg_Dbg(aout, "display name changed: %ls", wname);
487 vlc_AudioSessionEvents_OnIconPathChanged(IAudioSessionEvents *this,
488 LPCWSTR wpath, LPCGUID ctx)
490 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
491 audio_output_t *aout = sys->aout;
493 msg_Dbg(aout, "icon path changed: %ls", wpath);
499 vlc_AudioSessionEvents_OnSimpleVolumeChanged(IAudioSessionEvents *this, float vol,
500 WINBOOL mute, LPCGUID ctx)
502 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
503 audio_output_t *aout = sys->aout;
505 msg_Dbg(aout, "simple volume changed: %f, muting %sabled", vol,
506 mute ? "en" : "dis");
507 aout_VolumeReport(aout, vol);
508 aout_MuteReport(aout, mute == TRUE);
514 vlc_AudioSessionEvents_OnChannelVolumeChanged(IAudioSessionEvents *this,
515 DWORD count, float *vols,
516 DWORD changed, LPCGUID ctx)
518 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
519 audio_output_t *aout = sys->aout;
521 msg_Dbg(aout, "channel volume %lu of %lu changed: %f", changed, count,
528 vlc_AudioSessionEvents_OnGroupingParamChanged(IAudioSessionEvents *this,
529 LPCGUID param, LPCGUID ctx)
532 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
533 audio_output_t *aout = sys->aout;
535 msg_Dbg(aout, "grouping parameter changed");
542 vlc_AudioSessionEvents_OnStateChanged(IAudioSessionEvents *this,
543 AudioSessionState state)
545 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
546 audio_output_t *aout = sys->aout;
548 msg_Dbg(aout, "state changed: %d", state);
553 vlc_AudioSessionEvents_OnSessionDisconnected(IAudioSessionEvents *this,
554 AudioSessionDisconnectReason reason)
556 aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
557 audio_output_t *aout = sys->aout;
559 msg_Dbg(aout, "session disconnected: reason %d", reason);
563 static const struct IAudioSessionEventsVtbl vlc_AudioSessionEvents =
565 vlc_AudioSessionEvents_QueryInterface,
566 vlc_AudioSessionEvents_AddRef,
567 vlc_AudioSessionEvents_Release,
569 vlc_AudioSessionEvents_OnDisplayNameChanged,
570 vlc_AudioSessionEvents_OnIconPathChanged,
571 vlc_AudioSessionEvents_OnSimpleVolumeChanged,
572 vlc_AudioSessionEvents_OnChannelVolumeChanged,
573 vlc_AudioSessionEvents_OnGroupingParamChanged,
574 vlc_AudioSessionEvents_OnStateChanged,
575 vlc_AudioSessionEvents_OnSessionDisconnected,
579 /*** Initialization / deinitialization **/
580 static const uint32_t chans_out[] = {
581 SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
582 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
583 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER,
584 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0
586 static const uint32_t chans_in[] = {
587 SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
588 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
589 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER,
590 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0
593 static void vlc_ToWave(WAVEFORMATEXTENSIBLE *restrict wf,
594 audio_sample_format_t *restrict audio)
596 switch (audio->i_format)
599 audio->i_format = VLC_CODEC_FL32;
601 wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
606 audio->i_format = VLC_CODEC_S16N;
608 wf->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
612 audio->i_format = VLC_CODEC_FL32;
613 wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
616 aout_FormatPrepare (audio);
618 wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
619 wf->Format.nChannels = audio->i_channels;
620 wf->Format.nSamplesPerSec = audio->i_rate;
621 wf->Format.nAvgBytesPerSec = audio->i_bytes_per_frame * audio->i_rate;
622 wf->Format.nBlockAlign = audio->i_bytes_per_frame;
623 wf->Format.wBitsPerSample = audio->i_bitspersample;
624 wf->Format.cbSize = sizeof (*wf) - sizeof (wf->Format);
626 wf->Samples.wValidBitsPerSample = audio->i_bitspersample;
628 wf->dwChannelMask = 0;
629 for (unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++)
630 if (audio->i_physical_channels & pi_vlc_chan_order_wg4[i])
631 wf->dwChannelMask |= chans_in[i];
634 static int vlc_FromWave(const WAVEFORMATEX *restrict wf,
635 audio_sample_format_t *restrict audio)
637 audio->i_rate = wf->nSamplesPerSec;
639 if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
641 const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
643 audio->i_physical_channels = 0;
644 for (unsigned i = 0; chans_in[i]; i++)
645 if (wfe->dwChannelMask & chans_in[i])
646 audio->i_physical_channels |= pi_vlc_chan_order_wg4[i];
649 audio->i_original_channels = audio->i_physical_channels;
650 aout_FormatPrepare (audio);
652 if (wf->nChannels != audio->i_channels)
657 static wchar_t *var_InheritWide(vlc_object_t *obj, const char *name)
659 char *v8 = var_InheritString(obj, name);
663 wchar_t *v16 = ToWide(v8);
667 #define var_InheritWide(o,n) var_InheritWide(VLC_OBJECT(o),n)
669 static int var_SetWide(vlc_object_t *obj, const char *name, const wchar_t *val)
671 char *str = FromWide(val);
672 if (unlikely(str == NULL))
675 int ret = var_SetString(obj, name, str);
679 #define var_SetWide(o,n,v) var_SetWide(VLC_OBJECT(o),n,v)
681 /* Dummy thread to create and release COM interfaces when needed. */
682 static void MTAThread(void *data)
684 audio_output_t *aout = data;
685 aout_sys_t *sys = aout->sys;
690 hr = IAudioClient_GetService(sys->client, &IID_IAudioRenderClient,
691 (void **)&sys->render);
694 msg_Err(aout, "cannot get audio render service (error 0x%lx)", hr);
698 hr = IAudioClient_GetService(sys->client, &IID_IAudioClock,
699 (void **)&sys->clock);
701 msg_Warn(aout, "cannot get audio clock (error 0x%lx)", hr);
703 hr = IAudioClient_GetService(sys->client, &IID_IAudioSessionControl,
704 (void **)&sys->control);
706 msg_Warn(aout, "cannot get audio session control (error 0x%lx)", hr);
709 wchar_t *ua = var_InheritWide(aout, "user-agent");
710 IAudioSessionControl_SetDisplayName(sys->control, ua, NULL);
714 /* do nothing until the audio session terminates */
715 ReleaseSemaphore(sys->ready, 1, NULL);
716 WaitForSingleObject(sys->done, INFINITE);
718 if (sys->control != NULL)
719 IAudioSessionControl_Release(sys->control);
720 if (sys->clock != NULL)
721 IAudioClock_Release(sys->clock);
722 IAudioRenderClient_Release(sys->render);
725 ReleaseSemaphore(sys->ready, 1, NULL);
728 static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
730 aout_sys_t *sys = aout->sys;
736 sys->events.lpVtbl = &vlc_AudioSessionEvents;
743 /* Get audio device according to policy */
744 // Without configuration item, the variable must be created explicitly.
745 var_Create (aout, "wasapi-audio-device", VLC_VAR_STRING);
746 LPWSTR devid = var_InheritWide (aout, "wasapi-audio-device");
747 var_Destroy (aout, "wasapi-audio-device");
749 IMMDevice *dev = NULL;
752 msg_Dbg (aout, "using selected device %ls", devid);
753 hr = IMMDeviceEnumerator_GetDevice (sys->it, devid, &dev);
755 msg_Warn(aout, "cannot get audio endpoint (error 0x%lx)", hr);
760 msg_Dbg (aout, "using default device");
761 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(sys->it, eRender,
766 msg_Err(aout, "cannot get audio endpoint (error 0x%lx)", hr);
770 hr = IMMDevice_GetId(dev, &devid);
773 msg_Dbg(aout, "using device %ls", devid);
774 var_SetWide (aout, "audio-device", devid);
775 CoTaskMemFree(devid);
778 hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_ALL, NULL,
779 (void **)&sys->client);
780 IMMDevice_Release(dev);
783 msg_Err(aout, "cannot activate audio client (error 0x%lx)", hr);
787 /* Configure audio stream */
788 WAVEFORMATEXTENSIBLE wf;
791 vlc_ToWave(&wf, fmt);
792 hr = IAudioClient_IsFormatSupported(sys->client, AUDCLNT_SHAREMODE_SHARED,
796 msg_Err(aout, "cannot negotiate audio format (error 0x%lx)", hr);
803 if (vlc_FromWave(pwf, fmt))
806 msg_Err(aout, "unsupported audio format");
809 msg_Dbg(aout, "modified format");
814 sys->chans_to_reorder = aout_CheckChannelReorder(chans_in, chans_out,
815 fmt->i_physical_channels,
817 sys->bits = fmt->i_bitspersample;
819 hr = IAudioClient_Initialize(sys->client, AUDCLNT_SHAREMODE_SHARED, 0,
820 AOUT_MAX_PREPARE_TIME * 10, 0,
821 (hr == S_OK) ? &wf.Format : pwf,
826 msg_Err(aout, "cannot initialize audio client (error 0x%lx)", hr);
830 hr = IAudioClient_GetBufferSize(sys->client, &sys->frames);
833 msg_Err(aout, "cannot get buffer size (error 0x%lx)", hr);
837 sys->ready = CreateSemaphore(NULL, 0, 1, NULL);
838 sys->done = CreateSemaphore(NULL, 0, 1, NULL);
839 if (unlikely(sys->ready == NULL || sys->done == NULL))
841 /* Note: thread handle released by CRT, ignore it. */
842 if (_beginthread(MTAThread, 0, aout) == (uintptr_t)-1)
845 WaitForSingleObject(sys->ready, INFINITE);
846 if (sys->render == NULL)
851 sys->rate = fmt->i_rate;
852 sys->bytes_per_frame = fmt->i_bytes_per_frame;
854 aout->time_get = TimeGet;
858 if (likely(sys->control != NULL))
859 IAudioSessionControl_RegisterAudioSessionNotification(sys->control,
861 var_AddCallback (aout, "audio-device", DeviceChanged, NULL);
865 if (sys->done != NULL)
866 CloseHandle(sys->done);
867 if (sys->ready != NULL)
868 CloseHandle(sys->done);
869 if (sys->client != NULL)
870 IAudioClient_Release(sys->client);
871 if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
873 var_SetString(aout, "audio-device", "");
874 msg_Warn(aout, "device invalidated, retrying");
881 static void Stop(audio_output_t *aout)
883 aout_sys_t *sys = aout->sys;
886 if (likely(sys->control != NULL))
887 IAudioSessionControl_UnregisterAudioSessionNotification(sys->control,
889 ReleaseSemaphore(sys->done, 1, NULL); /* tell MTA thread to finish */
890 WaitForSingleObject(sys->ready, INFINITE); /* wait for that ^ */
891 IAudioClient_Stop(sys->client); /* should not be needed */
892 IAudioClient_Release(sys->client);
895 var_DelCallback (aout, "audio-device", DeviceChanged, NULL);
897 CloseHandle(sys->done);
898 CloseHandle(sys->ready);
901 static int Open(vlc_object_t *obj)
903 audio_output_t *aout = (audio_output_t *)obj;
907 if (!aout->b_force && var_InheritBool(aout, "spdif"))
908 /* Fallback to other plugin until pass-through is implemented */
911 aout_sys_t *sys = malloc(sizeof (*sys));
912 if (unlikely(sys == NULL))
919 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
920 &IID_IMMDeviceEnumerator, &pv);
923 msg_Dbg(aout, "cannot create device enumerator (error 0x%lx)", hr);
928 GetDevices(obj, sys->it);
931 sys->volume_hack = -1.f;
937 aout->volume_set = SimpleVolumeSet; /* FIXME */
938 aout->mute_set = SimpleMuteSet;
945 static void Close(vlc_object_t *obj)
947 audio_output_t *aout = (audio_output_t *)obj;
948 aout_sys_t *sys = aout->sys;
950 var_Destroy (aout, "audio-device");
953 IMMDeviceEnumerator_Release(sys->it);