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 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_codecs.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
))
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
, vlc_tick_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
= 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
);
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 vlc_tick_sleep(sys
->frames
* VLC_TICK_FROM_MS(500) / 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 if (audio
->i_rate
< 48000)
316 /* Wasapi doesn't accept DTS @ 44.1kHz but accept IEC 60958 PCM */
317 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_WAVEFORMATEX
;
320 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_IEC61937_DTS
;
322 case VLC_CODEC_SPDIFL
:
323 case VLC_CODEC_SPDIFB
:
325 wf
->SubFormat
= _KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL
;
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
)
354 audio
->i_format
= VLC_CODEC_FL32
;
356 wf
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
360 audio
->i_format
= VLC_CODEC_S16N
;
362 wf
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
366 audio
->i_format
= VLC_CODEC_FL32
;
367 wf
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
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
)
403 audio
->i_format
= VLC_CODEC_FL64
;
406 audio
->i_format
= VLC_CODEC_FL32
;
412 else if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
414 switch (wf
->wBitsPerSample
)
417 audio
->i_format
= VLC_CODEC_S32N
;
420 audio
->i_format
= VLC_CODEC_S16N
;
427 if (wfe
->Samples
.wValidBitsPerSample
!= wf
->wBitsPerSample
)
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
];
437 aout_FormatPrepare (audio
);
439 if (wf
->nChannels
!= audio
->i_channels
)
444 static unsigned vlc_CheckWaveOrder (const WAVEFORMATEX
*restrict wf
,
445 uint8_t *restrict table
)
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
,
462 static INIT_ONCE freq_once
= INIT_ONCE_STATIC_INIT
;
464 if (!InitOnceExecuteOnce(&freq_once
, InitFreq
, &freq
, NULL
))
467 aout_stream_sys_t
*sys
= malloc(sizeof (*sys
));
468 if (unlikely(sys
== NULL
))
469 return E_OUTOFMEMORY
;
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");
494 HRESULT hr
= aout_stream_Activate(s
, &IID_IAudioClient
, NULL
, &pv
);
497 msg_Err(s
, "cannot activate client (error 0x%lx)", hr
);
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);
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 */
531 /* Setup low latency in order to quickly react to ambisonics filters
532 * viewpoint changes. */
533 buffer_duration
= MSFTIME_FROM_MS(200);
537 vlc_ToWave(pwfe
, &fmt
);
538 buffer_duration
= MSFTIME_FROM_VLC_TICK(AOUT_MAX_PREPARE_TIME
);
547 hr
= IAudioClient_IsFormatSupported(sys
->client
, shared_mode
,
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
);
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" : "");
570 assert(pwf_closest
!= NULL
);
571 if (vlc_FromWave(pwf_closest
, &fmt
))
573 CoTaskMemFree(pwf_closest
);
574 msg_Err(s
, "unsupported audio format");
578 shared_mode
= AUDCLNT_SHAREMODE_SHARED
;
579 msg_Dbg(s
, "modified format");
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
,
593 CoTaskMemFree(pwf_closest
);
596 msg_Err(s
, "cannot initialize audio client (error 0x%lx)", hr
);
600 hr
= IAudioClient_GetBufferSize(sys
->client
, &sys
->frames
);
603 msg_Err(s
, "cannot get buffer size (error 0x%lx)", hr
);
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
);
621 s
->time_get
= TimeGet
;
627 CoTaskMemFree(pwf_mix
);
628 if (sys
->client
!= NULL
)
629 IAudioClient_Release(sys
->client
);
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
);
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
)