mirror of https://github.com/mpv-player/mpv
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
This commit is contained in:
parent
446fd5a43a
commit
c52833bf16
|
@ -113,6 +113,33 @@ exit_label:
|
||||||
return;
|
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)
|
static void thread_reset(struct ao *ao)
|
||||||
{
|
{
|
||||||
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
|
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
|
||||||
|
@ -151,7 +178,7 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
|
||||||
|
|
||||||
DWORD waitstatus;
|
DWORD waitstatus;
|
||||||
HANDLE playcontrol[] =
|
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");
|
MP_DBG(ao, "Entering dispatch loop\n");
|
||||||
while (true) { /* watch events */
|
while (true) { /* watch events */
|
||||||
waitstatus = MsgWaitForMultipleObjects(4, playcontrol, FALSE, INFINITE,
|
waitstatus = MsgWaitForMultipleObjects(4, playcontrol, FALSE, INFINITE,
|
||||||
|
@ -163,13 +190,12 @@ static DWORD __stdcall ThreadLoop(void *lpParameter)
|
||||||
case (WAIT_OBJECT_0 + 1): /* feed */
|
case (WAIT_OBJECT_0 + 1): /* feed */
|
||||||
thread_feed(ao);
|
thread_feed(ao);
|
||||||
break;
|
break;
|
||||||
case (WAIT_OBJECT_0 + 2): /* force feed */
|
case (WAIT_OBJECT_0 + 2): /* reset */
|
||||||
thread_feed(ao);
|
|
||||||
SetEvent(state->hFeedDone);
|
|
||||||
break;
|
|
||||||
case (WAIT_OBJECT_0 + 3): /* reset */
|
|
||||||
thread_reset(ao);
|
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) */
|
case (WAIT_OBJECT_0 + 4): /* messages to dispatch (COM marshalling) */
|
||||||
MP_DBG(ao, "Dispatch\n");
|
MP_DBG(ao, "Dispatch\n");
|
||||||
wasapi_dispatch();
|
wasapi_dispatch();
|
||||||
|
@ -193,8 +219,7 @@ static void closehandles(struct ao *ao)
|
||||||
if (state->init_done) CloseHandle(state->init_done);
|
if (state->init_done) CloseHandle(state->init_done);
|
||||||
if (state->hUninit) CloseHandle(state->hUninit);
|
if (state->hUninit) CloseHandle(state->hUninit);
|
||||||
if (state->hFeed) CloseHandle(state->hFeed);
|
if (state->hFeed) CloseHandle(state->hFeed);
|
||||||
if (state->hForceFeed) CloseHandle(state->hForceFeed);
|
if (state->hResume) CloseHandle(state->hResume);
|
||||||
if (state->hFeedDone) CloseHandle(state->hFeedDone);
|
|
||||||
if (state->hReset) CloseHandle(state->hReset);
|
if (state->hReset) CloseHandle(state->hReset);
|
||||||
if (state->threadLoop) CloseHandle(state->threadLoop);
|
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->init_done = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL);
|
state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
state->hFeed = CreateEventW(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */
|
state->hFeed = CreateEventW(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */
|
||||||
state->hForceFeed = CreateEventW(NULL, FALSE, FALSE, NULL);
|
state->hResume = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
state->hFeedDone = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
||||||
state->hReset = CreateEventW(NULL, FALSE, FALSE, NULL);
|
state->hReset = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
if (!state->init_done || !state->hFeed || !state->hUninit ||
|
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");
|
MP_ERR(ao, "Error initing events\n");
|
||||||
uninit(ao);
|
uninit(ao);
|
||||||
|
@ -371,10 +395,7 @@ static void audio_reset(struct ao *ao)
|
||||||
static void audio_resume(struct ao *ao)
|
static void audio_resume(struct ao *ao)
|
||||||
{
|
{
|
||||||
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
|
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
|
||||||
|
SetEvent(state->hResume);
|
||||||
SetEvent(state->hForceFeed);
|
|
||||||
WaitForSingleObject(state->hFeedDone, INFINITE);
|
|
||||||
IAudioClient_Start(state->pAudioClientProxy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void list_devs(struct ao *ao, struct ao_device_list *list)
|
static void list_devs(struct ao *ao, struct ao_device_list *list)
|
||||||
|
|
|
@ -72,8 +72,7 @@ typedef struct wasapi_state {
|
||||||
IMMDeviceEnumerator *pEnumerator;
|
IMMDeviceEnumerator *pEnumerator;
|
||||||
|
|
||||||
HANDLE hFeed; /* wasapi event */
|
HANDLE hFeed; /* wasapi event */
|
||||||
HANDLE hForceFeed; /* forces writing a buffer (e.g. before audio_resume) */
|
HANDLE hResume; /* signal audio thread to resume the stream */
|
||||||
HANDLE hFeedDone; /* set only after a hForceFeed */
|
|
||||||
HANDLE hReset; /* signal audio thread to reset the stream */
|
HANDLE hReset; /* signal audio thread to reset the stream */
|
||||||
HANDLE hTask; /* AV thread */
|
HANDLE hTask; /* AV thread */
|
||||||
DWORD taskIndex; /* AV task ID */
|
DWORD taskIndex; /* AV task ID */
|
||||||
|
@ -81,7 +80,6 @@ typedef struct wasapi_state {
|
||||||
|
|
||||||
/* WASAPI proxy handles, for Single-Threaded Apartment communication.
|
/* WASAPI proxy handles, for Single-Threaded Apartment communication.
|
||||||
One is needed for each object that's accessed by a different thread. */
|
One is needed for each object that's accessed by a different thread. */
|
||||||
IAudioClient *pAudioClientProxy;
|
|
||||||
ISimpleAudioVolume *pAudioVolumeProxy;
|
ISimpleAudioVolume *pAudioVolumeProxy;
|
||||||
IAudioEndpointVolume *pEndpointVolumeProxy;
|
IAudioEndpointVolume *pEndpointVolumeProxy;
|
||||||
IAudioSessionControl *pSessionControlProxy;
|
IAudioSessionControl *pSessionControlProxy;
|
||||||
|
@ -89,7 +87,6 @@ typedef struct wasapi_state {
|
||||||
/* Streams used to marshal the proxy objects. The thread owning the actual objects
|
/* 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
|
needs to marshal proxy objects into these streams, and the thread that wants the
|
||||||
proxies unmarshals them from here. */
|
proxies unmarshals them from here. */
|
||||||
IStream *sAudioClient;
|
|
||||||
IStream *sAudioVolume;
|
IStream *sAudioVolume;
|
||||||
IStream *sEndpointVolume;
|
IStream *sEndpointVolume;
|
||||||
IStream *sSessionControl;
|
IStream *sSessionControl;
|
||||||
|
|
|
@ -958,7 +958,6 @@ HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
|
||||||
EXIT_ON_ERROR(hr); \
|
EXIT_ON_ERROR(hr); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
UNMARSHAL(IID_IAudioClient, state->pAudioClientProxy, state->sAudioClient);
|
|
||||||
UNMARSHAL(IID_ISimpleAudioVolume, state->pAudioVolumeProxy, state->sAudioVolume);
|
UNMARSHAL(IID_ISimpleAudioVolume, state->pAudioVolumeProxy, state->sAudioVolume);
|
||||||
UNMARSHAL(IID_IAudioEndpointVolume, state->pEndpointVolumeProxy, state->sEndpointVolume);
|
UNMARSHAL(IID_IAudioEndpointVolume, state->pEndpointVolumeProxy, state->sEndpointVolume);
|
||||||
UNMARSHAL(IID_IAudioSessionControl, state->pSessionControlProxy, state->sSessionControl);
|
UNMARSHAL(IID_IAudioSessionControl, state->pSessionControlProxy, state->sSessionControl);
|
||||||
|
@ -973,7 +972,6 @@ exit_label:
|
||||||
}
|
}
|
||||||
|
|
||||||
void wasapi_release_proxies(wasapi_state *state) {
|
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->pAudioVolumeProxy, IUnknown_Release(state->pAudioVolumeProxy));
|
||||||
SAFE_RELEASE(state->pEndpointVolumeProxy, IUnknown_Release(state->pEndpointVolumeProxy));
|
SAFE_RELEASE(state->pEndpointVolumeProxy, IUnknown_Release(state->pEndpointVolumeProxy));
|
||||||
SAFE_RELEASE(state->pSessionControlProxy, IUnknown_Release(state->pSessionControlProxy));
|
SAFE_RELEASE(state->pSessionControlProxy, IUnknown_Release(state->pSessionControlProxy));
|
||||||
|
@ -991,7 +989,6 @@ static HRESULT create_proxies(struct wasapi_state *state) {
|
||||||
EXIT_ON_ERROR(hr); \
|
EXIT_ON_ERROR(hr); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
MARSHAL(IID_IAudioClient, state->sAudioClient, state->pAudioClient);
|
|
||||||
MARSHAL(IID_ISimpleAudioVolume, state->sAudioVolume, state->pAudioVolume);
|
MARSHAL(IID_ISimpleAudioVolume, state->sAudioVolume, state->pAudioVolume);
|
||||||
MARSHAL(IID_IAudioEndpointVolume, state->sEndpointVolume, state->pEndpointVolume);
|
MARSHAL(IID_IAudioEndpointVolume, state->sEndpointVolume, state->pEndpointVolume);
|
||||||
MARSHAL(IID_IAudioSessionControl, state->sSessionControl, state->pSessionControl);
|
MARSHAL(IID_IAudioSessionControl, state->sSessionControl, state->pSessionControl);
|
||||||
|
@ -1004,7 +1001,6 @@ exit_label:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy_proxies(struct wasapi_state *state) {
|
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->sAudioVolume, IUnknown_Release(state->sAudioVolume));
|
||||||
SAFE_RELEASE(state->sEndpointVolume, IUnknown_Release(state->sEndpointVolume));
|
SAFE_RELEASE(state->sEndpointVolume, IUnknown_Release(state->sEndpointVolume));
|
||||||
SAFE_RELEASE(state->sSessionControl, IUnknown_Release(state->sSessionControl));
|
SAFE_RELEASE(state->sSessionControl, IUnknown_Release(state->sSessionControl));
|
||||||
|
|
Loading…
Reference in New Issue