summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Mitchell <kevmitch@gmail.com>2015-03-28 17:12:48 -0700
committerKevin Mitchell <kevmitch@gmail.com>2015-04-04 16:31:14 -0700
commit642f84f9227607f0c64bf35ec5896cb80804f64f (patch)
tree6c21e7b59235686b5284ec2ad47858eb6b6ce5bf
parentfe60cff03bbdc0790f96365d6cf3ab8116785bb0 (diff)
downloadmpv-642f84f9227607f0c64bf35ec5896cb80804f64f.tar.bz2
mpv-642f84f9227607f0c64bf35ec5896cb80804f64f.tar.xz
ao/wasapi: use atomic state variable instead of different events
Unfortunately, because we have proxy objects (pAudioVolumeProxy, pEndpointVolumeProxy, pSessionControlProxy) it looks like we still have to use MsgWaitForMultipleObjects and watch for and dispatch pending messages: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680112%28v=vs.85%29.aspx
-rw-r--r--audio/out/ao_wasapi.c115
-rwxr-xr-xaudio/out/ao_wasapi.h17
-rwxr-xr-xaudio/out/ao_wasapi_utils.c9
-rwxr-xr-xaudio/out/ao_wasapi_utils.h2
4 files changed, 78 insertions, 65 deletions
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);
- break;
- case (WAIT_OBJECT_0 + 2): /* reset */
- thread_reset(ao);
- break;
- case (WAIT_OBJECT_0 + 3): /* resume */
- thread_resume(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 + 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);