From 1b9e01509da2b80867f60b2f98939f5dc66763ba Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Tue, 3 Sep 2019 15:10:06 +0200 Subject: [PATCH] Refactored winmm backend Using CALLBACK_FUNCTION again, but protect with semaphore to avoid closing the device while there are still buffers to be played. --- channels/rdpsnd/client/winmm/rdpsnd_winmm.c | 83 +++++++++++++++++++---------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/channels/rdpsnd/client/winmm/rdpsnd_winmm.c b/channels/rdpsnd/client/winmm/rdpsnd_winmm.c index 0d8ab32..5d96970 100644 --- a/channels/rdpsnd/client/winmm/rdpsnd_winmm.c +++ b/channels/rdpsnd/client/winmm/rdpsnd_winmm.c @@ -41,6 +41,8 @@ #include "rdpsnd_main.h" +#define SEM_COUNT_MAX 4 + typedef struct rdpsnd_winmm_plugin rdpsndWinmmPlugin; struct rdpsnd_winmm_plugin @@ -51,7 +53,8 @@ struct rdpsnd_winmm_plugin WAVEFORMATEX format; UINT32 volume; wLog* log; - HANDLE playedEvent; + UINT32 latency; + HANDLE semaphore; }; static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* out) @@ -84,14 +87,39 @@ static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, const AUDIO_FORM { rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; - WINPR_UNUSED(latency); - + winmm->latency = latency; if (!rdpsnd_winmm_convert_format(format, &winmm->format)) return FALSE; return TRUE; } +static void CALLBACK waveOutProc( + HWAVEOUT hwo, + UINT uMsg, + DWORD_PTR dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2 + ) +{ + rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) dwInstance; + LPWAVEHDR lpWaveHdr = (LPWAVEHDR)dwParam1; + + switch(uMsg) + { + case WOM_OPEN: + case WOM_CLOSE: + break; + case WOM_DONE: + waveOutUnprepareHeader(hwo, lpWaveHdr, sizeof(WAVEHDR)); + free(lpWaveHdr); + ReleaseSemaphore(winmm->semaphore, 1, NULL); + break; + default: + break; + } +} + static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency) { @@ -105,8 +133,8 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo return FALSE; mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format, - (DWORD_PTR) winmm->playedEvent, (DWORD_PTR)NULL, - CALLBACK_EVENT); + (DWORD_PTR) waveOutProc, (DWORD_PTR)winmm, + CALLBACK_FUNCTION); if (mmResult != MMSYSERR_NOERROR) { @@ -114,6 +142,8 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo return FALSE; } + ReleaseSemaphore(winmm->semaphore, SEM_COUNT_MAX, NULL); + mmResult = waveOutSetVolume(winmm->hWaveOut, winmm->volume); if (mmResult != MMSYSERR_NOERROR) @@ -127,22 +157,17 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo static void rdpsnd_winmm_close(rdpsndDevicePlugin* device) { + size_t x; MMRESULT mmResult; rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; if (winmm->hWaveOut) { - mmResult = waveOutReset(winmm->hWaveOut); - if (mmResult != MMSYSERR_NOERROR) - { - WLog_Print(winmm->log, WLOG_ERROR, "waveOutReset failure: %"PRIu32"", mmResult); - } + for (x=0; xsemaphore, INFINITE); mmResult = waveOutClose(winmm->hWaveOut); - if (mmResult != MMSYSERR_NOERROR) - { WLog_Print(winmm->log, WLOG_ERROR, "waveOutClose failure: %"PRIu32"", mmResult); - } winmm->hWaveOut = NULL; } @@ -155,7 +180,7 @@ static void rdpsnd_winmm_free(rdpsndDevicePlugin* device) if (winmm) { rdpsnd_winmm_close(device); - CloseHandle(winmm->playedEvent); + CloseHandle(winmm->semaphore); free(winmm); } } @@ -222,7 +247,7 @@ static void rdpsnd_winmm_start(rdpsndDevicePlugin* device) static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size) { MMRESULT mmResult; - WAVEHDR lpWaveHdr[1]; + LPWAVEHDR lpWaveHdr; rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; if (!winmm->hWaveOut) @@ -231,39 +256,36 @@ static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size if (size > UINT32_MAX) return 0; + lpWaveHdr = malloc(sizeof(WAVEHDR)); + if (!lpWaveHdr) + return 0; + lpWaveHdr->dwFlags = 0; lpWaveHdr->dwLoops = 0; lpWaveHdr->lpData = (LPSTR) data; lpWaveHdr->dwBufferLength = (DWORD)size; - lpWaveHdr->lpNext = NULL; - mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); if (mmResult != MMSYSERR_NOERROR) { WLog_Print(winmm->log, WLOG_ERROR, "waveOutPrepareHeader failure: %"PRIu32"", mmResult); + free(lpWaveHdr); return 0; } + WaitForSingleObject(winmm->semaphore, INFINITE); mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); if (mmResult != MMSYSERR_NOERROR) { WLog_Print(winmm->log, WLOG_ERROR, "waveOutWrite failure: %"PRIu32"", mmResult); waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); + free(lpWaveHdr); return 0; } - do - { - WaitForSingleObject(winmm->playedEvent, INFINITE); - } - while((lpWaveHdr->dwFlags & WHDR_DONE) == 0); - - waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); - - return 0; + return winmm->latency; } static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args) @@ -301,11 +323,16 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p winmm->device.Close = rdpsnd_winmm_close; winmm->device.Free = rdpsnd_winmm_free; winmm->log = WLog_Get(TAG); - winmm->playedEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - + winmm->semaphore = CreateSemaphore(NULL, 0, SEM_COUNT_MAX, NULL); + if (!winmm->semaphore) + goto fail; args = pEntryPoints->args; rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*) winmm, args); winmm->volume = 0xFFFFFFFF; pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) winmm); return CHANNEL_RC_OK; + +fail: + rdpsnd_winmm_free((rdpsndDevicePlugin*)winmm); + return ERROR_INTERNAL_ERROR; } -- 2.7.4