diff options
Diffstat (limited to 'audio/out/ao_wasapi.c')
-rw-r--r-- | audio/out/ao_wasapi.c | 268 |
1 files changed, 54 insertions, 214 deletions
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index 79f8dcf665..a496ff9aa6 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -32,13 +32,7 @@ #include "audio/out/ao_wasapi.h" #include "audio/out/ao_wasapi_utils.h" -#include "options/m_option.h" -#include "options/m_config.h" #include "audio/format.h" -#include "common/msg.h" -#include "misc/ring.h" -#include "ao.h" -#include "internal.h" #include "compat/atomics.h" #include "osdep/timer.h" @@ -47,61 +41,6 @@ #define SAFE_RELEASE(unk, release) \ do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0) -static int thread_init(struct ao *ao) -{ - struct wasapi_state *state = (struct wasapi_state *)ao->priv; - HRESULT hr; - CoInitialize(NULL); - - if (!state->opt_device) { - IMMDeviceEnumerator *pEnumerator; - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void**)&pEnumerator); - EXIT_ON_ERROR(hr); - - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, - eRender, eConsole, - &state->pDevice); - SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator)); - - char *id = wasapi_get_device_id(state->pDevice); - MP_VERBOSE(ao, "default device ID: %s\n", id); - free(id); - } else { - hr = wasapi_find_and_load_device(ao, &state->pDevice, state->opt_device); - } - EXIT_ON_ERROR(hr); - - char *name = wasapi_get_device_name(state->pDevice); - MP_VERBOSE(ao, "device loaded: %s\n", name); - free(name); - - hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient, - CLSCTX_ALL, NULL, (void **)&state->pAudioClient); - EXIT_ON_ERROR(hr); - - hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioEndpointVolume, - CLSCTX_ALL, NULL, - (void **)&state->pEndpointVolume); - EXIT_ON_ERROR(hr); - IAudioEndpointVolume_QueryHardwareSupport(state->pEndpointVolume, - &state->vol_hw_support); - - state->init_ret = wasapi_find_formats(ao); /* Probe support formats */ - if (state->init_ret) - goto exit_label; - if (!wasapi_fix_format(state)) { /* now that we're sure what format to use */ - MP_VERBOSE(ao, "thread_init OK!\n"); - SetEvent(state->init_done); - return state->init_ret; - } -exit_label: - state->init_ret = -1; - SetEvent(state->init_done); - return -1; -} - - static double get_device_delay(struct wasapi_state *state) { UINT64 sample_count = state->sample_count; UINT64 position, qpc_position; @@ -160,7 +99,8 @@ static void thread_feed(struct ao *ao) frame_count, 0); EXIT_ON_ERROR(hr); - state->sample_count += frame_count; + mp_atomic_add_and_fetch(&state->sample_count, frame_count); + mp_memory_barrier(); return; exit_label: @@ -168,131 +108,48 @@ exit_label: return; } -static void thread_pause(struct ao *ao) -{ - struct wasapi_state *state = (struct wasapi_state *)ao->priv; - - state->is_playing = 0; - IAudioClient_Stop(state->pAudioClient); - IAudioClient_Reset(state->pAudioClient); - state->sample_count = 0; - mp_memory_barrier(); -} - -static void thread_resume(struct ao *ao) -{ - struct wasapi_state *state = (struct wasapi_state *)ao->priv; - - state->is_playing = 1; - thread_feed(ao); - IAudioClient_Start(state->pAudioClient); -} - -static void thread_reset(struct ao *ao) -{ - thread_pause(ao); -} - -static void thread_getVol(wasapi_state *state) -{ - IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume, - &state->audio_volume); - SetEvent(state->hDoneVol); -} - -static void thread_setVol(wasapi_state *state) -{ - IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolume, - state->audio_volume, NULL); - SetEvent(state->hDoneVol); -} - -static void thread_uninit(wasapi_state *state) -{ - if (state->pAudioClient) - IAudioClient_Stop(state->pAudioClient); - if (state->pRenderClient) - IAudioRenderClient_Release(state->pRenderClient); - if (state->pAudioClock) - IAudioClock_Release(state->pAudioClock); - if (state->pAudioClient) - IAudioClient_Release(state->pAudioClient); - if (state->pDevice) - IMMDevice_Release(state->pDevice); - if (state->hTask) - state->VistaBlob.pAvRevertMmThreadCharacteristics(state->hTask); - CoUninitialize(); - ExitThread(0); -} - -static void audio_drain(struct ao *ao) -{ - struct wasapi_state *state = (struct wasapi_state *)ao->priv; - while (1) { - if (WaitForSingleObject(state->hFeed,2000) == WAIT_OBJECT_0 && - ao->api->get_delay(ao)) - { - thread_feed(ao); - } else - break; - } -} - static DWORD __stdcall ThreadLoop(void *lpParameter) { struct ao *ao = lpParameter; - int feedwatch = 0; if (!ao || !ao->priv) return -1; struct wasapi_state *state = (struct wasapi_state *)ao->priv; - if (thread_init(ao)) + if (wasapi_thread_init(ao)) goto exit_label; + MSG msg; DWORD waitstatus = WAIT_FAILED; HANDLE playcontrol[] = - {state->hUninit, state->hPause, state->hReset, state->hGetvol, - state->hSetvol, state->hPlay, state->hFeed, NULL}; + {state->hUninit, state->hFeed, state->hForceFeed, NULL}; MP_VERBOSE(ao, "Entering dispatch loop!\n"); - while (1) { /* watch events, poll at least every 2 seconds */ - waitstatus = WaitForMultipleObjects(7, playcontrol, FALSE, 2000); + while (1) { /* watch events */ + waitstatus = MsgWaitForMultipleObjects(3, playcontrol, FALSE, INFINITE, + QS_POSTMESSAGE | QS_SENDMESSAGE); switch (waitstatus) { case WAIT_OBJECT_0: /*shutdown*/ - feedwatch = 0; - thread_uninit(state); + wasapi_thread_uninit(state); goto exit_label; - case (WAIT_OBJECT_0 + 1): /* pause */ - feedwatch = 0; - thread_pause(ao); - break; - case (WAIT_OBJECT_0 + 2): /* reset */ - feedwatch = 0; - thread_reset(ao); - break; - case (WAIT_OBJECT_0 + 3): /* getVolume */ - thread_getVol(state); - break; - case (WAIT_OBJECT_0 + 4): /* setVolume */ - thread_setVol(state); - break; - case (WAIT_OBJECT_0 + 5): /* play */ - feedwatch = 0; - thread_resume(ao); + case (WAIT_OBJECT_0 + 1): /* feed */ + thread_feed(ao); break; - case (WAIT_OBJECT_0 + 6): /* feed */ - if (state->is_playing) - feedwatch = 1; + case (WAIT_OBJECT_0 + 2): /* force feed */ thread_feed(ao); + SetEvent(state->hFeedDone); break; - case WAIT_TIMEOUT: /* Did our feed die? */ - if (feedwatch) - return -1; + case (WAIT_OBJECT_0 + 3): /* messages to dispatch (COM marshalling) */ + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + DispatchMessage(&msg); + } break; - default: case WAIT_FAILED: /* ??? */ return -1; } } exit_label: + /* dispatch any possible pending messages */ + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + DispatchMessage(&msg); + } return state->init_ret; } @@ -301,33 +158,21 @@ static void closehandles(struct ao *ao) struct wasapi_state *state = (struct wasapi_state *)ao->priv; if (state->init_done) CloseHandle(state->init_done); - if (state->hPlay) - CloseHandle(state->hPlay); - if (state->hPause) - CloseHandle(state->hPause); - if (state->hReset) - CloseHandle(state->hReset); if (state->hUninit) CloseHandle(state->hUninit); if (state->hFeed) CloseHandle(state->hFeed); - if (state->hGetvol) - CloseHandle(state->hGetvol); - if (state->hSetvol) - CloseHandle(state->hSetvol); - if (state->hDoneVol) - CloseHandle(state->hDoneVol); } static void uninit(struct ao *ao) { MP_VERBOSE(ao, "uninit!\n"); struct wasapi_state *state = (struct wasapi_state *)ao->priv; - state->immed = 1; + wasapi_release_proxies(state); SetEvent(state->hUninit); /* wait up to 10 seconds */ if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT) - SetEvent(state->fatal_error); + MP_ERR(ao, "audio loop thread refuses to abort!"); if (state->VistaBlob.hAvrt) FreeLibrary(state->VistaBlob.hAvrt); closehandles(ao); @@ -357,18 +202,12 @@ static int init(struct ao *ao) } state->init_done = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hPlay = CreateEventW(NULL, FALSE, FALSE, NULL); /* kick start audio feed */ - state->hPause = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hReset = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hGetvol = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hSetvol = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hDoneVol = CreateEventW(NULL, FALSE, FALSE, NULL); state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL); - state->fatal_error = CreateEventW(NULL, TRUE, FALSE, NULL); - state->hFeed = CreateEvent(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */ - if (!state->init_done || !state->fatal_error || !state->hPlay || - !state->hPause || !state->hFeed || !state->hReset || !state->hGetvol || - !state->hSetvol || !state->hDoneVol) + state->hFeed = CreateEventW(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */ + state->hForceFeed = CreateEventW(NULL, FALSE, FALSE, NULL); + state->hFeedDone = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!state->init_done || !state->hFeed || !state->hUninit || + !state->hForceFeed || !state->hFeedDone) { closehandles(ao); /* failed to init events */ @@ -388,57 +227,59 @@ static int init(struct ao *ao) } } else MP_VERBOSE(ao, "Init Done!\n"); + + wasapi_setup_proxies(state); return state->init_ret; } static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; - if (!(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_VOLUME)) + if (!state->share_mode && !(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_VOLUME)) { return CONTROL_UNKNOWN; /* hw does not support volume controls in exclusive mode */ + } ao_control_vol_t *vol = (ao_control_vol_t *)arg; - ResetEvent(state->hDoneVol); switch (cmd) { case AOCONTROL_GET_VOLUME: - SetEvent(state->hGetvol); - if (WaitForSingleObject(state->hDoneVol, 100) == WAIT_OBJECT_0) { - vol->left = vol->right = 100.0f * state->audio_volume; - return CONTROL_OK; - } - return CONTROL_UNKNOWN; + IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolumeProxy, + &state->audio_volume); + vol->left = vol->right = 100.0f * state->audio_volume; + return CONTROL_OK; case AOCONTROL_SET_VOLUME: state->audio_volume = vol->left / 100.f; - SetEvent(state->hSetvol); - if (WaitForSingleObject(state->hDoneVol, 100) == WAIT_OBJECT_0) - return CONTROL_OK; - return CONTROL_UNKNOWN; + IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolumeProxy, + state->audio_volume, NULL); + return CONTROL_OK; default: return CONTROL_UNKNOWN; } } -static void audio_resume(struct ao *ao) + +static void audio_pause(struct ao *ao) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; - ResetEvent(state->hPause); - ResetEvent(state->hReset); - SetEvent(state->hPlay); + + IAudioClient_Stop(state->pAudioClientProxy); + IAudioClient_Reset(state->pAudioClientProxy); + state->sample_count = 0; + mp_memory_barrier(); } -static void audio_pause(struct ao *ao) +static void audio_resume(struct ao *ao) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; - ResetEvent(state->hPlay); - SetEvent(state->hPause); + + SetEvent(state->hForceFeed); + WaitForSingleObject(state->hFeedDone, INFINITE); + IAudioClient_Start(state->pAudioClientProxy); } -static void reset(struct ao *ao) +static void audio_reset(struct ao *ao) { - struct wasapi_state *state = (struct wasapi_state *)ao->priv; - ResetEvent(state->hPlay); - SetEvent(state->hReset); + audio_pause(ao); } #define OPT_BASE_STRUCT struct wasapi_state @@ -451,8 +292,7 @@ const struct ao_driver audio_out_wasapi = { .control = control, .pause = audio_pause, .resume = audio_resume, - .reset = reset, - .drain = audio_drain, + .reset = audio_reset, .priv_size = sizeof(wasapi_state), .options = (const struct m_option[]) { OPT_FLAG("exclusive", opt_exclusive, 0), |