demux/playlist: xspf: only use text-elements inside current tag
[vlc.git] / modules / access / wasapi.c
blob59b4883346e7b4aefbaf08b4eb35d994e3f5d7a5
1 /**
2 * \file wasapi.c
3 * \brief Windows Audio Session API capture plugin for VLC
4 */
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 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #define INITGUID
28 #define COBJMACROS
29 #define CONST_VTABLE
31 #include <assert.h>
32 #include <stdlib.h>
34 #include <vlc_common.h>
35 #include <vlc_aout.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)
47 (void) dll;
48 (void) reserved;
50 switch (reason)
52 case DLL_PROCESS_ATTACH:
53 if (!QueryPerformanceFrequency(&freq))
54 return FALSE;
55 break;
57 return TRUE;
60 static UINT64 GetQPC(void)
62 LARGE_INTEGER counter;
64 if (!QueryPerformanceCounter(&counter))
65 abort();
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)
76 void *pv;
78 if (FAILED(IMMDevice_QueryInterface(dev, &IID_IMMEndpoint, &pv)))
79 return false;
81 IMMEndpoint *ep = pv;
82 EDataFlow flow;
84 if (SUCCEEDED(IMMEndpoint_GetDataFlow(ep, &flow)))
85 flow = eAll;
86 IMMEndpoint_Release(ep);
87 return flow;
90 static IAudioClient *GetClient(demux_t *demux, bool *restrict loopbackp)
92 IMMDeviceEnumerator *e;
93 IMMDevice *dev;
94 void *pv;
95 HRESULT hr;
97 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
98 &IID_IMMDeviceEnumerator, &pv);
99 if (FAILED(hr))
101 msg_Err(demux, "cannot create device enumerator (error 0x%lx)", hr);
102 return NULL;
104 e = pv;
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);
112 if (FAILED(hr))
114 msg_Err(demux, "cannot get default device (error 0x%lx)", hr);
115 return NULL;
118 hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_ALL, NULL, &pv);
119 *loopbackp = GetDeviceFlow(dev) == eRender;
120 IMMDevice_Release(dev);
121 if (FAILED(hr))
122 msg_Err(demux, "cannot activate device (error 0x%lx)", hr);
123 return pv;
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 fmt->i_original_channels = fmt->i_physical_channels;
147 assert(popcount(wfe->dwChannelMask) == wf->nChannels);
149 if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
151 switch (wf->wBitsPerSample)
153 case 32:
154 switch (wfe->Samples.wValidBitsPerSample)
156 case 32:
157 fmt->i_format = VLC_CODEC_S32N;
158 break;
159 case 24:
160 #ifdef WORDS_BIGENDIAN
161 fmt->i_format = VLC_CODEC_S24B32;
162 #else
163 fmt->i_format = VLC_CODEC_S24L32;
164 #endif
165 break;
166 default:
167 return -1;
169 break;
170 case 24:
171 if (wfe->Samples.wValidBitsPerSample == 24)
172 fmt->i_format = VLC_CODEC_S24N;
173 else
174 return -1;
175 break;
176 case 16:
177 if (wfe->Samples.wValidBitsPerSample == 16)
178 fmt->i_format = VLC_CODEC_S16N;
179 else
180 return -1;
181 break;
182 case 8:
183 if (wfe->Samples.wValidBitsPerSample == 8)
184 fmt->i_format = VLC_CODEC_S8;
185 else
186 return -1;
187 break;
188 default:
189 return -1;
192 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
194 if (wf->wBitsPerSample != wfe->Samples.wValidBitsPerSample)
195 return -1;
197 switch (wf->wBitsPerSample)
199 case 64:
200 fmt->i_format = VLC_CODEC_FL64;
201 break;
202 case 32:
203 fmt->i_format = VLC_CODEC_FL32;
204 break;
205 default:
206 return -1;
209 /*else if (IsEqualIID(&wfe->Subformat, &KSDATAFORMAT_SUBTYPE_DRM)) {} */
210 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))
211 fmt->i_format = VLC_CODEC_ALAW;
212 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))
213 fmt->i_format = VLC_CODEC_MULAW;
214 else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_ADPCM))
215 fmt->i_format = VLC_CODEC_ADPCM_MS;
216 else
217 return -1;
219 aout_FormatPrepare(fmt);
220 if (wf->nChannels != fmt->i_channels)
221 return -1;
223 return 0;
226 static es_out_id_t *CreateES(demux_t *demux, IAudioClient *client, bool loop,
227 mtime_t caching, size_t *restrict frame_size)
229 es_format_t fmt;
230 WAVEFORMATEX *pwf;
231 HRESULT hr;
233 hr = IAudioClient_GetMixFormat(client, &pwf);
234 if (FAILED(hr))
236 msg_Err(demux, "cannot get mix format (error 0x%lx)", hr);
237 return NULL;
240 es_format_Init(&fmt, AUDIO_ES, 0);
241 if (vlc_FromWave(pwf, &fmt.audio))
243 msg_Err(demux, "unsupported mix format");
244 CoTaskMemFree(pwf);
245 return NULL;
248 fmt.i_codec = fmt.audio.i_format;
249 fmt.i_bitrate = fmt.audio.i_bitspersample * fmt.audio.i_channels
250 * fmt.audio.i_rate;
251 *frame_size = fmt.audio.i_bitspersample * fmt.audio.i_channels / 8;
253 DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
254 if (loop)
255 flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
257 /* Request at least thrice the PTS delay */
258 REFERENCE_TIME bufsize = caching * INT64_C(10) * 3;
260 hr = IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, flags,
261 bufsize, 0, pwf, NULL);
262 CoTaskMemFree(pwf);
263 if (FAILED(hr))
265 msg_Err(demux, "cannot initialize audio client (error 0x%lx)", hr);
266 return NULL;
268 return es_out_Add(demux->out, &fmt);
271 struct demux_sys_t
273 IAudioClient *client;
274 es_out_id_t *es;
276 size_t frame_size;
277 mtime_t caching;
278 mtime_t start_time;
280 HANDLE events[2];
281 union {
282 HANDLE thread;
283 HANDLE ready;
287 static unsigned __stdcall Thread(void *data)
289 demux_t *demux = data;
290 demux_sys_t *sys = demux->p_sys;
291 IAudioCaptureClient *capture = NULL;
292 void *pv;
293 HRESULT hr;
295 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
296 assert(SUCCEEDED(hr)); /* COM already allocated by parent thread */
297 SetEvent(sys->ready);
299 hr = IAudioClient_GetService(sys->client, &IID_IAudioCaptureClient, &pv);
300 if (FAILED(hr))
302 msg_Err(demux, "cannot get capture client (error 0x%lx)", hr);
303 goto out;
305 capture = pv;
307 hr = IAudioClient_Start(sys->client);
308 if (FAILED(hr))
310 msg_Err(demux, "cannot start client (error 0x%lx)", hr);
311 IAudioCaptureClient_Release(capture);
312 goto out;
315 while (WaitForMultipleObjects(2, sys->events, FALSE, INFINITE)
316 != WAIT_OBJECT_0)
318 BYTE *data;
319 UINT32 frames;
320 DWORD flags;
321 UINT64 qpc;
322 mtime_t pts;
324 hr = IAudioCaptureClient_GetBuffer(capture, &data, &frames, &flags,
325 NULL, &qpc);
326 if (hr != S_OK)
327 continue;
329 pts = mdate() - ((GetQPC() - qpc) / 10);
331 es_out_Control(demux->out, ES_OUT_SET_PCR, pts);
333 size_t bytes = frames * sys->frame_size;
334 block_t *block = block_Alloc(bytes);
336 if (likely(block != NULL)) {
337 memcpy(block->p_buffer, data, bytes);
338 block->i_nb_samples = frames;
339 block->i_pts = block->i_dts = pts;
340 es_out_Send(demux->out, sys->es, block);
343 IAudioCaptureClient_ReleaseBuffer(capture, frames);
346 IAudioClient_Stop(sys->client);
347 IAudioCaptureClient_Release(capture);
348 out:
349 CoUninitialize();
350 return 0;
353 static int Control(demux_t *demux, int query, va_list ap)
355 demux_sys_t *sys = demux->p_sys;
357 switch (query)
359 case DEMUX_GET_TIME:
360 *(va_arg(ap, int64_t *)) = mdate() - sys->start_time;
361 break;
363 case DEMUX_GET_PTS_DELAY:
364 *(va_arg(ap, int64_t *)) = sys->caching;
365 break;
367 case DEMUX_HAS_UNSUPPORTED_META:
368 case DEMUX_CAN_RECORD:
369 case DEMUX_CAN_PAUSE:
370 case DEMUX_CAN_CONTROL_PACE:
371 case DEMUX_CAN_CONTROL_RATE:
372 case DEMUX_CAN_SEEK:
373 *(va_arg(ap, bool *)) = false;
374 break;
376 default:
377 return VLC_EGENERIC;
380 return VLC_SUCCESS;
383 static int Open(vlc_object_t *obj)
385 demux_t *demux = (demux_t *)obj;
386 HRESULT hr;
388 if (demux->psz_location != NULL && demux->psz_location != '\0')
389 return VLC_EGENERIC; /* TODO non-default device */
391 demux_sys_t *sys = malloc(sizeof (*sys));
392 if (unlikely(sys == NULL))
393 return VLC_ENOMEM;
395 sys->client = NULL;
396 sys->es = NULL;
397 sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching");
398 sys->start_time = mdate();
399 for (unsigned i = 0; i < 2; i++)
400 sys->events[i] = NULL;
402 for (unsigned i = 0; i < 2; i++) {
403 sys->events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
404 if (sys->events[i] == NULL)
405 goto error;
408 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
409 if (unlikely(FAILED(hr))) {
410 msg_Err(demux, "cannot initialize COM (error 0x%lx)", hr);
411 goto error;
414 bool loopback;
415 sys->client = GetClient(demux, &loopback);
416 if (sys->client == NULL) {
417 CoUninitialize();
418 goto error;
421 sys->es = CreateES(demux, sys->client, loopback, sys->caching,
422 &sys->frame_size);
423 if (sys->es == NULL)
424 goto error;
426 hr = IAudioClient_SetEventHandle(sys->client, sys->events[1]);
427 if (FAILED(hr)) {
428 msg_Err(demux, "cannot set event handle (error 0x%lx)", hr);
429 goto error;
432 demux->p_sys = sys;
434 sys->ready = CreateEvent(NULL, FALSE, FALSE, NULL);
435 if (sys->ready == NULL)
436 goto error;
438 uintptr_t h = _beginthreadex(NULL, 0, Thread, demux, 0, NULL);
439 if (h != 0)
440 WaitForSingleObject(sys->ready, INFINITE);
441 CloseHandle(sys->ready);
443 sys->thread = (HANDLE)h;
444 if (sys->thread == NULL)
445 goto error;
446 CoUninitialize();
448 demux->pf_demux = NULL;
449 demux->pf_control = Control;
450 return VLC_SUCCESS;
452 error:
453 if (sys->es != NULL)
454 es_out_Del(demux->out, sys->es);
455 if (sys->client != NULL)
457 IAudioClient_Release(sys->client);
458 CoUninitialize();
460 for (unsigned i = 0; i < 2; i++)
461 if (sys->events[i] != NULL)
462 CloseHandle(sys->events[i]);
463 free(sys);
464 return VLC_ENOMEM;
467 static void Close (vlc_object_t *obj)
469 demux_t *demux = (demux_t *)obj;
470 demux_sys_t *sys = demux->p_sys;
471 HRESULT hr;
473 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
474 assert(SUCCEEDED(hr));
476 SetEvent(sys->events[0]);
477 WaitForSingleObject(sys->thread, INFINITE);
478 CloseHandle(sys->thread);
480 es_out_Del(demux->out, sys->es);
481 IAudioClient_Release(sys->client);
482 CoUninitialize();
483 for (unsigned i = 0; i < 2; i++)
484 CloseHandle(sys->events[i]);
485 free(sys);
488 #define LOOPBACK_TEXT N_("Loopback mode")
489 #define LOOPBACK_LONGTEXT N_("Record an audio rendering endpoint.")
491 vlc_module_begin()
492 set_shortname(N_("WASAPI"))
493 set_description(N_("Windows Audio Session API input"))
494 set_capability("access_demux", 0)
495 set_category(CAT_INPUT)
496 set_subcategory(SUBCAT_INPUT_ACCESS)
498 add_bool("wasapi-loopback", false, LOOPBACK_TEXT, LOOPBACK_LONGTEXT, true)
500 add_shortcut("wasapi")
501 set_callbacks(Open, Close)
502 vlc_module_end()