summaryrefslogtreecommitdiffstats
path: root/audio/out/ao_wasapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out/ao_wasapi.c')
-rw-r--r--audio/out/ao_wasapi.c268
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),