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