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 IAudioClient *GetClient(demux_t *demux)
76 IMMDeviceEnumerator *e;
81 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
82 &IID_IMMDeviceEnumerator, &pv);
85 msg_Err(demux, "cannot create device enumerator (error 0x%lx)", hr);
90 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(e, eCapture,
91 eCommunications, &dev);
92 IMMDeviceEnumerator_Release(e);
95 msg_Err(demux, "cannot get default device (error 0x%lx)", hr);
99 hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_ALL, NULL, &pv);
100 IMMDevice_Release(dev);
102 msg_Err(demux, "cannot activate device (error 0x%lx)", hr);
106 static int vlc_FromWave(const WAVEFORMATEX *restrict wf,
107 audio_sample_format_t *restrict fmt)
109 fmt->i_rate = wf->nSamplesPerSec;
111 /* As per MSDN, IAudioClient::GetMixFormat() always uses this format. */
112 assert(wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
114 const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
116 fmt->i_physical_channels = 0;
117 if (wfe->dwChannelMask & SPEAKER_FRONT_LEFT)
118 fmt->i_physical_channels |= AOUT_CHAN_LEFT;
119 if (wfe->dwChannelMask & SPEAKER_FRONT_RIGHT)
120 fmt->i_physical_channels |= AOUT_CHAN_RIGHT;
121 if (wfe->dwChannelMask & SPEAKER_FRONT_CENTER)
122 fmt->i_physical_channels |= AOUT_CHAN_CENTER;
123 if (wfe->dwChannelMask & SPEAKER_LOW_FREQUENCY)
124 fmt->i_physical_channels |= AOUT_CHAN_LFE;
126 fmt->i_original_channels = fmt->i_physical_channels;
127 assert(popcount(wfe->dwChannelMask) == wf->nChannels);
129 if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
131 switch (wf->wBitsPerSample)
134 switch (wfe->Samples.wValidBitsPerSample)
137 fmt->i_format = VLC_CODEC_S32N;
140 #ifdef WORDS_BIGENDIAN
141 fmt->i_format = VLC_CODEC_S24B32;
143 fmt->i_format = VLC_CODEC_S24L32;
151 if (wfe->Samples.wValidBitsPerSample == 24)
152 fmt->i_format = VLC_CODEC_S24N;
157 if (wfe->Samples.wValidBitsPerSample == 16)
158 fmt->i_format = VLC_CODEC_S16N;
163 if (wfe->Samples.wValidBitsPerSample == 8)
164 fmt->i_format = VLC_CODEC_S8;
172 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
174 if (wf->wBitsPerSample != wfe->Samples.wValidBitsPerSample)
177 switch (wf->wBitsPerSample)
180 fmt->i_format = VLC_CODEC_FL64;
183 fmt->i_format = VLC_CODEC_FL32;
189 /*else if (IsEqualIID(&wfe->Subformat, &KSDATAFORMAT_SUBTYPE_DRM)) {} */
190 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))
191 fmt->i_format = VLC_CODEC_ALAW;
192 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))
193 fmt->i_format = VLC_CODEC_MULAW;
194 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_ADPCM))
195 fmt->i_format = VLC_CODEC_ADPCM_MS;
199 aout_FormatPrepare(fmt);
200 if (wf->nChannels != fmt->i_channels)
206 static es_out_id_t *CreateES(demux_t *demux, IAudioClient *client,
207 mtime_t caching, size_t *restrict frame_size)
213 hr = IAudioClient_GetMixFormat(client, &pwf);
216 msg_Err(demux, "cannot get mix format (error 0x%lx)", hr);
220 es_format_Init(&fmt, AUDIO_ES, 0);
221 if (vlc_FromWave(pwf, &fmt.audio))
223 msg_Err(demux, "unsupported mix format");
228 fmt.i_codec = fmt.audio.i_format;
229 fmt.i_bitrate = fmt.audio.i_bitspersample * fmt.audio.i_channels
231 *frame_size = fmt.audio.i_bitspersample * fmt.audio.i_channels / 8;
233 DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; /* TODO: loopback */
234 /* Request at least thrice the PTS delay */
235 REFERENCE_TIME bufsize = caching * INT64_C(10) * 3;
237 hr = IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, flags,
238 bufsize, 0, pwf, NULL);
242 msg_Err(demux, "cannot initialize audio client (error 0x%lx)", hr);
245 return es_out_Add(demux->out, &fmt);
250 IAudioClient *client;
264 static unsigned __stdcall Thread(void *data)
266 demux_t *demux = data;
267 demux_sys_t *sys = demux->p_sys;
268 IAudioCaptureClient *capture = NULL;
272 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
273 assert(SUCCEEDED(hr)); /* COM already allocated by parent thread */
274 SetEvent(sys->ready);
276 hr = IAudioClient_GetService(sys->client, &IID_IAudioCaptureClient, &pv);
279 msg_Err(demux, "cannot get capture client (error 0x%lx)", hr);
284 hr = IAudioClient_Start(sys->client);
287 msg_Err(demux, "cannot start client (error 0x%lx)", hr);
288 IAudioCaptureClient_Release(capture);
292 while (WaitForMultipleObjects(2, sys->events, FALSE, INFINITE)
301 hr = IAudioCaptureClient_GetBuffer(capture, &data, &frames, &flags,
306 pts = mdate() - ((GetQPC() - qpc) / 10);
308 es_out_Control(demux->out, ES_OUT_SET_PCR, pts);
310 size_t bytes = frames * sys->frame_size;
311 block_t *block = block_Alloc(bytes);
313 if (likely(block != NULL)) {
314 memcpy(block->p_buffer, data, bytes);
315 block->i_nb_samples = frames;
316 block->i_pts = block->i_dts = pts;
317 es_out_Send(demux->out, sys->es, block);
320 IAudioCaptureClient_ReleaseBuffer(capture, frames);
323 IAudioClient_Stop(sys->client);
324 IAudioCaptureClient_Release(capture);
330 static int Control(demux_t *demux, int query, va_list ap)
332 demux_sys_t *sys = demux->p_sys;
337 *(va_arg(ap, int64_t *)) = mdate() - sys->start_time;
340 case DEMUX_GET_PTS_DELAY:
341 *(va_arg(ap, int64_t *)) = sys->caching;
344 case DEMUX_HAS_UNSUPPORTED_META:
345 case DEMUX_CAN_RECORD:
346 case DEMUX_CAN_PAUSE:
347 case DEMUX_CAN_CONTROL_PACE:
348 case DEMUX_CAN_CONTROL_RATE:
350 *(va_arg(ap, bool *)) = false;
360 static int Open(vlc_object_t *obj)
362 demux_t *demux = (demux_t *)obj;
365 if (demux->psz_location != NULL && demux->psz_location != '\0')
366 return VLC_EGENERIC; /* TODO non-default device */
368 demux_sys_t *sys = malloc(sizeof (*sys));
369 if (unlikely(sys == NULL))
374 sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching");
375 sys->start_time = mdate();
376 for (unsigned i = 0; i < 2; i++)
377 sys->events[i] = NULL;
379 for (unsigned i = 0; i < 2; i++) {
380 sys->events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
381 if (sys->events[i] == NULL)
385 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
386 if (unlikely(FAILED(hr))) {
387 msg_Err(demux, "cannot initialize COM (error 0x%lx)", hr);
391 sys->client = GetClient(demux);
392 if (sys->client == NULL) {
397 sys->es = CreateES(demux, sys->client, sys->caching, &sys->frame_size);
401 hr = IAudioClient_SetEventHandle(sys->client, sys->events[1]);
403 msg_Err(demux, "cannot set event handle (error 0x%lx)", hr);
409 sys->ready = CreateEvent(NULL, FALSE, FALSE, NULL);
410 if (sys->ready == NULL)
413 uintptr_t h = _beginthreadex(NULL, 0, Thread, demux, 0, NULL);
415 WaitForSingleObject(sys->ready, INFINITE);
416 CloseHandle(sys->ready);
418 sys->thread = (HANDLE)h;
419 if (sys->thread == NULL)
423 demux->pf_demux = NULL;
424 demux->pf_control = Control;
429 es_out_Del(demux->out, sys->es);
430 if (sys->client != NULL)
432 IAudioClient_Release(sys->client);
435 for (unsigned i = 0; i < 2; i++)
436 if (sys->events[i] != NULL)
437 CloseHandle(sys->events[i]);
442 static void Close (vlc_object_t *obj)
444 demux_t *demux = (demux_t *)obj;
445 demux_sys_t *sys = demux->p_sys;
448 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
449 assert(SUCCEEDED(hr));
451 SetEvent(sys->events[0]);
452 WaitForSingleObject(sys->thread, INFINITE);
453 CloseHandle(sys->thread);
455 es_out_Del(demux->out, sys->es);
456 IAudioClient_Release(sys->client);
458 for (unsigned i = 0; i < 2; i++)
459 CloseHandle(sys->events[i]);
464 set_shortname(N_("WASAPI"))
465 set_description(N_("Windows Audio Session API input"))
466 set_capability("access_demux", 0)
467 set_category(CAT_INPUT)
468 set_subcategory(SUBCAT_INPUT_ACCESS)
470 add_shortcut("wasapi")
471 set_callbacks(Open, Close)