From c52833bf16c9f4cc2bb676d13df86ef6be30f434 Mon Sep 17 00:00:00 2001 From: Kevin Mitchell Date: Sun, 22 Feb 2015 18:27:58 -0800 Subject: [PATCH] ao/wasapi: move resume to audio thread This echanges the two events hForceFeed/hFeedDone for hResume. This like the last commit makes things more deterministic. Importantly, the forcefeed is only done if there is not already a full buffer yet to be played by the device. This should fix some of the problems with exclusive mode. This commit also removes the necessity to have a proxy to the AudioClient object in the main thread. fixes #1529 --- audio/out/ao_wasapi.c | 53 ++++++++++++++++++++++++++----------- audio/out/ao_wasapi.h | 5 +--- audio/out/ao_wasapi_utils.c | 4 --- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c index 89f51c4295..75318c22ee 100644 --- a/audio/out/ao_wasapi.c +++ b/audio/out/ao_wasapi.c @@ -113,6 +113,33 @@ exit_label: return; } +static void thread_resume(struct ao *ao) +{ + struct wasapi_state *state = (struct wasapi_state *)ao->priv; + HRESULT hr; + + MP_DBG(state, "Thread Resume\n"); + UINT32 padding = 0; + hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding); + if (hr != S_OK) { + MP_ERR(state, "IAudioClient_GetCurrentPadding returned %s (0x%"PRIx32")\n", + wasapi_explain_err(hr), (uint32_t) hr); + } + + /* 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) + thread_feed(ao); + + hr = IAudioClient_Start(state->pAudioClient); + if (hr != S_OK) { + MP_ERR(state, "IAudioClient_Start returned %s (0x%"PRIx32")\n", + wasapi_explain_err(hr), (uint32_t) hr); + } + + return; +} + static void thread_reset(struct ao *ao) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; @@ -151,7 +178,7 @@ static DWORD __stdcall ThreadLoop(void *lpParameter) DWORD waitstatus; HANDLE playcontrol[] = - {state->hUninit, state->hFeed, state->hForceFeed, state->hReset, NULL}; + {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, @@ -163,13 +190,12 @@ static DWORD __stdcall ThreadLoop(void *lpParameter) case (WAIT_OBJECT_0 + 1): /* feed */ thread_feed(ao); break; - case (WAIT_OBJECT_0 + 2): /* force feed */ - thread_feed(ao); - SetEvent(state->hFeedDone); - break; - case (WAIT_OBJECT_0 + 3): /* reset */ + case (WAIT_OBJECT_0 + 2): /* reset */ thread_reset(ao); - break; + 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(); @@ -193,8 +219,7 @@ static void closehandles(struct ao *ao) if (state->init_done) CloseHandle(state->init_done); if (state->hUninit) CloseHandle(state->hUninit); if (state->hFeed) CloseHandle(state->hFeed); - if (state->hForceFeed) CloseHandle(state->hForceFeed); - if (state->hFeedDone) CloseHandle(state->hFeedDone); + if (state->hResume) CloseHandle(state->hResume); if (state->hReset) CloseHandle(state->hReset); if (state->threadLoop) CloseHandle(state->threadLoop); } @@ -249,11 +274,10 @@ static int init(struct ao *ao) 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->hForceFeed = CreateEventW(NULL, FALSE, FALSE, NULL); - state->hFeedDone = CreateEventW(NULL, FALSE, FALSE, NULL); + state->hResume = CreateEventW(NULL, FALSE, FALSE, NULL); state->hReset = CreateEventW(NULL, FALSE, FALSE, NULL); if (!state->init_done || !state->hFeed || !state->hUninit || - !state->hForceFeed || !state->hFeedDone || !state->hReset) + !state->hResume || !state->hReset) { MP_ERR(ao, "Error initing events\n"); uninit(ao); @@ -371,10 +395,7 @@ static void audio_reset(struct ao *ao) static void audio_resume(struct ao *ao) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; - - SetEvent(state->hForceFeed); - WaitForSingleObject(state->hFeedDone, INFINITE); - IAudioClient_Start(state->pAudioClientProxy); + SetEvent(state->hResume); } static void list_devs(struct ao *ao, struct ao_device_list *list) diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h index 4923229a4b..2fd266eab0 100755 --- a/audio/out/ao_wasapi.h +++ b/audio/out/ao_wasapi.h @@ -72,8 +72,7 @@ typedef struct wasapi_state { IMMDeviceEnumerator *pEnumerator; HANDLE hFeed; /* wasapi event */ - HANDLE hForceFeed; /* forces writing a buffer (e.g. before audio_resume) */ - HANDLE hFeedDone; /* set only after a hForceFeed */ + HANDLE hResume; /* signal audio thread to resume the stream */ HANDLE hReset; /* signal audio thread to reset the stream */ HANDLE hTask; /* AV thread */ DWORD taskIndex; /* AV task ID */ @@ -81,7 +80,6 @@ typedef struct wasapi_state { /* WASAPI proxy handles, for Single-Threaded Apartment communication. One is needed for each object that's accessed by a different thread. */ - IAudioClient *pAudioClientProxy; ISimpleAudioVolume *pAudioVolumeProxy; IAudioEndpointVolume *pEndpointVolumeProxy; IAudioSessionControl *pSessionControlProxy; @@ -89,7 +87,6 @@ typedef struct wasapi_state { /* Streams used to marshal the proxy objects. The thread owning the actual objects needs to marshal proxy objects into these streams, and the thread that wants the proxies unmarshals them from here. */ - IStream *sAudioClient; IStream *sAudioVolume; IStream *sEndpointVolume; IStream *sSessionControl; diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c index 08fdd14930..13bc0fac6d 100755 --- a/audio/out/ao_wasapi_utils.c +++ b/audio/out/ao_wasapi_utils.c @@ -958,7 +958,6 @@ HRESULT wasapi_setup_proxies(struct wasapi_state *state) { EXIT_ON_ERROR(hr); \ } while (0) - UNMARSHAL(IID_IAudioClient, state->pAudioClientProxy, state->sAudioClient); UNMARSHAL(IID_ISimpleAudioVolume, state->pAudioVolumeProxy, state->sAudioVolume); UNMARSHAL(IID_IAudioEndpointVolume, state->pEndpointVolumeProxy, state->sEndpointVolume); UNMARSHAL(IID_IAudioSessionControl, state->pSessionControlProxy, state->sSessionControl); @@ -973,7 +972,6 @@ exit_label: } void wasapi_release_proxies(wasapi_state *state) { - SAFE_RELEASE(state->pAudioClientProxy, IUnknown_Release(state->pAudioClientProxy)); SAFE_RELEASE(state->pAudioVolumeProxy, IUnknown_Release(state->pAudioVolumeProxy)); SAFE_RELEASE(state->pEndpointVolumeProxy, IUnknown_Release(state->pEndpointVolumeProxy)); SAFE_RELEASE(state->pSessionControlProxy, IUnknown_Release(state->pSessionControlProxy)); @@ -991,7 +989,6 @@ static HRESULT create_proxies(struct wasapi_state *state) { EXIT_ON_ERROR(hr); \ } while (0) - MARSHAL(IID_IAudioClient, state->sAudioClient, state->pAudioClient); MARSHAL(IID_ISimpleAudioVolume, state->sAudioVolume, state->pAudioVolume); MARSHAL(IID_IAudioEndpointVolume, state->sEndpointVolume, state->pEndpointVolume); MARSHAL(IID_IAudioSessionControl, state->sSessionControl, state->pSessionControl); @@ -1004,7 +1001,6 @@ exit_label: } static void destroy_proxies(struct wasapi_state *state) { - SAFE_RELEASE(state->sAudioClient, IUnknown_Release(state->sAudioClient)); SAFE_RELEASE(state->sAudioVolume, IUnknown_Release(state->sAudioVolume)); SAFE_RELEASE(state->sEndpointVolume, IUnknown_Release(state->sEndpointVolume)); SAFE_RELEASE(state->sSessionControl, IUnknown_Release(state->sSessionControl));