diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index c86e7311d7..3c0bab3231 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -126,9 +126,12 @@ static void thread_resume(struct ao *ao) /* Fill the buffer before starting, but only if there is no audio queued to play. */ /* This prevents overfilling the buffer, which leads to problems in exclusive mode */ - if (padding < (UINT32)state->bufferFrameCount) + if (padding < (UINT32) state->bufferFrameCount) thread_feed(ao); + // start feeding next wakeup if something else hasn't been requested + int expected = WASAPI_THREAD_RESUME; + atomic_compare_exchange_strong(&state->thread_state, &expected, WASAPI_THREAD_FEED); hr = IAudioClient_Start(state->pAudioClient); if (hr != S_OK) MP_ERR(state, "IAudioClient_Start returned %s\n", mp_HRESULT_to_str(hr)); @@ -152,46 +155,55 @@ static void thread_reset(struct ao *ao) MP_ERR(state, "IAudioClient_Reset returned: %s\n", mp_HRESULT_to_str(hr)); atomic_store(&state->sample_count, 0); + // start feeding next wakeup if something else hasn't been requested + int expected = WASAPI_THREAD_RESET; + atomic_compare_exchange_strong(&state->thread_state, &expected, WASAPI_THREAD_FEED); return; } -static DWORD __stdcall ThreadLoop(void *lpParameter) +static DWORD __stdcall AudioThread(void *lpParameter) { struct ao *ao = lpParameter; struct wasapi_state *state = ao->priv; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); state->init_ret = wasapi_thread_init(ao); - SetEvent(state->init_done); + SetEvent(state->hInitDone); if (state->init_ret != S_OK) goto exit_label; - DWORD waitstatus; - HANDLE playcontrol[] = - {state->hUninit, state->hFeed, state->hReset, state->hResume, NULL}; MP_DBG(ao, "Entering dispatch loop\n"); while (true) { /* watch events */ - waitstatus = MsgWaitForMultipleObjects(4, playcontrol, FALSE, INFINITE, - QS_POSTMESSAGE | QS_SENDMESSAGE); - switch (waitstatus) { - case WAIT_OBJECT_0: /*shutdown*/ - MP_DBG(ao, "Thread shutdown\n"); - goto exit_label; - case (WAIT_OBJECT_0 + 1): /* feed */ - thread_feed(ao); + HANDLE events[] = {state->hWake}; + switch (MsgWaitForMultipleObjects(MP_ARRAY_SIZE(events), events, FALSE, INFINITE, + QS_POSTMESSAGE | QS_SENDMESSAGE)) { + /* AudioThread wakeup */ + case WAIT_OBJECT_0: + switch (atomic_load(&state->thread_state)) { + case WASAPI_THREAD_FEED: + thread_feed(ao); + break; + case WASAPI_THREAD_RESET: + thread_reset(ao); + break; + case WASAPI_THREAD_RESUME: + thread_reset(ao); + thread_resume(ao); + break; + case WASAPI_THREAD_SHUTDOWN: + thread_reset(ao); + goto exit_label; + default: + MP_ERR(ao, "Unhandled thread state\n"); + goto exit_label; + } break; - case (WAIT_OBJECT_0 + 2): /* reset */ - thread_reset(ao); - break; - case (WAIT_OBJECT_0 + 3): /* resume */ - thread_resume(ao); - break; - case (WAIT_OBJECT_0 + 4): /* messages to dispatch (COM marshalling) */ - MP_DBG(ao, "Dispatch\n"); - wasapi_dispatch(); + /* messages to dispatch (COM marshalling) */ + case (WAIT_OBJECT_0 + MP_ARRAY_SIZE(events)): + wasapi_dispatch(ao); break; default: - MP_ERR(ao, "Unhandled case in thread loop\n"); + MP_ERR(ao, "Unhandled thread event\n"); goto exit_label; } } @@ -203,15 +215,11 @@ exit_label: return 0; } -static void closehandles(struct ao *ao) +static void set_thread_state(struct ao *ao, enum wasapi_thread_state thread_state) { struct wasapi_state *state = ao->priv; - if (state->init_done) CloseHandle(state->init_done); - if (state->hUninit) CloseHandle(state->hUninit); - if (state->hFeed) CloseHandle(state->hFeed); - if (state->hResume) CloseHandle(state->hResume); - if (state->hReset) CloseHandle(state->hReset); - if (state->threadLoop) CloseHandle(state->threadLoop); + atomic_store(&state->thread_state, thread_state); + SetEvent(state->hWake); } static void uninit(struct ao *ao) @@ -219,16 +227,23 @@ static void uninit(struct ao *ao) MP_DBG(ao, "Uninit wasapi\n"); struct wasapi_state *state = ao->priv; wasapi_release_proxies(state); - if (state->hUninit) - SetEvent(state->hUninit); + if (state->hWake) + set_thread_state(ao, WASAPI_THREAD_SHUTDOWN); + /* wait up to 10 seconds */ - if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT) { + if (state->hAudioThread && + WaitForSingleObject(state->hAudioThread, 10000) == WAIT_TIMEOUT) + { MP_ERR(ao, "Audio loop thread refuses to abort\n"); return; } if (state->VistaBlob.hAvrt) FreeLibrary(state->VistaBlob.hAvrt); - closehandles(ao); + + SAFE_RELEASE(state->hInitDone, CloseHandle(state->hInitDone)); + SAFE_RELEASE(state->hWake, CloseHandle(state->hWake)); + SAFE_RELEASE(state->hAudioThread,CloseHandle(state->hAudioThread)); + CoUninitialize(); MP_DBG(ao, "Uninit wasapi done\n"); } @@ -243,30 +258,24 @@ static int init(struct ao *ao) if(!wasapi_fill_VistaBlob(state)) MP_WARN(ao, "Error loading thread priority functions\n"); - state->init_done = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hFeed = CreateEventW(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */ - state->hResume = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hReset = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!state->init_done || !state->hFeed || !state->hUninit || - !state->hResume || !state->hReset) - { - MP_ERR(ao, "Error initing events\n"); + state->hInitDone = CreateEventW(NULL, FALSE, FALSE, NULL); + state->hWake = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!state->hInitDone || !state->hWake) { + MP_ERR(ao, "Error creating events\n"); uninit(ao); - /* failed to init events */ return -1; } state->init_ret = E_FAIL; - state->threadLoop = CreateThread(NULL, 0, &ThreadLoop, ao, 0, NULL); - if (!state->threadLoop) { - /* failed to init thread */ - MP_ERR(ao, "Failed to create thread\n"); + state->hAudioThread = CreateThread(NULL, 0, &AudioThread, ao, 0, NULL); + if (!state->hAudioThread) { + MP_ERR(ao, "Failed to create audio thread\n"); uninit(ao); return -1; } - WaitForSingleObject(state->init_done, INFINITE); /* wait on init complete */ + WaitForSingleObject(state->hInitDone, INFINITE); /* wait on init complete */ + SAFE_RELEASE(state->hInitDone,CloseHandle(state->hInitDone)); if (state->init_ret != S_OK) { if (!ao->probing) MP_ERR(ao, "Received failure from audio thread\n"); @@ -360,14 +369,12 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) static void audio_reset(struct ao *ao) { - struct wasapi_state *state = ao->priv; - SetEvent(state->hReset); + set_thread_state(ao, WASAPI_THREAD_RESET); } static void audio_resume(struct ao *ao) { - struct wasapi_state *state = ao->priv; - SetEvent(state->hResume); + set_thread_state(ao, WASAPI_THREAD_RESUME); } static void hotplug_uninit(struct ao *ao) diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h index eb06e46566..24b9b862ed 100755 --- a/audio/out/ao_wasapi.h +++ b/audio/out/ao_wasapi.h @@ -43,11 +43,18 @@ void wasapi_change_uninit(struct ao* ao); #define SAFE_RELEASE(unk, release) \ do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0) +enum wasapi_thread_state { + WASAPI_THREAD_FEED = 0, + WASAPI_THREAD_RESUME, + WASAPI_THREAD_RESET, + WASAPI_THREAD_SHUTDOWN +}; + typedef struct wasapi_state { struct mp_log *log; /* Init phase */ HRESULT init_ret; - HANDLE init_done; + HANDLE hInitDone; int share_mode; /* volume control */ @@ -66,11 +73,9 @@ typedef struct wasapi_state { IMMDeviceEnumerator *pEnumerator; /* thread handles */ - HANDLE threadLoop; /* the thread itself */ - HANDLE hUninit; /* thread shutdown */ - HANDLE hFeed; /* wasapi event */ - HANDLE hResume; /* signal audio thread to resume the stream */ - HANDLE hReset; /* signal audio thread to reset the stream */ + HANDLE hAudioThread; /* the thread itself */ + HANDLE hWake; /* thread wakeup event */ + atomic_int thread_state; /* enum wasapi_thread_state */ /* for setting the audio thread priority */ HANDLE hTask; /* AV thread */ diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c index dfe76918f9..b0b0c10575 100755 --- a/audio/out/ao_wasapi_utils.c +++ b/audio/out/ao_wasapi_utils.c @@ -697,7 +697,7 @@ reinit: EXIT_ON_ERROR(hr); MP_DBG(state, "IAudioClient::Initialize IAudioClient_SetEventHandle\n"); - hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hFeed); + hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hWake); EXIT_ON_ERROR(hr); MP_DBG(state, "IAudioClient::Initialize IAudioClient_GetBufferSize\n"); @@ -1054,8 +1054,9 @@ static void destroy_proxies(struct wasapi_state *state) { SAFE_RELEASE(state->sSessionControl, IUnknown_Release(state->sSessionControl)); } -void wasapi_dispatch(void) +void wasapi_dispatch(struct ao *ao) { + MP_DBG(ao, "Dispatch\n"); /* dispatch any possible pending messages */ MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) @@ -1154,8 +1155,8 @@ exit_label: void wasapi_thread_uninit(struct ao *ao) { struct wasapi_state *state = ao->priv; - - wasapi_dispatch(); + MP_DBG(ao, "Thread shutdown\n"); + wasapi_dispatch(ao); if (state->pAudioClient) IAudioClient_Stop(state->pAudioClient); diff --git a/audio/out/ao_wasapi_utils.h b/audio/out/ao_wasapi_utils.h index 330a502046..5ac2d3efba 100755 --- a/audio/out/ao_wasapi_utils.h +++ b/audio/out/ao_wasapi_utils.h @@ -38,7 +38,7 @@ bool wasapi_fill_VistaBlob(wasapi_state *state); void wasapi_list_devs(struct ao *ao, struct ao_device_list *list); -void wasapi_dispatch(void); +void wasapi_dispatch(struct ao *ao); HRESULT wasapi_thread_init(struct ao *ao); void wasapi_thread_uninit(struct ao *ao);