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 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_VISTA
23 # define _WIN32_WINNT _WIN32_WINNT_VISTA
38 #include <vlc_common.h>
39 #include <vlc_codecs.h>
41 #include <vlc_plugin.h>
43 #include <audioclient.h>
44 #include "audio_output/mmdevice.h"
46 /* 00000092-0000-0010-8000-00aa00389b71 */
47 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL
,
48 WAVE_FORMAT_DOLBY_AC3_SPDIF
, 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 UINT64
GetQPC(void)
81 LARGE_INTEGER counter
;
83 if (!QueryPerformanceCounter(&counter
))
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
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 */
100 UINT64 written
; /**< Frames written to the buffer */
101 UINT32 frames
; /**< Total buffer size (frames) */
105 /*** VLC audio output callbacks ***/
106 static HRESULT
TimeGet(aout_stream_t
*s
, mtime_t
*restrict delay
)
108 aout_stream_sys_t
*sys
= s
->sys
;
110 UINT64 pos
, qpcpos
, freq
;
113 hr
= IAudioClient_GetService(sys
->client
, &IID_IAudioClock
, &pv
);
116 msg_Err(s
, "cannot get clock (error 0x%lx)", hr
);
120 IAudioClock
*clock
= pv
;
122 hr
= IAudioClock_GetPosition(clock
, &pos
, &qpcpos
);
124 hr
= IAudioClock_GetFrequency(clock
, &freq
);
125 IAudioClock_Release(clock
);
128 msg_Err(s
, "cannot get position (error 0x%lx)", 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
= ((w
.quot
- r
.quot
) * CLOCK_FREQ
)
138 + ((w
.rem
* CLOCK_FREQ
) / sys
->rate
)
139 - ((r
.rem
* CLOCK_FREQ
) / freq
)
140 - ((GetQPC() - qpcpos
) / (10000000 / CLOCK_FREQ
));
145 static HRESULT
Play(aout_stream_t
*s
, block_t
*block
)
147 aout_stream_sys_t
*sys
= s
->sys
;
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
);
158 msg_Err(s
, "cannot get render client (error 0x%lx)", hr
);
162 IAudioRenderClient
*render
= pv
;
166 hr
= IAudioClient_GetCurrentPadding(sys
->client
, &frames
);
169 msg_Err(s
, "cannot get current padding (error 0x%lx)", hr
);
173 assert(frames
<= sys
->frames
);
174 frames
= sys
->frames
- frames
;
175 if (frames
> block
->i_nb_samples
)
176 frames
= block
->i_nb_samples
;
179 hr
= IAudioRenderClient_GetBuffer(render
, frames
, &dst
);
182 msg_Err(s
, "cannot get buffer (error 0x%lx)", hr
);
186 const size_t copy
= frames
* sys
->block_align
;
188 memcpy(dst
, block
->p_buffer
, copy
);
189 hr
= IAudioRenderClient_ReleaseBuffer(render
, frames
, 0);
192 msg_Err(s
, "cannot release buffer (error 0x%lx)", hr
);
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)
204 /* Out of buffer space, sleep */
205 msleep(sys
->frames
* (CLOCK_FREQ
/ 2) / sys
->rate
);
207 IAudioRenderClient_Release(render
);
209 block_Release(block
);
214 static HRESULT
Pause(aout_stream_t
*s
, bool paused
)
216 aout_stream_sys_t
*sys
= s
->sys
;
220 hr
= IAudioClient_Stop(sys
->client
);
222 hr
= IAudioClient_Start(sys
->client
);
224 msg_Warn(s
, "cannot %s stream (error 0x%lx)",
225 paused
? "stop" : "start", hr
);
229 static HRESULT
Flush(aout_stream_t
*s
)
231 aout_stream_sys_t
*sys
= s
->sys
;
234 IAudioClient_Stop(sys
->client
);
236 hr
= IAudioClient_Reset(sys
->client
);
243 msg_Warn(s
, "cannot reset stream (error 0x%lx)", 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
)
270 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD
;
271 wf
->Format
.nChannels
= 8;
272 wf
->dwChannelMask
= KSAUDIO_SPEAKER_7POINT1
;
273 audio
->i_rate
= 768000;
276 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS
;
277 wf
->Format
.nChannels
= 2;
278 wf
->dwChannelMask
= KSAUDIO_SPEAKER_5POINT1
;
280 case VLC_CODEC_TRUEHD
:
282 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP
;
283 wf
->Format
.nChannels
= 8;
284 wf
->dwChannelMask
= KSAUDIO_SPEAKER_7POINT1
;
285 audio
->i_rate
= 768000;
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
)
314 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_IEC61937_DTS
;
316 case VLC_CODEC_SPDIFL
:
317 case VLC_CODEC_SPDIFB
:
319 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL
;
322 vlc_assert_unreachable();
325 wf
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
326 wf
->Format
.nChannels
= 2; /* To prevent channel re-ordering */
327 wf
->Format
.nSamplesPerSec
= audio
->i_rate
;
328 wf
->Format
.wBitsPerSample
= 16;
329 wf
->Format
.nBlockAlign
= 4; /* wf->Format.wBitsPerSample / 8 * wf->Format.nChannels */
330 wf
->Format
.nAvgBytesPerSec
= wf
->Format
.nSamplesPerSec
* wf
->Format
.nBlockAlign
;
331 wf
->Format
.cbSize
= sizeof (*wf
) - sizeof (wf
->Format
);
333 wf
->Samples
.wValidBitsPerSample
= wf
->Format
.wBitsPerSample
;
335 wf
->dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
;
337 audio
->i_format
= VLC_CODEC_SPDIFL
;
338 audio
->i_bytes_per_frame
= wf
->Format
.nBlockAlign
;
339 audio
->i_frame_length
= 1;
342 static void vlc_ToWave(WAVEFORMATEXTENSIBLE
*restrict wf
,
343 audio_sample_format_t
*restrict audio
)
345 switch (audio
->i_format
)
348 audio
->i_format
= VLC_CODEC_FL32
;
350 wf
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
354 audio
->i_format
= VLC_CODEC_S16N
;
356 wf
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
360 audio
->i_format
= VLC_CODEC_FL32
;
361 wf
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
364 aout_FormatPrepare (audio
);
366 wf
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
367 wf
->Format
.nChannels
= audio
->i_channels
;
368 wf
->Format
.nSamplesPerSec
= audio
->i_rate
;
369 wf
->Format
.nAvgBytesPerSec
= audio
->i_bytes_per_frame
* audio
->i_rate
;
370 wf
->Format
.nBlockAlign
= audio
->i_bytes_per_frame
;
371 wf
->Format
.wBitsPerSample
= audio
->i_bitspersample
;
372 wf
->Format
.cbSize
= sizeof (*wf
) - sizeof (wf
->Format
);
374 wf
->Samples
.wValidBitsPerSample
= audio
->i_bitspersample
;
376 wf
->dwChannelMask
= 0;
377 for (unsigned i
= 0; pi_vlc_chan_order_wg4
[i
]; i
++)
378 if (audio
->i_physical_channels
& pi_vlc_chan_order_wg4
[i
])
379 wf
->dwChannelMask
|= chans_in
[i
];
382 static int vlc_FromWave(const WAVEFORMATEX
*restrict wf
,
383 audio_sample_format_t
*restrict audio
)
385 audio
->i_rate
= wf
->nSamplesPerSec
;
386 audio
->i_physical_channels
= 0;
388 if (wf
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
390 const WAVEFORMATEXTENSIBLE
*wfe
= (void *)wf
;
392 if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
394 switch (wf
->wBitsPerSample
)
397 audio
->i_format
= VLC_CODEC_FL64
;
400 audio
->i_format
= VLC_CODEC_FL32
;
406 else if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
408 switch (wf
->wBitsPerSample
)
411 audio
->i_format
= VLC_CODEC_S32N
;
414 audio
->i_format
= VLC_CODEC_S16N
;
421 if (wfe
->Samples
.wValidBitsPerSample
!= wf
->wBitsPerSample
)
424 for (unsigned i
= 0; chans_in
[i
]; i
++)
425 if (wfe
->dwChannelMask
& chans_in
[i
])
426 audio
->i_physical_channels
|= pi_vlc_chan_order_wg4
[i
];
431 aout_FormatPrepare (audio
);
433 if (wf
->nChannels
!= audio
->i_channels
)
438 static unsigned vlc_CheckWaveOrder (const WAVEFORMATEX
*restrict wf
,
439 uint8_t *restrict table
)
443 if (wf
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
445 const WAVEFORMATEXTENSIBLE
*wfe
= (void *)wf
;
447 mask
= wfe
->dwChannelMask
;
449 return aout_CheckChannelReorder(chans_in
, chans_out
, mask
, table
);
453 static HRESULT
Restart(aout_stream_t
*s
, audio_sample_format_t
*restrict pfmt
,
454 const GUID
*sid
, bool force_dts_spdif
)
456 static INIT_ONCE freq_once
= INIT_ONCE_STATIC_INIT
;
458 if (!InitOnceExecuteOnce(&freq_once
, InitFreq
, &freq
, NULL
))
461 aout_stream_sys_t
*sys
= malloc(sizeof (*sys
));
462 if (unlikely(sys
== NULL
))
463 return E_OUTOFMEMORY
;
466 /* Configure audio stream */
467 WAVEFORMATEXTENSIBLE_IEC61937 wf_iec61937
;
468 WAVEFORMATEXTENSIBLE
*pwfe
= &wf_iec61937
.FormatExt
;
469 WAVEFORMATEX
*pwf
= &pwfe
->Format
, *pwf_closest
, *pwf_mix
= NULL
;
470 AUDCLNT_SHAREMODE shared_mode
;
471 REFERENCE_TIME buffer_duration
;
472 audio_sample_format_t fmt
= *pfmt
;
473 bool b_spdif
= AOUT_FMT_SPDIF(&fmt
);
474 bool b_hdmi
= AOUT_FMT_HDMI(&fmt
);
477 HRESULT hr
= aout_stream_Activate(s
, &IID_IAudioClient
, NULL
, &pv
);
480 msg_Err(s
, "cannot activate client (error 0x%lx)", hr
);
485 if (b_spdif
&& !b_hdmi
&& fmt
.i_format
== VLC_CODEC_DTS
&& !force_dts_spdif
486 && fmt
.i_rate
>= 48000)
488 /* Try to configure the output rate (IEC958 rate) at 768kHz. Indeed,
489 * DTS-HD (and other DTS extensions like DTS-X) can only be transmitted
490 * at 768kHz. We'll also be able to transmit DTS-Core only at this
498 vlc_SpdifToWave(pwfe
, &fmt
);
499 shared_mode
= AUDCLNT_SHAREMODE_EXCLUSIVE
;
500 /* The max buffer duration in exclusive mode is 2 seconds */
501 buffer_duration
= AOUT_MAX_PREPARE_TIME
;
505 vlc_HdmiToWave(&wf_iec61937
, &fmt
);
506 shared_mode
= AUDCLNT_SHAREMODE_EXCLUSIVE
;
507 /* The max buffer duration in exclusive mode is 2 seconds */
508 buffer_duration
= AOUT_MAX_PREPARE_TIME
;
510 else if (AOUT_FMT_LINEAR(&fmt
))
512 shared_mode
= AUDCLNT_SHAREMODE_SHARED
;
514 if (fmt
.channel_type
== AUDIO_CHANNEL_TYPE_AMBISONICS
)
516 fmt
.channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
518 /* Render Ambisonics on the native mix format */
519 hr
= IAudioClient_GetMixFormat(sys
->client
, &pwf_mix
);
520 if (FAILED(hr
) || vlc_FromWave(pwf_mix
, &fmt
))
521 vlc_ToWave(pwfe
, &fmt
); /* failed, fallback to default */
525 /* Setup low latency in order to quickly react to ambisonics filters
526 * viewpoint changes. */
527 buffer_duration
= AOUT_MIN_PREPARE_TIME
;
531 vlc_ToWave(pwfe
, &fmt
);
532 buffer_duration
= AOUT_MAX_PREPARE_TIME
* 10;
541 hr
= IAudioClient_IsFormatSupported(sys
->client
, shared_mode
,
546 if (pfmt
->i_format
== VLC_CODEC_DTS
&& b_hdmi
)
548 msg_Warn(s
, "cannot negotiate DTS at 768khz IEC958 rate (HDMI), "
549 "fallback to 48kHz (S/PDIF)");
550 IAudioClient_Release(sys
->client
);
552 return Restart(s
, pfmt
, sid
, true);
554 msg_Err(s
, "cannot negotiate audio format (error 0x%lx)%s", hr
,
555 hr
== AUDCLNT_E_UNSUPPORTED_FORMAT
556 && fmt
.i_format
== VLC_CODEC_SPDIFL
?
557 ": digital pass-through not supported" : "");
563 assert(pwf_closest
!= NULL
);
564 if (vlc_FromWave(pwf_closest
, &fmt
))
566 CoTaskMemFree(pwf_closest
);
567 msg_Err(s
, "unsupported audio format");
571 shared_mode
= AUDCLNT_SHAREMODE_SHARED
;
572 msg_Dbg(s
, "modified format");
576 assert(pwf_closest
== NULL
);
578 sys
->chans_to_reorder
= fmt
.i_format
!= VLC_CODEC_SPDIFL
?
579 vlc_CheckWaveOrder(pwf
, sys
->chans_table
) : 0;
580 sys
->format
= fmt
.i_format
;
581 sys
->block_align
= pwf
->nBlockAlign
;
582 sys
->rate
= pwf
->nSamplesPerSec
;
584 hr
= IAudioClient_Initialize(sys
->client
, shared_mode
, 0, buffer_duration
,
586 CoTaskMemFree(pwf_closest
);
589 msg_Err(s
, "cannot initialize audio client (error 0x%lx)", hr
);
593 hr
= IAudioClient_GetBufferSize(sys
->client
, &sys
->frames
);
596 msg_Err(s
, "cannot get buffer size (error 0x%lx)", hr
);
599 msg_Dbg(s
, "buffer size : %"PRIu32
" frames", sys
->frames
);
601 REFERENCE_TIME latT
, defT
, minT
;
602 if (SUCCEEDED(IAudioClient_GetStreamLatency(sys
->client
, &latT
))
603 && SUCCEEDED(IAudioClient_GetDevicePeriod(sys
->client
, &defT
, &minT
)))
605 msg_Dbg(s
, "maximum latency: %"PRIu64
"00 ns", latT
);
606 msg_Dbg(s
, "default period : %"PRIu64
"00 ns", defT
);
607 msg_Dbg(s
, "minimum period : %"PRIu64
"00 ns", minT
);
610 CoTaskMemFree(pwf_mix
);
614 s
->time_get
= TimeGet
;
620 CoTaskMemFree(pwf_mix
);
621 if (sys
->client
!= NULL
)
622 IAudioClient_Release(sys
->client
);
627 static HRESULT
Start(aout_stream_t
*s
, audio_sample_format_t
*restrict pfmt
,
630 return Restart(s
, pfmt
, sid
, false);
633 static void Stop(aout_stream_t
*s
)
635 aout_stream_sys_t
*sys
= s
->sys
;
637 IAudioClient_Stop(sys
->client
); /* should not be needed */
638 IAudioClient_Release(sys
->client
);
644 set_shortname("WASAPI")
645 set_description(N_("Windows Audio Session API output"))
646 set_capability("aout stream", 50)
647 set_category(CAT_AUDIO
)
648 set_subcategory(SUBCAT_AUDIO_AOUT
)
649 set_callbacks(Start
, Stop
)