wasapi:
- fixed object (IAudioRenderClient/IAudioCaptureClient) leak if stream is stopped and restarted without closing - improved IAudioClient event handle management on stream restart (not deleting/creating event handle but rather keeping it until stream is closed)
This commit is contained in:
parent
083b34a10f
commit
e7bf9ce49d
1 changed files with 117 additions and 41 deletions
|
|
@ -512,6 +512,9 @@ typedef struct PaWasapiStream
|
||||||
IAudioRenderClient *rclient;
|
IAudioRenderClient *rclient;
|
||||||
IAudioEndpointVolume *outVol;
|
IAudioEndpointVolume *outVol;
|
||||||
|
|
||||||
|
// event handles for event-driven processing mode
|
||||||
|
HANDLE event[S_COUNT];
|
||||||
|
|
||||||
// must be volatile to avoid race condition on user query while
|
// must be volatile to avoid race condition on user query while
|
||||||
// thread is being started
|
// thread is being started
|
||||||
volatile BOOL running;
|
volatile BOOL running;
|
||||||
|
|
@ -2527,13 +2530,16 @@ static PaError CloseStream( PaStream* s )
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
SAFE_RELEASE(stream->out.client);
|
|
||||||
SAFE_RELEASE(stream->in.client);
|
|
||||||
SAFE_RELEASE(stream->cclient);
|
SAFE_RELEASE(stream->cclient);
|
||||||
SAFE_RELEASE(stream->rclient);
|
SAFE_RELEASE(stream->rclient);
|
||||||
|
SAFE_RELEASE(stream->out.client);
|
||||||
|
SAFE_RELEASE(stream->in.client);
|
||||||
SAFE_RELEASE(stream->inVol);
|
SAFE_RELEASE(stream->inVol);
|
||||||
SAFE_RELEASE(stream->outVol);
|
SAFE_RELEASE(stream->outVol);
|
||||||
|
|
||||||
|
CloseHandle(stream->event[S_INPUT]);
|
||||||
|
CloseHandle(stream->event[S_OUTPUT]);
|
||||||
|
|
||||||
SAFE_CLOSE(stream->hThread);
|
SAFE_CLOSE(stream->hThread);
|
||||||
SAFE_CLOSE(stream->hThreadStart);
|
SAFE_CLOSE(stream->hThreadStart);
|
||||||
SAFE_CLOSE(stream->hThreadExit);
|
SAFE_CLOSE(stream->hThreadExit);
|
||||||
|
|
@ -2573,8 +2579,8 @@ static PaError StartStream( PaStream *s )
|
||||||
stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);
|
stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);
|
stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
|
||||||
if ((stream->in.client && stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) ||
|
if ((stream->in.client && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||
|
||||||
(stream->out.client && stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
|
(stream->out.client && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))
|
||||||
{
|
{
|
||||||
if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)
|
if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)
|
||||||
return paUnanticipatedHostError;
|
return paUnanticipatedHostError;
|
||||||
|
|
@ -3292,6 +3298,7 @@ void _OnStreamStop(PaWasapiStream *stream)
|
||||||
// Stop INPUT client
|
// Stop INPUT client
|
||||||
if (stream->in.client != NULL)
|
if (stream->in.client != NULL)
|
||||||
IAudioClient_Stop(stream->in.client);
|
IAudioClient_Stop(stream->in.client);
|
||||||
|
|
||||||
// Stop OUTPUT client
|
// Stop OUTPUT client
|
||||||
if (stream->out.client != NULL)
|
if (stream->out.client != NULL)
|
||||||
IAudioClient_Stop(stream->out.client);
|
IAudioClient_Stop(stream->out.client);
|
||||||
|
|
@ -3311,7 +3318,6 @@ void _OnStreamStop(PaWasapiStream *stream)
|
||||||
// ------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------
|
||||||
PA_THREAD_FUNC ProcThreadEvent(void *param)
|
PA_THREAD_FUNC ProcThreadEvent(void *param)
|
||||||
{
|
{
|
||||||
HANDLE event[S_COUNT];
|
|
||||||
PaWasapiHostProcessor processor[S_COUNT];
|
PaWasapiHostProcessor processor[S_COUNT];
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
DWORD dwResult;
|
DWORD dwResult;
|
||||||
|
|
@ -3332,53 +3338,90 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
|
||||||
processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
|
processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
|
||||||
processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
|
processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
|
||||||
|
|
||||||
// Initialize event to NULL first
|
|
||||||
event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
||||||
event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
||||||
|
|
||||||
// Check on low resources and disallow any processing further
|
|
||||||
if (!event[S_INPUT] || !event[S_OUTPUT])
|
|
||||||
{
|
|
||||||
PRINT(("WASAPI Thread: failed creating one or two event handles\n"));
|
|
||||||
|
|
||||||
// Prevent deadlocking in Pa_StreamStart
|
|
||||||
SetEvent(stream->hThreadStart);
|
|
||||||
// Exit
|
|
||||||
goto thread_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Boost thread priority
|
// Boost thread priority
|
||||||
PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
|
PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
|
||||||
|
|
||||||
// Initialize event & start INPUT stream
|
// Initialize event & start INPUT stream
|
||||||
if (stream->in.client)
|
if (stream->in.client)
|
||||||
{
|
{
|
||||||
if ((hr = IAudioClient_SetEventHandle(stream->in.client, event[S_INPUT])) != S_OK)
|
// Create & set handle
|
||||||
LogHostError(hr);
|
if (stream->event[S_INPUT] == NULL)
|
||||||
|
{
|
||||||
|
stream->event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (stream->event[S_INPUT] == NULL)
|
||||||
|
{
|
||||||
|
PRINT(("WASAPI Thread: failed creating Input event handle\n"));
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
if ((hr = IAudioClient_SetEventHandle(stream->in.client, stream->event[S_INPUT])) != S_OK)
|
||||||
|
{
|
||||||
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
|
// Create Capture client
|
||||||
LogHostError(hr);
|
if (stream->cclient == NULL)
|
||||||
|
{
|
||||||
|
if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
|
||||||
|
{
|
||||||
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start
|
||||||
if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
|
if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
|
||||||
|
{
|
||||||
LogHostError(hr);
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize event & start OUTPUT stream
|
// Initialize event & start OUTPUT stream
|
||||||
if (stream->out.client)
|
if (stream->out.client)
|
||||||
{
|
{
|
||||||
if ((hr = IAudioClient_SetEventHandle(stream->out.client, event[S_OUTPUT])) != S_OK)
|
// Create & set handle
|
||||||
LogHostError(hr);
|
if (stream->event[S_OUTPUT] == NULL)
|
||||||
|
{
|
||||||
|
stream->event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (stream->event[S_OUTPUT] == NULL)
|
||||||
|
{
|
||||||
|
PRINT(("WASAPI Thread: failed creating Output event handle\n"));
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
|
||||||
if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
|
if ((hr = IAudioClient_SetEventHandle(stream->out.client, stream->event[S_OUTPUT])) != S_OK)
|
||||||
LogHostError(hr);
|
{
|
||||||
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Render client
|
||||||
|
if (stream->rclient == NULL)
|
||||||
|
{
|
||||||
|
if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
|
||||||
|
{
|
||||||
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Preload buffer before start
|
// Preload buffer before start
|
||||||
if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
|
if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
|
||||||
|
{
|
||||||
LogHostError(hr);
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
|
||||||
// Start
|
// Start
|
||||||
if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
|
if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
|
||||||
|
{
|
||||||
LogHostError(hr);
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal: stream running
|
// Signal: stream running
|
||||||
|
|
@ -3391,7 +3434,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
// 2 sec timeout
|
// 2 sec timeout
|
||||||
dwResult = WaitForMultipleObjects(S_COUNT, event, bWaitAllEvents, 10000);
|
dwResult = WaitForMultipleObjects(S_COUNT, stream->event, bWaitAllEvents, 10000);
|
||||||
|
|
||||||
// Check for close event (after wait for buffers to avoid any calls to user
|
// Check for close event (after wait for buffers to avoid any calls to user
|
||||||
// callback when hCloseRequest was set)
|
// callback when hCloseRequest was set)
|
||||||
|
|
@ -3403,7 +3446,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
|
||||||
{
|
{
|
||||||
case WAIT_TIMEOUT: {
|
case WAIT_TIMEOUT: {
|
||||||
PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));
|
PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));
|
||||||
goto processing_stop;
|
goto thread_end;
|
||||||
break; }
|
break; }
|
||||||
|
|
||||||
// Input stream
|
// Input stream
|
||||||
|
|
@ -3422,17 +3465,11 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processing_stop:
|
thread_end:
|
||||||
|
|
||||||
// Process stop
|
// Process stop
|
||||||
_OnStreamStop(stream);
|
_OnStreamStop(stream);
|
||||||
|
|
||||||
thread_end:
|
|
||||||
|
|
||||||
// Close event
|
|
||||||
CloseHandle(event[S_INPUT]);
|
|
||||||
CloseHandle(event[S_OUTPUT]);
|
|
||||||
|
|
||||||
// Notify: thread exited
|
// Notify: thread exited
|
||||||
SetEvent(stream->hThreadExit);
|
SetEvent(stream->hThreadExit);
|
||||||
|
|
||||||
|
|
@ -3440,6 +3477,14 @@ thread_end:
|
||||||
stream->running = FALSE;
|
stream->running = FALSE;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
thread_error:
|
||||||
|
|
||||||
|
// Prevent deadlocking in Pa_StreamStart
|
||||||
|
SetEvent(stream->hThreadStart);
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
goto thread_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------
|
||||||
|
|
@ -3474,27 +3519,48 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
||||||
// Initialize event & start INPUT stream
|
// Initialize event & start INPUT stream
|
||||||
if (stream->in.client)
|
if (stream->in.client)
|
||||||
{
|
{
|
||||||
if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
|
if (stream->cclient == NULL)
|
||||||
LogHostError(hr);
|
{
|
||||||
|
if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
|
||||||
|
{
|
||||||
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
|
if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
|
||||||
|
{
|
||||||
LogHostError(hr);
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize event & start OUTPUT stream
|
// Initialize event & start OUTPUT stream
|
||||||
if (stream->out.client)
|
if (stream->out.client)
|
||||||
{
|
{
|
||||||
if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
|
if (stream->rclient == NULL)
|
||||||
LogHostError(hr);
|
{
|
||||||
|
if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
|
||||||
|
{
|
||||||
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Preload buffer (obligatory, othervise ->Start() will fail)
|
// Preload buffer (obligatory, othervise ->Start() will fail)
|
||||||
if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
|
if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
|
||||||
|
{
|
||||||
LogHostError(hr);
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
|
|
||||||
// Start
|
// Start
|
||||||
if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
|
if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
|
||||||
|
{
|
||||||
LogHostError(hr);
|
LogHostError(hr);
|
||||||
|
goto thread_error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal: stream running
|
// Signal: stream running
|
||||||
|
|
@ -3547,6 +3613,8 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_end:
|
||||||
|
|
||||||
// Process stop
|
// Process stop
|
||||||
_OnStreamStop(stream);
|
_OnStreamStop(stream);
|
||||||
|
|
||||||
|
|
@ -3557,6 +3625,14 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
||||||
stream->running = FALSE;
|
stream->running = FALSE;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
thread_error:
|
||||||
|
|
||||||
|
// Prevent deadlocking in Pa_StreamStart
|
||||||
|
SetEvent(stream->hThreadStart);
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
goto thread_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endif //VC 2005
|
//#endif //VC 2005
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue