3 * \brief Windows Audio Session API capture plugin for VLC
5 /*****************************************************************************
6 * Copyright (C) 2014-2015 RĂ©mi Denis-Courmont
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
34 #include <vlc_common.h>
36 #include <vlc_demux.h>
37 #include <vlc_plugin.h>
38 #include <mmdeviceapi.h>
39 #include <audioclient.h>
41 static LARGE_INTEGER freq
; /* performance counters frequency */
43 BOOL WINAPI
DllMain(HINSTANCE
, DWORD
, LPVOID
); /* avoid warning */
45 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, LPVOID reserved
)
52 case DLL_PROCESS_ATTACH
:
53 if (!QueryPerformanceFrequency(&freq
))
60 static UINT64
GetQPC(void)
62 LARGE_INTEGER counter
;
64 if (!QueryPerformanceCounter(&counter
))
67 lldiv_t d
= lldiv(counter
.QuadPart
, freq
.QuadPart
);
68 return (d
.quot
* 10000000) + ((d
.rem
* 10000000) / freq
.QuadPart
);
71 static_assert(CLOCK_FREQ
* 10 == 10000000,
72 "REFERENCE_TIME conversion broken");
74 static EDataFlow
GetDeviceFlow(IMMDevice
*dev
)
78 if (FAILED(IMMDevice_QueryInterface(dev
, &IID_IMMEndpoint
, &pv
)))
84 if (SUCCEEDED(IMMEndpoint_GetDataFlow(ep
, &flow
)))
86 IMMEndpoint_Release(ep
);
90 static IAudioClient
*GetClient(demux_t
*demux
, bool *restrict loopbackp
)
92 IMMDeviceEnumerator
*e
;
97 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_ALL
,
98 &IID_IMMDeviceEnumerator
, &pv
);
101 msg_Err(demux
, "cannot create device enumerator (error 0x%lx)", hr
);
106 bool loopback
= var_InheritBool(demux
, "wasapi-loopback");
107 EDataFlow flow
= loopback
? eRender
: eCapture
;
108 ERole role
= loopback
? eConsole
: eCommunications
;
110 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(e
, flow
, role
, &dev
);
111 IMMDeviceEnumerator_Release(e
);
114 msg_Err(demux
, "cannot get default device (error 0x%lx)", hr
);
118 hr
= IMMDevice_Activate(dev
, &IID_IAudioClient
, CLSCTX_ALL
, NULL
, &pv
);
119 *loopbackp
= GetDeviceFlow(dev
) == eRender
;
120 IMMDevice_Release(dev
);
122 msg_Err(demux
, "cannot activate device (error 0x%lx)", hr
);
126 static int vlc_FromWave(const WAVEFORMATEX
*restrict wf
,
127 audio_sample_format_t
*restrict fmt
)
129 fmt
->i_rate
= wf
->nSamplesPerSec
;
131 /* As per MSDN, IAudioClient::GetMixFormat() always uses this format. */
132 assert(wf
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
);
134 const WAVEFORMATEXTENSIBLE
*wfe
= (void *)wf
;
136 fmt
->i_physical_channels
= 0;
137 if (wfe
->dwChannelMask
& SPEAKER_FRONT_LEFT
)
138 fmt
->i_physical_channels
|= AOUT_CHAN_LEFT
;
139 if (wfe
->dwChannelMask
& SPEAKER_FRONT_RIGHT
)
140 fmt
->i_physical_channels
|= AOUT_CHAN_RIGHT
;
141 if (wfe
->dwChannelMask
& SPEAKER_FRONT_CENTER
)
142 fmt
->i_physical_channels
|= AOUT_CHAN_CENTER
;
143 if (wfe
->dwChannelMask
& SPEAKER_LOW_FREQUENCY
)
144 fmt
->i_physical_channels
|= AOUT_CHAN_LFE
;
146 assert(popcount(wfe
->dwChannelMask
) == wf
->nChannels
);
148 if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
150 switch (wf
->wBitsPerSample
)
153 switch (wfe
->Samples
.wValidBitsPerSample
)
156 fmt
->i_format
= VLC_CODEC_S32N
;
159 #ifdef WORDS_BIGENDIAN
160 fmt
->i_format
= VLC_CODEC_S24B32
;
162 fmt
->i_format
= VLC_CODEC_S24L32
;
170 if (wfe
->Samples
.wValidBitsPerSample
== 24)
171 fmt
->i_format
= VLC_CODEC_S24N
;
176 if (wfe
->Samples
.wValidBitsPerSample
== 16)
177 fmt
->i_format
= VLC_CODEC_S16N
;
182 if (wfe
->Samples
.wValidBitsPerSample
== 8)
183 fmt
->i_format
= VLC_CODEC_S8
;
191 else if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
193 if (wf
->wBitsPerSample
!= wfe
->Samples
.wValidBitsPerSample
)
196 switch (wf
->wBitsPerSample
)
199 fmt
->i_format
= VLC_CODEC_FL64
;
202 fmt
->i_format
= VLC_CODEC_FL32
;
208 /*else if (IsEqualIID(&wfe->Subformat, &KSDATAFORMAT_SUBTYPE_DRM)) {} */
209 else if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_ALAW
))
210 fmt
->i_format
= VLC_CODEC_ALAW
;
211 else if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_MULAW
))
212 fmt
->i_format
= VLC_CODEC_MULAW
;
213 else if (IsEqualIID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_ADPCM
))
214 fmt
->i_format
= VLC_CODEC_ADPCM_MS
;
218 aout_FormatPrepare(fmt
);
219 if (wf
->nChannels
!= fmt
->i_channels
)
225 static es_out_id_t
*CreateES(demux_t
*demux
, IAudioClient
*client
, bool loop
,
226 mtime_t caching
, size_t *restrict frame_size
)
232 hr
= IAudioClient_GetMixFormat(client
, &pwf
);
235 msg_Err(demux
, "cannot get mix format (error 0x%lx)", hr
);
239 es_format_Init(&fmt
, AUDIO_ES
, 0);
240 if (vlc_FromWave(pwf
, &fmt
.audio
))
242 msg_Err(demux
, "unsupported mix format");
247 fmt
.i_codec
= fmt
.audio
.i_format
;
248 fmt
.i_bitrate
= fmt
.audio
.i_bitspersample
* fmt
.audio
.i_channels
250 *frame_size
= fmt
.audio
.i_bitspersample
* fmt
.audio
.i_channels
/ 8;
252 DWORD flags
= AUDCLNT_STREAMFLAGS_EVENTCALLBACK
;
254 flags
|= AUDCLNT_STREAMFLAGS_LOOPBACK
;
256 /* Request at least thrice the PTS delay */
257 REFERENCE_TIME bufsize
= caching
* INT64_C(10) * 3;
259 hr
= IAudioClient_Initialize(client
, AUDCLNT_SHAREMODE_SHARED
, flags
,
260 bufsize
, 0, pwf
, NULL
);
264 msg_Err(demux
, "cannot initialize audio client (error 0x%lx)", hr
);
267 return es_out_Add(demux
->out
, &fmt
);
272 IAudioClient
*client
;
286 static unsigned __stdcall
Thread(void *data
)
288 demux_t
*demux
= data
;
289 demux_sys_t
*sys
= demux
->p_sys
;
290 IAudioCaptureClient
*capture
= NULL
;
294 hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
295 assert(SUCCEEDED(hr
)); /* COM already allocated by parent thread */
296 SetEvent(sys
->ready
);
298 hr
= IAudioClient_GetService(sys
->client
, &IID_IAudioCaptureClient
, &pv
);
301 msg_Err(demux
, "cannot get capture client (error 0x%lx)", hr
);
306 hr
= IAudioClient_Start(sys
->client
);
309 msg_Err(demux
, "cannot start client (error 0x%lx)", hr
);
310 IAudioCaptureClient_Release(capture
);
314 while (WaitForMultipleObjects(2, sys
->events
, FALSE
, INFINITE
)
323 hr
= IAudioCaptureClient_GetBuffer(capture
, &data
, &frames
, &flags
,
328 pts
= mdate() - ((GetQPC() - qpc
) / 10);
330 es_out_Control(demux
->out
, ES_OUT_SET_PCR
, pts
);
332 size_t bytes
= frames
* sys
->frame_size
;
333 block_t
*block
= block_Alloc(bytes
);
335 if (likely(block
!= NULL
)) {
336 memcpy(block
->p_buffer
, data
, bytes
);
337 block
->i_nb_samples
= frames
;
338 block
->i_pts
= block
->i_dts
= pts
;
339 es_out_Send(demux
->out
, sys
->es
, block
);
342 IAudioCaptureClient_ReleaseBuffer(capture
, frames
);
345 IAudioClient_Stop(sys
->client
);
346 IAudioCaptureClient_Release(capture
);
352 static int Control(demux_t
*demux
, int query
, va_list ap
)
354 demux_sys_t
*sys
= demux
->p_sys
;
359 *(va_arg(ap
, int64_t *)) = mdate() - sys
->start_time
;
362 case DEMUX_GET_PTS_DELAY
:
363 *(va_arg(ap
, int64_t *)) = sys
->caching
;
366 case DEMUX_HAS_UNSUPPORTED_META
:
367 case DEMUX_CAN_RECORD
:
368 case DEMUX_CAN_PAUSE
:
369 case DEMUX_CAN_CONTROL_PACE
:
370 case DEMUX_CAN_CONTROL_RATE
:
372 *(va_arg(ap
, bool *)) = false;
382 static int Open(vlc_object_t
*obj
)
384 demux_t
*demux
= (demux_t
*)obj
;
387 if (demux
->psz_location
!= NULL
&& *demux
->psz_location
!= '\0')
388 return VLC_EGENERIC
; /* TODO non-default device */
390 demux_sys_t
*sys
= vlc_malloc(obj
, sizeof (*sys
));
391 if (unlikely(sys
== NULL
))
396 sys
->caching
= INT64_C(1000) * var_InheritInteger(obj
, "live-caching");
397 sys
->start_time
= mdate();
398 for (unsigned i
= 0; i
< 2; i
++)
399 sys
->events
[i
] = NULL
;
401 for (unsigned i
= 0; i
< 2; i
++) {
402 sys
->events
[i
] = CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
403 if (sys
->events
[i
] == NULL
)
407 hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
408 if (unlikely(FAILED(hr
))) {
409 msg_Err(demux
, "cannot initialize COM (error 0x%lx)", hr
);
414 sys
->client
= GetClient(demux
, &loopback
);
415 if (sys
->client
== NULL
) {
420 sys
->es
= CreateES(demux
, sys
->client
, loopback
, sys
->caching
,
425 hr
= IAudioClient_SetEventHandle(sys
->client
, sys
->events
[1]);
427 msg_Err(demux
, "cannot set event handle (error 0x%lx)", hr
);
433 sys
->ready
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
434 if (sys
->ready
== NULL
)
437 uintptr_t h
= _beginthreadex(NULL
, 0, Thread
, demux
, 0, NULL
);
439 WaitForSingleObject(sys
->ready
, INFINITE
);
440 CloseHandle(sys
->ready
);
442 sys
->thread
= (HANDLE
)h
;
443 if (sys
->thread
== NULL
)
447 demux
->pf_demux
= NULL
;
448 demux
->pf_control
= Control
;
453 es_out_Del(demux
->out
, sys
->es
);
454 if (sys
->client
!= NULL
)
456 IAudioClient_Release(sys
->client
);
459 for (unsigned i
= 0; i
< 2; i
++)
460 if (sys
->events
[i
] != NULL
)
461 CloseHandle(sys
->events
[i
]);
465 static void Close (vlc_object_t
*obj
)
467 demux_t
*demux
= (demux_t
*)obj
;
468 demux_sys_t
*sys
= demux
->p_sys
;
471 hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
472 assert(SUCCEEDED(hr
));
474 SetEvent(sys
->events
[0]);
475 WaitForSingleObject(sys
->thread
, INFINITE
);
476 CloseHandle(sys
->thread
);
478 es_out_Del(demux
->out
, sys
->es
);
479 IAudioClient_Release(sys
->client
);
481 for (unsigned i
= 0; i
< 2; i
++)
482 CloseHandle(sys
->events
[i
]);
485 #define LOOPBACK_TEXT N_("Loopback mode")
486 #define LOOPBACK_LONGTEXT N_("Record an audio rendering endpoint.")
489 set_shortname(N_("WASAPI"))
490 set_description(N_("Windows Audio Session API input"))
491 set_capability("access_demux", 0)
492 set_category(CAT_INPUT
)
493 set_subcategory(SUBCAT_INPUT_ACCESS
)
495 add_bool("wasapi-loopback", false, LOOPBACK_TEXT
, LOOPBACK_LONGTEXT
, true)
497 add_shortcut("wasapi")
498 set_callbacks(Open
, Close
)