1 /*
2 * This file is part of mpv.
3 *
4 * Original author: Jonathan Yong <10walls@gmail.com>
5 *
6 * mpv is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * mpv 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.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <math.h>
21 #include <inttypes.h>
22 #include <libavutil/mathematics.h>
23
24 #include "options/m_option.h"
25 #include "osdep/threads.h"
26 #include "osdep/timer.h"
27 #include "osdep/io.h"
28 #include "misc/dispatch.h"
29 #include "ao_wasapi.h"
30
31 // naive av_rescale for unsigned
uint64_scale(UINT64 x,UINT64 num,UINT64 den)32 static UINT64 uint64_scale(UINT64 x, UINT64 num, UINT64 den)
33 {
34 return (x / den) * num
35 + ((x % den) * (num / den))
36 + ((x % den) * (num % den)) / den;
37 }
38
get_device_delay(struct wasapi_state * state,double * delay_us)39 static HRESULT get_device_delay(struct wasapi_state *state, double *delay_us) {
40 UINT64 sample_count = atomic_load(&state->sample_count);
41 UINT64 position, qpc_position;
42 HRESULT hr;
43
44 hr = IAudioClock_GetPosition(state->pAudioClock, &position, &qpc_position);
45 EXIT_ON_ERROR(hr);
46 // GetPosition succeeded, but the result may be
47 // inaccurate due to the length of the call
48 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd370889%28v=vs.85%29.aspx
49 if (hr == S_FALSE)
50 MP_VERBOSE(state, "Possibly inaccurate device position.\n");
51
52 // convert position to number of samples careful to avoid overflow
53 UINT64 sample_position = uint64_scale(position,
54 state->format.Format.nSamplesPerSec,
55 state->clock_frequency);
56 INT64 diff = sample_count - sample_position;
57 *delay_us = diff * 1e6 / state->format.Format.nSamplesPerSec;
58
59 // Correct for any delay in IAudioClock_GetPosition above.
60 // This should normally be very small (<1 us), but just in case. . .
61 LARGE_INTEGER qpc;
62 QueryPerformanceCounter(&qpc);
63 INT64 qpc_diff = av_rescale(qpc.QuadPart, 10000000, state->qpc_frequency.QuadPart)
64 - qpc_position;
65 // ignore the above calculation if it yields more than 10 seconds (due to
66 // possible overflow inside IAudioClock_GetPosition)
67 if (qpc_diff < 10 * 10000000) {
68 *delay_us -= qpc_diff / 10.0; // convert to us
69 } else {
70 MP_VERBOSE(state, "Insane qpc delay correction of %g seconds. "
71 "Ignoring it.\n", qpc_diff / 10000000.0);
72 }
73
74 if (sample_count > 0 && *delay_us <= 0) {
75 MP_WARN(state, "Under-run: Device delay: %g us\n", *delay_us);
76 } else {
77 MP_TRACE(state, "Device delay: %g us\n", *delay_us);
78 }
79
80 return S_OK;
81 exit_label:
82 MP_ERR(state, "Error getting device delay: %s\n", mp_HRESULT_to_str(hr));
83 return hr;
84 }
85
thread_feed(struct ao * ao)86 static bool thread_feed(struct ao *ao)
87 {
88 struct wasapi_state *state = ao->priv;
89 HRESULT hr;
90
91 UINT32 frame_count = state->bufferFrameCount;
92 UINT32 padding;
93 hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding);
94 EXIT_ON_ERROR(hr);
95 bool refill = false;
96 if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) {
97 // Return if there's nothing to do.
98 if (frame_count <= padding)
99 return false;
100 // In shared mode, there is only one buffer of size bufferFrameCount.
101 // We must therefore take care not to overwrite the samples that have
102 // yet to play.
103 frame_count -= padding;
104 } else if (padding >= 2 * frame_count) {
105 // In exclusive mode, we exchange entire buffers of size
106 // bufferFrameCount with the device. If there are already two such
107 // full buffers waiting to play, there is no work to do.
108 return false;
109 } else if (padding < frame_count) {
110 // If there is not at least one full buffer of audio queued to play in
111 // exclusive mode, call this function again immediately to try and catch
112 // up and avoid a cascade of under-runs. WASAPI doesn't seem to be smart
113 // enough to send more feed events when it gets behind.
114 refill = true;
115 }
116 MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n",
117 frame_count, padding);
118
119 double delay_us;
120 hr = get_device_delay(state, &delay_us);
121 EXIT_ON_ERROR(hr);
122 // add the buffer delay
123 delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec;
124
125 BYTE *pData;
126 hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
127 frame_count, &pData);
128 EXIT_ON_ERROR(hr);
129
130 BYTE *data[1] = {pData};
131
132 ao_read_data_converted(ao, &state->convert_format,
133 (void **)data, frame_count,
134 mp_time_us() + (int64_t)llrint(delay_us));
135
136 // note, we can't use ao_read_data return value here since we already
137 // committed to frame_count above in the GetBuffer call
138 hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
139 frame_count, 0);
140 EXIT_ON_ERROR(hr);
141
142 atomic_fetch_add(&state->sample_count, frame_count);
143
144 return refill;
145 exit_label:
146 MP_ERR(state, "Error feeding audio: %s\n", mp_HRESULT_to_str(hr));
147 MP_VERBOSE(ao, "Requesting ao reload\n");
148 ao_request_reload(ao);
149 return false;
150 }
151
thread_reset(struct ao * ao)152 static void thread_reset(struct ao *ao)
153 {
154 struct wasapi_state *state = ao->priv;
155 HRESULT hr;
156 MP_DBG(state, "Thread Reset\n");
157 hr = IAudioClient_Stop(state->pAudioClient);
158 if (FAILED(hr))
159 MP_ERR(state, "IAudioClient_Stop returned: %s\n", mp_HRESULT_to_str(hr));
160
161 hr = IAudioClient_Reset(state->pAudioClient);
162 if (FAILED(hr))
163 MP_ERR(state, "IAudioClient_Reset returned: %s\n", mp_HRESULT_to_str(hr));
164
165 atomic_store(&state->sample_count, 0);
166 }
167
thread_resume(struct ao * ao)168 static void thread_resume(struct ao *ao)
169 {
170 struct wasapi_state *state = ao->priv;
171 MP_DBG(state, "Thread Resume\n");
172 thread_reset(ao);
173 thread_feed(ao);
174
175 HRESULT hr = IAudioClient_Start(state->pAudioClient);
176 if (FAILED(hr)) {
177 MP_ERR(state, "IAudioClient_Start returned %s\n",
178 mp_HRESULT_to_str(hr));
179 }
180 }
181
thread_wakeup(void * ptr)182 static void thread_wakeup(void *ptr)
183 {
184 struct ao *ao = ptr;
185 struct wasapi_state *state = ao->priv;
186 SetEvent(state->hWake);
187 }
188
set_thread_state(struct ao * ao,enum wasapi_thread_state thread_state)189 static void set_thread_state(struct ao *ao,
190 enum wasapi_thread_state thread_state)
191 {
192 struct wasapi_state *state = ao->priv;
193 atomic_store(&state->thread_state, thread_state);
194 thread_wakeup(ao);
195 }
196
AudioThread(void * lpParameter)197 static DWORD __stdcall AudioThread(void *lpParameter)
198 {
199 struct ao *ao = lpParameter;
200 struct wasapi_state *state = ao->priv;
201 mpthread_set_name("wasapi event");
202 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
203
204 state->init_ok = wasapi_thread_init(ao);
205 SetEvent(state->hInitDone);
206 if (!state->init_ok)
207 goto exit_label;
208
209 MP_DBG(ao, "Entering dispatch loop\n");
210 while (true) {
211 if (WaitForSingleObject(state->hWake, INFINITE) != WAIT_OBJECT_0)
212 MP_ERR(ao, "Unexpected return value from WaitForSingleObject\n");
213
214 mp_dispatch_queue_process(state->dispatch, 0);
215
216 int thread_state = atomic_load(&state->thread_state);
217 switch (thread_state) {
218 case WASAPI_THREAD_FEED:
219 // fill twice on under-full buffer (see comment in thread_feed)
220 if (thread_feed(ao) && thread_feed(ao))
221 MP_ERR(ao, "Unable to fill buffer fast enough\n");
222 break;
223 case WASAPI_THREAD_RESET:
224 thread_reset(ao);
225 break;
226 case WASAPI_THREAD_RESUME:
227 thread_resume(ao);
228 break;
229 case WASAPI_THREAD_SHUTDOWN:
230 thread_reset(ao);
231 goto exit_label;
232 default:
233 MP_ERR(ao, "Unhandled thread state: %d\n", thread_state);
234 }
235 // the default is to feed unless something else is requested
236 atomic_compare_exchange_strong(&state->thread_state, &thread_state,
237 WASAPI_THREAD_FEED);
238 }
239 exit_label:
240 wasapi_thread_uninit(ao);
241
242 CoUninitialize();
243 MP_DBG(ao, "Thread return\n");
244 return 0;
245 }
246
uninit(struct ao * ao)247 static void uninit(struct ao *ao)
248 {
249 MP_DBG(ao, "Uninit wasapi\n");
250 struct wasapi_state *state = ao->priv;
251 if (state->hWake)
252 set_thread_state(ao, WASAPI_THREAD_SHUTDOWN);
253
254 if (state->hAudioThread &&
255 WaitForSingleObject(state->hAudioThread, INFINITE) != WAIT_OBJECT_0)
256 {
257 MP_ERR(ao, "Unexpected return value from WaitForSingleObject "
258 "while waiting for audio thread to terminate\n");
259 }
260
261 SAFE_DESTROY(state->hInitDone, CloseHandle(state->hInitDone));
262 SAFE_DESTROY(state->hWake, CloseHandle(state->hWake));
263 SAFE_DESTROY(state->hAudioThread,CloseHandle(state->hAudioThread));
264
265 wasapi_change_uninit(ao);
266
267 talloc_free(state->deviceID);
268
269 CoUninitialize();
270 MP_DBG(ao, "Uninit wasapi done\n");
271 }
272
init(struct ao * ao)273 static int init(struct ao *ao)
274 {
275 MP_DBG(ao, "Init wasapi\n");
276 CoInitializeEx(NULL, COINIT_MULTITHREADED);
277
278 struct wasapi_state *state = ao->priv;
279 state->log = ao->log;
280
281 state->opt_exclusive |= ao->init_flags & AO_INIT_EXCLUSIVE;
282
283 #if !HAVE_UWP
284 state->deviceID = wasapi_find_deviceID(ao);
285 if (!state->deviceID) {
286 uninit(ao);
287 return -1;
288 }
289 #endif
290
291 if (state->deviceID)
292 wasapi_change_init(ao, false);
293
294 state->hInitDone = CreateEventW(NULL, FALSE, FALSE, NULL);
295 state->hWake = CreateEventW(NULL, FALSE, FALSE, NULL);
296 if (!state->hInitDone || !state->hWake) {
297 MP_FATAL(ao, "Error creating events\n");
298 uninit(ao);
299 return -1;
300 }
301
302 state->dispatch = mp_dispatch_create(state);
303 mp_dispatch_set_wakeup_fn(state->dispatch, thread_wakeup, ao);
304
305 state->init_ok = false;
306 state->hAudioThread = CreateThread(NULL, 0, &AudioThread, ao, 0, NULL);
307 if (!state->hAudioThread) {
308 MP_FATAL(ao, "Failed to create audio thread\n");
309 uninit(ao);
310 return -1;
311 }
312
313 WaitForSingleObject(state->hInitDone, INFINITE); // wait on init complete
314 SAFE_DESTROY(state->hInitDone,CloseHandle(state->hInitDone));
315 if (!state->init_ok) {
316 if (!ao->probing)
317 MP_FATAL(ao, "Received failure from audio thread\n");
318 uninit(ao);
319 return -1;
320 }
321
322 MP_DBG(ao, "Init wasapi done\n");
323 return 0;
324 }
325
thread_control_exclusive(struct ao * ao,enum aocontrol cmd,void * arg)326 static int thread_control_exclusive(struct ao *ao, enum aocontrol cmd, void *arg)
327 {
328 struct wasapi_state *state = ao->priv;
329 if (!state->pEndpointVolume)
330 return CONTROL_UNKNOWN;
331
332 switch (cmd) {
333 case AOCONTROL_GET_VOLUME:
334 case AOCONTROL_SET_VOLUME:
335 if (!(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_VOLUME))
336 return CONTROL_FALSE;
337 break;
338 case AOCONTROL_GET_MUTE:
339 case AOCONTROL_SET_MUTE:
340 if (!(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_MUTE))
341 return CONTROL_FALSE;
342 break;
343 }
344
345 float volume;
346 BOOL mute;
347 switch (cmd) {
348 case AOCONTROL_GET_VOLUME:
349 IAudioEndpointVolume_GetMasterVolumeLevelScalar(
350 state->pEndpointVolume, &volume);
351 *(ao_control_vol_t *)arg = (ao_control_vol_t){
352 .left = 100.0f * volume,
353 .right = 100.0f * volume,
354 };
355 return CONTROL_OK;
356 case AOCONTROL_SET_VOLUME:
357 volume = ((ao_control_vol_t *)arg)->left / 100.f;
358 IAudioEndpointVolume_SetMasterVolumeLevelScalar(
359 state->pEndpointVolume, volume, NULL);
360 return CONTROL_OK;
361 case AOCONTROL_GET_MUTE:
362 IAudioEndpointVolume_GetMute(state->pEndpointVolume, &mute);
363 *(bool *)arg = mute;
364 return CONTROL_OK;
365 case AOCONTROL_SET_MUTE:
366 mute = *(bool *)arg;
367 IAudioEndpointVolume_SetMute(state->pEndpointVolume, mute, NULL);
368 return CONTROL_OK;
369 }
370 return CONTROL_UNKNOWN;
371 }
372
thread_control_shared(struct ao * ao,enum aocontrol cmd,void * arg)373 static int thread_control_shared(struct ao *ao, enum aocontrol cmd, void *arg)
374 {
375 struct wasapi_state *state = ao->priv;
376 if (!state->pAudioVolume)
377 return CONTROL_UNKNOWN;
378
379 float volume;
380 BOOL mute;
381 switch(cmd) {
382 case AOCONTROL_GET_VOLUME:
383 ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume, &volume);
384 *(ao_control_vol_t *)arg = (ao_control_vol_t){
385 .left = 100.0f * volume,
386 .right = 100.0f * volume,
387 };
388 return CONTROL_OK;
389 case AOCONTROL_SET_VOLUME:
390 volume = ((ao_control_vol_t *)arg)->left / 100.f;
391 ISimpleAudioVolume_SetMasterVolume(state->pAudioVolume, volume, NULL);
392 return CONTROL_OK;
393 case AOCONTROL_GET_MUTE:
394 ISimpleAudioVolume_GetMute(state->pAudioVolume, &mute);
395 *(bool *)arg = mute;
396 return CONTROL_OK;
397 case AOCONTROL_SET_MUTE:
398 mute = *(bool *)arg;
399 ISimpleAudioVolume_SetMute(state->pAudioVolume, mute, NULL);
400 return CONTROL_OK;
401 }
402 return CONTROL_UNKNOWN;
403 }
404
thread_control(struct ao * ao,enum aocontrol cmd,void * arg)405 static int thread_control(struct ao *ao, enum aocontrol cmd, void *arg)
406 {
407 struct wasapi_state *state = ao->priv;
408
409 // common to exclusive and shared
410 switch (cmd) {
411 case AOCONTROL_UPDATE_STREAM_TITLE:
412 if (!state->pSessionControl)
413 return CONTROL_FALSE;
414
415 wchar_t *title = mp_from_utf8(NULL, (char*)arg);
416 wchar_t *tmp = NULL;
417 // There is a weird race condition in the IAudioSessionControl itself --
418 // it seems that *sometimes* the SetDisplayName does not take effect and
419 // it still shows the old title. Use this loop to insist until it works.
420 do {
421 IAudioSessionControl_SetDisplayName(state->pSessionControl, title, NULL);
422
423 SAFE_DESTROY(tmp, CoTaskMemFree(tmp));
424 IAudioSessionControl_GetDisplayName(state->pSessionControl, &tmp);
425 } while (wcscmp(title, tmp));
426 SAFE_DESTROY(tmp, CoTaskMemFree(tmp));
427 talloc_free(title);
428 return CONTROL_OK;
429 }
430
431 return state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE ?
432 thread_control_exclusive(ao, cmd, arg) :
433 thread_control_shared(ao, cmd, arg);
434 }
435
run_control(void * p)436 static void run_control(void *p)
437 {
438 void **pp = p;
439 struct ao *ao = pp[0];
440 enum aocontrol cmd = *(enum aocontrol *)pp[1];
441 void *arg = pp[2];
442 *(int *)pp[3] = thread_control(ao, cmd, arg);
443 }
444
control(struct ao * ao,enum aocontrol cmd,void * arg)445 static int control(struct ao *ao, enum aocontrol cmd, void *arg)
446 {
447 struct wasapi_state *state = ao->priv;
448 int ret;
449 void *p[] = {ao, &cmd, arg, &ret};
450 mp_dispatch_run(state->dispatch, run_control, p);
451 return ret;
452 }
453
audio_reset(struct ao * ao)454 static void audio_reset(struct ao *ao)
455 {
456 set_thread_state(ao, WASAPI_THREAD_RESET);
457 }
458
audio_resume(struct ao * ao)459 static void audio_resume(struct ao *ao)
460 {
461 set_thread_state(ao, WASAPI_THREAD_RESUME);
462 }
463
hotplug_uninit(struct ao * ao)464 static void hotplug_uninit(struct ao *ao)
465 {
466 MP_DBG(ao, "Hotplug uninit\n");
467 wasapi_change_uninit(ao);
468 CoUninitialize();
469 }
470
hotplug_init(struct ao * ao)471 static int hotplug_init(struct ao *ao)
472 {
473 MP_DBG(ao, "Hotplug init\n");
474 struct wasapi_state *state = ao->priv;
475 state->log = ao->log;
476 CoInitializeEx(NULL, COINIT_MULTITHREADED);
477 HRESULT hr = wasapi_change_init(ao, true);
478 EXIT_ON_ERROR(hr);
479
480 return 0;
481 exit_label:
482 MP_FATAL(state, "Error setting up audio hotplug: %s\n", mp_HRESULT_to_str(hr));
483 hotplug_uninit(ao);
484 return -1;
485 }
486
487 #define OPT_BASE_STRUCT struct wasapi_state
488
489 const struct ao_driver audio_out_wasapi = {
490 .description = "Windows WASAPI audio output (event mode)",
491 .name = "wasapi",
492 .init = init,
493 .uninit = uninit,
494 .control = control,
495 .reset = audio_reset,
496 .start = audio_resume,
497 .list_devs = wasapi_list_devs,
498 .hotplug_init = hotplug_init,
499 .hotplug_uninit = hotplug_uninit,
500 .priv_size = sizeof(wasapi_state),
501 };
502