misc: medialibrary: ctx does not need dynamic lifetime
[vlc.git] / modules / audio_output / wasapi.c
blob6a992799d7558cff4069d6ac4cf7e88ad45017b7
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 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #define INITGUID
26 #define COBJMACROS
27 #define CONST_VTABLE
28 #define NONEWWAVE
30 #include <stdlib.h>
31 #include <assert.h>
33 #include <vlc_common.h>
34 #include <vlc_codecs.h>
35 #include <vlc_aout.h>
36 #include <vlc_plugin.h>
38 #include <audioclient.h>
39 #include "audio_output/mmdevice.h"
41 /* 00000092-0000-0010-8000-00aa00389b71 */
42 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL,
43 WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00,
44 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
46 /* 00000001-0000-0010-8000-00aa00389b71 */
47 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX,
48 WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00,
49 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
51 /* 00000008-0000-0010-8000-00aa00389b71 */
52 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DTS,
53 WAVE_FORMAT_DTS_MS, 0x0000, 0x0010, 0x80, 0x00,
54 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
56 /* 0000000b-0cea-0010-8000-00aa00389b71 */
57 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD,
58 0x000b, 0x0cea, 0x0010, 0x80, 0x00,
59 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
61 /* 0000000a-0cea-0010-8000-00aa00389b71 */
62 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS,
63 0x000a, 0x0cea, 0x0010, 0x80, 0x00,
64 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
66 /* 0000000c-0cea-0010-8000-00aa00389b71 */
67 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP,
68 0x000c, 0x0cea, 0x0010, 0x80, 0x00,
69 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
71 static BOOL CALLBACK InitFreq(INIT_ONCE *once, void *param, void **context)
73 (void) once; (void) context;
74 return QueryPerformanceFrequency(param);
77 static LARGE_INTEGER freq; /* performance counters frequency */
79 static msftime_t GetQPC(void)
81 LARGE_INTEGER counter;
83 if (!QueryPerformanceCounter(&counter))
84 abort();
86 lldiv_t d = lldiv(counter.QuadPart, freq.QuadPart);
87 return (d.quot * 10000000) + ((d.rem * 10000000) / freq.QuadPart);
90 typedef struct aout_stream_sys
92 IAudioClient *client;
94 uint8_t chans_table[AOUT_CHAN_MAX];
95 uint8_t chans_to_reorder;
97 vlc_fourcc_t format; /**< Sample format */
98 unsigned rate; /**< Sample rate */
99 unsigned block_align;
100 UINT64 written; /**< Frames written to the buffer */
101 UINT32 frames; /**< Total buffer size (frames) */
102 } aout_stream_sys_t;
105 /*** VLC audio output callbacks ***/
106 static HRESULT TimeGet(aout_stream_t *s, vlc_tick_t *restrict delay)
108 aout_stream_sys_t *sys = s->sys;
109 void *pv;
110 UINT64 pos, qpcpos, freq;
111 HRESULT hr;
113 hr = IAudioClient_GetService(sys->client, &IID_IAudioClock, &pv);
114 if (FAILED(hr))
116 msg_Err(s, "cannot get clock (error 0x%lx)", hr);
117 return hr;
120 IAudioClock *clock = pv;
122 hr = IAudioClock_GetPosition(clock, &pos, &qpcpos);
123 if (SUCCEEDED(hr))
124 hr = IAudioClock_GetFrequency(clock, &freq);
125 IAudioClock_Release(clock);
126 if (FAILED(hr))
128 msg_Err(s, "cannot get position (error 0x%lx)", hr);
129 return hr;
132 lldiv_t w = lldiv(sys->written, sys->rate);
133 lldiv_t r = lldiv(pos, freq);
135 static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
137 *delay = vlc_tick_from_sec(w.quot - r.quot)
138 + (vlc_tick_from_sec(w.rem) / sys->rate)
139 - (vlc_tick_from_sec(r.rem) / freq)
140 - VLC_TICK_FROM_MSFTIME(GetQPC() - qpcpos);
142 return hr;
145 static HRESULT Play(aout_stream_t *s, block_t *block)
147 aout_stream_sys_t *sys = s->sys;
148 void *pv;
149 HRESULT hr;
151 if (sys->chans_to_reorder)
152 aout_ChannelReorder(block->p_buffer, block->i_buffer,
153 sys->chans_to_reorder, sys->chans_table, sys->format);
155 hr = IAudioClient_GetService(sys->client, &IID_IAudioRenderClient, &pv);
156 if (FAILED(hr))
158 msg_Err(s, "cannot get render client (error 0x%lx)", hr);
159 goto out;
162 IAudioRenderClient *render = pv;
163 for (;;)
165 UINT32 frames;
166 hr = IAudioClient_GetCurrentPadding(sys->client, &frames);
167 if (FAILED(hr))
169 msg_Err(s, "cannot get current padding (error 0x%lx)", hr);
170 break;
173 assert(frames <= sys->frames);
174 frames = sys->frames - frames;
175 if (frames > block->i_nb_samples)
176 frames = block->i_nb_samples;
178 BYTE *dst;
179 hr = IAudioRenderClient_GetBuffer(render, frames, &dst);
180 if (FAILED(hr))
182 msg_Err(s, "cannot get buffer (error 0x%lx)", hr);
183 break;
186 const size_t copy = frames * sys->block_align;
188 memcpy(dst, block->p_buffer, copy);
189 hr = IAudioRenderClient_ReleaseBuffer(render, frames, 0);
190 if (FAILED(hr))
192 msg_Err(s, "cannot release buffer (error 0x%lx)", hr);
193 break;
195 IAudioClient_Start(sys->client);
197 block->p_buffer += copy;
198 block->i_buffer -= copy;
199 block->i_nb_samples -= frames;
200 sys->written += frames;
201 if (block->i_nb_samples == 0)
202 break; /* done */
204 /* Out of buffer space, sleep */
205 vlc_tick_sleep(sys->frames * VLC_TICK_FROM_MS(500) / sys->rate);
207 IAudioRenderClient_Release(render);
208 out:
209 block_Release(block);
211 return hr;
214 static HRESULT Pause(aout_stream_t *s, bool paused)
216 aout_stream_sys_t *sys = s->sys;
217 HRESULT hr;
219 if (paused)
220 hr = IAudioClient_Stop(sys->client);
221 else
222 hr = IAudioClient_Start(sys->client);
223 if (FAILED(hr))
224 msg_Warn(s, "cannot %s stream (error 0x%lx)",
225 paused ? "stop" : "start", hr);
226 return hr;
229 static HRESULT Flush(aout_stream_t *s)
231 aout_stream_sys_t *sys = s->sys;
232 HRESULT hr;
234 IAudioClient_Stop(sys->client);
236 hr = IAudioClient_Reset(sys->client);
237 if (SUCCEEDED(hr))
239 msg_Dbg(s, "reset");
240 sys->written = 0;
242 else
243 msg_Warn(s, "cannot reset stream (error 0x%lx)", hr);
244 return hr;
248 /*** Initialization / deinitialization **/
249 static const uint32_t chans_out[] = {
250 SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
251 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
252 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER,
253 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0
255 static const uint32_t chans_in[] = {
256 SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
257 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
258 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER,
259 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0
262 static void vlc_HdmiToWave(WAVEFORMATEXTENSIBLE_IEC61937 *restrict wf_iec61937,
263 audio_sample_format_t *restrict audio)
265 WAVEFORMATEXTENSIBLE *wf = &wf_iec61937->FormatExt;
267 switch (audio->i_format)
269 case VLC_CODEC_DTS:
270 wf->SubFormat = _KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD;
271 wf->Format.nChannels = 8;
272 wf->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
273 audio->i_rate = 768000;
274 break;
275 case VLC_CODEC_EAC3:
276 wf->SubFormat = _KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS;
277 wf->Format.nChannels = 2;
278 wf->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
279 break;
280 case VLC_CODEC_TRUEHD:
281 case VLC_CODEC_MLP:
282 wf->SubFormat = _KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP;
283 wf->Format.nChannels = 8;
284 wf->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
285 audio->i_rate = 768000;
286 break;
287 default:
288 vlc_assert_unreachable();
290 wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
291 wf->Format.nSamplesPerSec = 192000;
292 wf->Format.wBitsPerSample = 16;
293 wf->Format.nBlockAlign = wf->Format.wBitsPerSample / 8 * wf->Format.nChannels;
294 wf->Format.nAvgBytesPerSec = wf->Format.nSamplesPerSec * wf->Format.nBlockAlign;
295 wf->Format.cbSize = sizeof (*wf_iec61937) - sizeof (wf->Format);
297 wf->Samples.wValidBitsPerSample = wf->Format.wBitsPerSample;
299 wf_iec61937->dwEncodedSamplesPerSec = audio->i_rate;
300 wf_iec61937->dwEncodedChannelCount = audio->i_channels;
301 wf_iec61937->dwAverageBytesPerSec = 0;
303 audio->i_format = VLC_CODEC_SPDIFL;
304 audio->i_bytes_per_frame = wf->Format.nBlockAlign;
305 audio->i_frame_length = 1;
308 static void vlc_SpdifToWave(WAVEFORMATEXTENSIBLE *restrict wf,
309 audio_sample_format_t *restrict audio)
311 switch (audio->i_format)
313 case VLC_CODEC_DTS:
314 if (audio->i_rate < 48000)
316 /* Wasapi doesn't accept DTS @ 44.1kHz but accept IEC 60958 PCM */
317 wf->SubFormat = _KSDATAFORMAT_SUBTYPE_WAVEFORMATEX;
319 else
320 wf->SubFormat = _KSDATAFORMAT_SUBTYPE_IEC61937_DTS;
321 break;
322 case VLC_CODEC_SPDIFL:
323 case VLC_CODEC_SPDIFB:
324 case VLC_CODEC_A52:
325 wf->SubFormat = _KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL;
326 break;
327 default:
328 vlc_assert_unreachable();
331 wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
332 wf->Format.nChannels = 2; /* To prevent channel re-ordering */
333 wf->Format.nSamplesPerSec = audio->i_rate;
334 wf->Format.wBitsPerSample = 16;
335 wf->Format.nBlockAlign = 4; /* wf->Format.wBitsPerSample / 8 * wf->Format.nChannels */
336 wf->Format.nAvgBytesPerSec = wf->Format.nSamplesPerSec * wf->Format.nBlockAlign;
337 wf->Format.cbSize = sizeof (*wf) - sizeof (wf->Format);
339 wf->Samples.wValidBitsPerSample = wf->Format.wBitsPerSample;
341 wf->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
343 audio->i_format = VLC_CODEC_SPDIFL;
344 audio->i_bytes_per_frame = wf->Format.nBlockAlign;
345 audio->i_frame_length = 1;
348 static void vlc_ToWave(WAVEFORMATEXTENSIBLE *restrict wf,
349 audio_sample_format_t *restrict audio)
351 switch (audio->i_format)
353 case VLC_CODEC_FL64:
354 audio->i_format = VLC_CODEC_FL32;
355 case VLC_CODEC_FL32:
356 wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
357 break;
359 case VLC_CODEC_U8:
360 audio->i_format = VLC_CODEC_S16N;
361 case VLC_CODEC_S16N:
362 wf->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
363 break;
365 default:
366 audio->i_format = VLC_CODEC_FL32;
367 wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
368 break;
370 aout_FormatPrepare (audio);
372 wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
373 wf->Format.nChannels = audio->i_channels;
374 wf->Format.nSamplesPerSec = audio->i_rate;
375 wf->Format.nAvgBytesPerSec = audio->i_bytes_per_frame * audio->i_rate;
376 wf->Format.nBlockAlign = audio->i_bytes_per_frame;
377 wf->Format.wBitsPerSample = audio->i_bitspersample;
378 wf->Format.cbSize = sizeof (*wf) - sizeof (wf->Format);
380 wf->Samples.wValidBitsPerSample = audio->i_bitspersample;
382 wf->dwChannelMask = 0;
383 for (unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++)
384 if (audio->i_physical_channels & pi_vlc_chan_order_wg4[i])
385 wf->dwChannelMask |= chans_in[i];
388 static int vlc_FromWave(const WAVEFORMATEX *restrict wf,
389 audio_sample_format_t *restrict audio)
391 audio->i_rate = wf->nSamplesPerSec;
392 audio->i_physical_channels = 0;
394 if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
396 const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
398 if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
400 switch (wf->wBitsPerSample)
402 case 64:
403 audio->i_format = VLC_CODEC_FL64;
404 break;
405 case 32:
406 audio->i_format = VLC_CODEC_FL32;
407 break;
408 default:
409 return -1;
412 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
414 switch (wf->wBitsPerSample)
416 case 32:
417 audio->i_format = VLC_CODEC_S32N;
418 break;
419 case 16:
420 audio->i_format = VLC_CODEC_S16N;
421 break;
422 default:
423 return -1;
427 if (wfe->Samples.wValidBitsPerSample != wf->wBitsPerSample)
428 return -1;
430 for (unsigned i = 0; chans_in[i]; i++)
431 if (wfe->dwChannelMask & chans_in[i])
432 audio->i_physical_channels |= pi_vlc_chan_order_wg4[i];
434 else
435 return -1;
437 aout_FormatPrepare (audio);
439 if (wf->nChannels != audio->i_channels)
440 return -1;
441 return 0;
444 static unsigned vlc_CheckWaveOrder (const WAVEFORMATEX *restrict wf,
445 uint8_t *restrict table)
447 uint32_t mask = 0;
449 if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
451 const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
453 mask = wfe->dwChannelMask;
455 return aout_CheckChannelReorder(chans_in, chans_out, mask, table);
459 static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
460 const GUID *sid)
462 static INIT_ONCE freq_once = INIT_ONCE_STATIC_INIT;
464 if (!InitOnceExecuteOnce(&freq_once, InitFreq, &freq, NULL))
465 return E_FAIL;
467 aout_stream_sys_t *sys = malloc(sizeof (*sys));
468 if (unlikely(sys == NULL))
469 return E_OUTOFMEMORY;
470 sys->client = NULL;
472 /* Configure audio stream */
473 WAVEFORMATEXTENSIBLE_IEC61937 wf_iec61937;
474 WAVEFORMATEXTENSIBLE *pwfe = &wf_iec61937.FormatExt;
475 WAVEFORMATEX *pwf = &pwfe->Format, *pwf_closest, *pwf_mix = NULL;
476 AUDCLNT_SHAREMODE shared_mode;
477 REFERENCE_TIME buffer_duration;
478 audio_sample_format_t fmt = *pfmt;
479 bool b_spdif = AOUT_FMT_SPDIF(&fmt);
480 bool b_hdmi = AOUT_FMT_HDMI(&fmt);
481 bool b_dtshd = false;
483 if (fmt.i_format == VLC_CODEC_DTS)
485 b_dtshd = var_GetBool(s->obj.parent, "dtshd");
486 if (b_dtshd)
488 b_hdmi = true;
489 b_spdif = false;
493 void *pv;
494 HRESULT hr = aout_stream_Activate(s, &IID_IAudioClient, NULL, &pv);
495 if (FAILED(hr))
497 msg_Err(s, "cannot activate client (error 0x%lx)", hr);
498 goto error;
500 sys->client = pv;
502 if (b_spdif)
504 vlc_SpdifToWave(pwfe, &fmt);
505 shared_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
506 /* The max buffer duration in exclusive mode is 200ms */
507 buffer_duration = MSFTIME_FROM_MS(200);
509 else if (b_hdmi)
511 vlc_HdmiToWave(&wf_iec61937, &fmt);
512 shared_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
513 /* The max buffer duration in exclusive mode is 200ms */
514 buffer_duration = MSFTIME_FROM_MS(200);
516 else if (AOUT_FMT_LINEAR(&fmt))
518 shared_mode = AUDCLNT_SHAREMODE_SHARED;
520 if (fmt.channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
522 fmt.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
524 /* Render Ambisonics on the native mix format */
525 hr = IAudioClient_GetMixFormat(sys->client, &pwf_mix);
526 if (FAILED(hr) || vlc_FromWave(pwf_mix, &fmt))
527 vlc_ToWave(pwfe, &fmt); /* failed, fallback to default */
528 else
529 pwf = pwf_mix;
531 /* Setup low latency in order to quickly react to ambisonics filters
532 * viewpoint changes. */
533 buffer_duration = MSFTIME_FROM_MS(200);
535 else
537 vlc_ToWave(pwfe, &fmt);
538 buffer_duration = MSFTIME_FROM_VLC_TICK(AOUT_MAX_PREPARE_TIME);
541 else
543 hr = E_FAIL;
544 goto error;
547 hr = IAudioClient_IsFormatSupported(sys->client, shared_mode,
548 pwf, &pwf_closest);
550 if (FAILED(hr))
552 if (pfmt->i_format == VLC_CODEC_DTS && b_hdmi)
554 msg_Warn(s, "cannot negotiate DTS at 768khz IEC958 rate (HDMI), "
555 "fallback to 48kHz (S/PDIF) (error 0x%lx)", hr);
556 IAudioClient_Release(sys->client);
557 free(sys);
558 var_SetBool(s->obj.parent, "dtshd", false);
559 return Start(s, pfmt, sid);
561 msg_Err(s, "cannot negotiate audio format (error 0x%lx)%s", hr,
562 hr == AUDCLNT_E_UNSUPPORTED_FORMAT
563 && fmt.i_format == VLC_CODEC_SPDIFL ?
564 ": digital pass-through not supported" : "");
565 goto error;
568 if (hr == S_FALSE)
570 assert(pwf_closest != NULL);
571 if (vlc_FromWave(pwf_closest, &fmt))
573 CoTaskMemFree(pwf_closest);
574 msg_Err(s, "unsupported audio format");
575 hr = E_INVALIDARG;
576 goto error;
578 shared_mode = AUDCLNT_SHAREMODE_SHARED;
579 msg_Dbg(s, "modified format");
580 pwf = pwf_closest;
582 else
583 assert(pwf_closest == NULL);
585 sys->chans_to_reorder = fmt.i_format != VLC_CODEC_SPDIFL ?
586 vlc_CheckWaveOrder(pwf, sys->chans_table) : 0;
587 sys->format = fmt.i_format;
588 sys->block_align = pwf->nBlockAlign;
589 sys->rate = pwf->nSamplesPerSec;
591 hr = IAudioClient_Initialize(sys->client, shared_mode, 0, buffer_duration,
592 0, pwf, sid);
593 CoTaskMemFree(pwf_closest);
594 if (FAILED(hr))
596 msg_Err(s, "cannot initialize audio client (error 0x%lx)", hr);
597 goto error;
600 hr = IAudioClient_GetBufferSize(sys->client, &sys->frames);
601 if (FAILED(hr))
603 msg_Err(s, "cannot get buffer size (error 0x%lx)", hr);
604 goto error;
606 msg_Dbg(s, "buffer size : %"PRIu32" frames", sys->frames);
608 REFERENCE_TIME latT, defT, minT;
609 if (SUCCEEDED(IAudioClient_GetStreamLatency(sys->client, &latT))
610 && SUCCEEDED(IAudioClient_GetDevicePeriod(sys->client, &defT, &minT)))
612 msg_Dbg(s, "maximum latency: %"PRIu64"00 ns", latT);
613 msg_Dbg(s, "default period : %"PRIu64"00 ns", defT);
614 msg_Dbg(s, "minimum period : %"PRIu64"00 ns", minT);
617 CoTaskMemFree(pwf_mix);
618 *pfmt = fmt;
619 sys->written = 0;
620 s->sys = sys;
621 s->time_get = TimeGet;
622 s->play = Play;
623 s->pause = Pause;
624 s->flush = Flush;
625 return S_OK;
626 error:
627 CoTaskMemFree(pwf_mix);
628 if (sys->client != NULL)
629 IAudioClient_Release(sys->client);
630 free(sys);
631 return hr;
634 static void Stop(aout_stream_t *s)
636 aout_stream_sys_t *sys = s->sys;
638 IAudioClient_Stop(sys->client); /* should not be needed */
639 IAudioClient_Release(sys->client);
641 free(sys);
644 vlc_module_begin()
645 set_shortname("WASAPI")
646 set_description(N_("Windows Audio Session API output"))
647 set_capability("aout stream", 50)
648 set_category(CAT_AUDIO)
649 set_subcategory(SUBCAT_AUDIO_AOUT)
650 set_callbacks(Start, Stop)
651 vlc_module_end()