RDPSND device API refinements

* Added default format callback for rdpsnd backend to allow
  different default input formats (different samplerates, ...)
* Made WINMM backend in flight packet limitation a compile time
  option
* Fixed missing buffer copy in winmm backend

Signed-off-by: Armin Novak <armin.novak@thincast.com>
This commit is contained in:
Armin Novak 2020-01-08 11:27:49 +01:00
parent 05a865281e
commit f720ec5383
6 changed files with 66 additions and 41 deletions

View File

@ -81,10 +81,6 @@ static UINT rdpsnd_fake_play(rdpsndDevicePlugin* device, const BYTE* data, size_
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
static void rdpsnd_fake_start(rdpsndDevicePlugin* device)
{
}
/** /**
* Function description * Function description
* *
@ -142,7 +138,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
fake->device.FormatSupported = rdpsnd_fake_format_supported; fake->device.FormatSupported = rdpsnd_fake_format_supported;
fake->device.SetVolume = rdpsnd_fake_set_volume; fake->device.SetVolume = rdpsnd_fake_set_volume;
fake->device.Play = rdpsnd_fake_play; fake->device.Play = rdpsnd_fake_play;
fake->device.Start = rdpsnd_fake_start;
fake->device.Close = rdpsnd_fake_close; fake->device.Close = rdpsnd_fake_close;
fake->device.Free = rdpsnd_fake_free; fake->device.Free = rdpsnd_fake_free;
args = pEntryPoints->args; args = pEntryPoints->args;

View File

@ -95,10 +95,6 @@ static UINT rdpsnd_proxy_play(rdpsndDevicePlugin* device, const BYTE* data, size
return GetTickCount() - start; return GetTickCount() - start;
} }
static void rdpsnd_proxy_start(rdpsndDevicePlugin* device)
{
/* do nothing */
}
/** /**
* Function description * Function description
@ -131,7 +127,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
proxy->device.FormatSupported = rdpsnd_proxy_format_supported; proxy->device.FormatSupported = rdpsnd_proxy_format_supported;
proxy->device.SetVolume = rdpsnd_proxy_set_volume; proxy->device.SetVolume = rdpsnd_proxy_set_volume;
proxy->device.Play = rdpsnd_proxy_play; proxy->device.Play = rdpsnd_proxy_play;
proxy->device.Start = rdpsnd_proxy_start;
proxy->device.Close = rdpsnd_proxy_close; proxy->device.Close = rdpsnd_proxy_close;
proxy->device.Free = rdpsnd_proxy_free; proxy->device.Free = rdpsnd_proxy_free;
args = pEntryPoints->args; args = pEntryPoints->args;

View File

@ -390,6 +390,29 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
free(pulse); free(pulse);
} }
static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* desired,
AUDIO_FORMAT* defaultFormat)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse || !defaultFormat)
return FALSE;
*defaultFormat = *desired;
defaultFormat->data = NULL;
defaultFormat->cbSize = 0;
defaultFormat->wFormatTag = WAVE_FORMAT_PCM;
if ((defaultFormat->nChannels < 1) || (defaultFormat->nChannels > PA_CHANNELS_MAX))
defaultFormat->nChannels = 2;
if ((defaultFormat->nSamplesPerSec < 1) || (defaultFormat->nSamplesPerSec > PA_RATE_MAX))
defaultFormat->nSamplesPerSec = 44100;
if ((defaultFormat->wBitsPerSample != 8) && (defaultFormat->wBitsPerSample != 16))
defaultFormat->wBitsPerSample = 16;
defaultFormat->nBlockAlign = defaultFormat->nChannels * defaultFormat->wBitsPerSample / 8;
defaultFormat->nAvgBytesPerSec = defaultFormat->nBlockAlign * defaultFormat->nSamplesPerSec;
return TRUE;
}
BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format) BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
{ {
switch (format->wFormatTag) switch (format->wFormatTag)
@ -507,18 +530,6 @@ static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size
return latency / 1000; return latency / 1000;
} }
static void rdpsnd_pulse_start(rdpsndDevicePlugin* device)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
if (!pulse->stream)
return;
pa_threaded_mainloop_lock(pulse->mainloop);
pa_stream_trigger(pulse->stream, NULL, NULL);
pa_threaded_mainloop_unlock(pulse->mainloop);
}
/** /**
* Function description * Function description
* *
@ -587,9 +598,9 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
pulse->device.GetVolume = rdpsnd_pulse_get_volume; pulse->device.GetVolume = rdpsnd_pulse_get_volume;
pulse->device.SetVolume = rdpsnd_pulse_set_volume; pulse->device.SetVolume = rdpsnd_pulse_set_volume;
pulse->device.Play = rdpsnd_pulse_play; pulse->device.Play = rdpsnd_pulse_play;
pulse->device.Start = rdpsnd_pulse_start;
pulse->device.Close = rdpsnd_pulse_close; pulse->device.Close = rdpsnd_pulse_close;
pulse->device.Free = rdpsnd_pulse_free; pulse->device.Free = rdpsnd_pulse_free;
pulse->device.DefaultFormat = rdpsnd_pulse_default_format;
args = pEntryPoints->args; args = pEntryPoints->args;
if (args->argc > 1) if (args->argc > 1)

View File

@ -343,11 +343,15 @@ static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
supported = IFCALLRESULT(FALSE, rdpsnd->device->FormatSupported, rdpsnd->device, format); supported = IFCALLRESULT(FALSE, rdpsnd->device->FormatSupported, rdpsnd->device, format);
if (!supported) if (!supported)
{
if (!IFCALLRESULT(FALSE, rdpsnd->device->DefaultFormat, rdpsnd->device, format,
&deviceFormat))
{ {
deviceFormat.wFormatTag = WAVE_FORMAT_PCM; deviceFormat.wFormatTag = WAVE_FORMAT_PCM;
deviceFormat.wBitsPerSample = 16; deviceFormat.wBitsPerSample = 16;
deviceFormat.cbSize = 0; deviceFormat.cbSize = 0;
} }
}
WLog_Print(rdpsnd->log, WLOG_DEBUG, "Opening device with format %s [backend %s]", WLog_Print(rdpsnd->log, WLOG_DEBUG, "Opening device with format %s [backend %s]",
audio_format_get_tag_string(format->wFormatTag), audio_format_get_tag_string(format->wFormatTag),

View File

@ -41,7 +41,9 @@
#include "rdpsnd_main.h" #include "rdpsnd_main.h"
#define SEM_COUNT_MAX 4 #if defined(WITH_WINMM_SEMAPHORE)
#define SEM_COUNT_MAX 6
#endif
typedef struct rdpsnd_winmm_plugin rdpsndWinmmPlugin; typedef struct rdpsnd_winmm_plugin rdpsndWinmmPlugin;
@ -54,7 +56,9 @@ struct rdpsnd_winmm_plugin
UINT32 volume; UINT32 volume;
wLog* log; wLog* log;
UINT32 latency; UINT32 latency;
#if defined(WITH_WINMM_SEMAPHORE)
HANDLE semaphore; HANDLE semaphore;
#endif
}; };
static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* out) static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* out)
@ -107,8 +111,11 @@ static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
break; break;
case WOM_DONE: case WOM_DONE:
waveOutUnprepareHeader(hwo, lpWaveHdr, sizeof(WAVEHDR)); waveOutUnprepareHeader(hwo, lpWaveHdr, sizeof(WAVEHDR));
free(lpWaveHdr->lpData);
free(lpWaveHdr); free(lpWaveHdr);
#if defined(WITH_WINMM_SEMAPHORE)
ReleaseSemaphore(winmm->semaphore, 1, NULL); ReleaseSemaphore(winmm->semaphore, 1, NULL);
#endif
break; break;
default: default:
break; break;
@ -136,7 +143,9 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
return FALSE; return FALSE;
} }
#if defined(WITH_WINMM_SEMAPHORE)
ReleaseSemaphore(winmm->semaphore, SEM_COUNT_MAX, NULL); ReleaseSemaphore(winmm->semaphore, SEM_COUNT_MAX, NULL);
#endif
mmResult = waveOutSetVolume(winmm->hWaveOut, winmm->volume); mmResult = waveOutSetVolume(winmm->hWaveOut, winmm->volume);
@ -151,14 +160,16 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
static void rdpsnd_winmm_close(rdpsndDevicePlugin* device) static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
{ {
size_t x;
MMRESULT mmResult; MMRESULT mmResult;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device; rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
if (winmm->hWaveOut) if (winmm->hWaveOut)
{ {
#if defined(WITH_WINMM_SEMAPHORE)
size_t x;
for (x = 0; x < SEM_COUNT_MAX; x++) for (x = 0; x < SEM_COUNT_MAX; x++)
WaitForSingleObject(winmm->semaphore, INFINITE); WaitForSingleObject(winmm->semaphore, INFINITE);
#endif
mmResult = waveOutClose(winmm->hWaveOut); mmResult = waveOutClose(winmm->hWaveOut);
if (mmResult != MMSYSERR_NOERROR) if (mmResult != MMSYSERR_NOERROR)
WLog_Print(winmm->log, WLOG_ERROR, "waveOutClose failure: %" PRIu32 "", mmResult); WLog_Print(winmm->log, WLOG_ERROR, "waveOutClose failure: %" PRIu32 "", mmResult);
@ -174,7 +185,9 @@ static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
if (winmm) if (winmm)
{ {
rdpsnd_winmm_close(device); rdpsnd_winmm_close(device);
#if defined(WITH_WINMM_SEMAPHORE)
CloseHandle(winmm->semaphore); CloseHandle(winmm->semaphore);
#endif
free(winmm); free(winmm);
} }
} }
@ -232,12 +245,6 @@ static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
return TRUE; return TRUE;
} }
static void rdpsnd_winmm_start(rdpsndDevicePlugin* device)
{
// rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
WINPR_UNUSED(device);
}
static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size) static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
{ {
MMRESULT mmResult; MMRESULT mmResult;
@ -256,7 +263,10 @@ static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size
lpWaveHdr->dwFlags = 0; lpWaveHdr->dwFlags = 0;
lpWaveHdr->dwLoops = 0; lpWaveHdr->dwLoops = 0;
lpWaveHdr->lpData = (LPSTR)data; lpWaveHdr->lpData = malloc(size);
if (!lpWaveHdr->lpData)
goto fail;
memcpy(lpWaveHdr->lpData, data, size);
lpWaveHdr->dwBufferLength = (DWORD)size; lpWaveHdr->dwBufferLength = (DWORD)size;
mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
@ -264,22 +274,27 @@ static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size
if (mmResult != MMSYSERR_NOERROR) if (mmResult != MMSYSERR_NOERROR)
{ {
WLog_Print(winmm->log, WLOG_ERROR, "waveOutPrepareHeader failure: %" PRIu32 "", mmResult); WLog_Print(winmm->log, WLOG_ERROR, "waveOutPrepareHeader failure: %" PRIu32 "", mmResult);
free(lpWaveHdr); goto fail;
return 0;
} }
#if defined(WITH_WINMM_SEMAPHORE)
WaitForSingleObject(winmm->semaphore, INFINITE); WaitForSingleObject(winmm->semaphore, INFINITE);
#endif
mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
if (mmResult != MMSYSERR_NOERROR) if (mmResult != MMSYSERR_NOERROR)
{ {
WLog_Print(winmm->log, WLOG_ERROR, "waveOutWrite failure: %" PRIu32 "", mmResult); WLog_Print(winmm->log, WLOG_ERROR, "waveOutWrite failure: %" PRIu32 "", mmResult);
waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
free(lpWaveHdr); goto fail;
return 0;
} }
return winmm->latency; return winmm->latency;
fail:
if (lpWaveHdr)
free(lpWaveHdr->lpData);
free(lpWaveHdr);
return 0;
} }
static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args) static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
@ -311,6 +326,7 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
} }
winmm = (rdpsndWinmmPlugin*)calloc(1, sizeof(rdpsndWinmmPlugin)); winmm = (rdpsndWinmmPlugin*)calloc(1, sizeof(rdpsndWinmmPlugin));
if (!winmm) if (!winmm)
return CHANNEL_RC_NO_MEMORY; return CHANNEL_RC_NO_MEMORY;
@ -318,14 +334,15 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
winmm->device.FormatSupported = rdpsnd_winmm_format_supported; winmm->device.FormatSupported = rdpsnd_winmm_format_supported;
winmm->device.GetVolume = rdpsnd_winmm_get_volume; winmm->device.GetVolume = rdpsnd_winmm_get_volume;
winmm->device.SetVolume = rdpsnd_winmm_set_volume; winmm->device.SetVolume = rdpsnd_winmm_set_volume;
winmm->device.Start = rdpsnd_winmm_start;
winmm->device.Play = rdpsnd_winmm_play; winmm->device.Play = rdpsnd_winmm_play;
winmm->device.Close = rdpsnd_winmm_close; winmm->device.Close = rdpsnd_winmm_close;
winmm->device.Free = rdpsnd_winmm_free; winmm->device.Free = rdpsnd_winmm_free;
winmm->log = WLog_Get(TAG); winmm->log = WLog_Get(TAG);
#if defined(WITH_WINMM_SEMAPHORE)
winmm->semaphore = CreateSemaphore(NULL, 0, SEM_COUNT_MAX, NULL); winmm->semaphore = CreateSemaphore(NULL, 0, SEM_COUNT_MAX, NULL);
if (!winmm->semaphore) if (!winmm->semaphore)
goto fail; goto fail;
#endif
args = pEntryPoints->args; args = pEntryPoints->args;
rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*)winmm, args); rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*)winmm, args);
winmm->volume = 0xFFFFFFFF; winmm->volume = 0xFFFFFFFF;

View File

@ -38,6 +38,8 @@ typedef UINT (*pcPlay)(rdpsndDevicePlugin* device, const BYTE* data, size_t size
typedef void (*pcStart)(rdpsndDevicePlugin* device); typedef void (*pcStart)(rdpsndDevicePlugin* device);
typedef void (*pcClose)(rdpsndDevicePlugin* device); typedef void (*pcClose)(rdpsndDevicePlugin* device);
typedef void (*pcFree)(rdpsndDevicePlugin* device); typedef void (*pcFree)(rdpsndDevicePlugin* device);
typedef BOOL (*pcDefaultFormat)(rdpsndDevicePlugin* device, const AUDIO_FORMAT* desired,
AUDIO_FORMAT* defaultFormat);
struct rdpsnd_device_plugin struct rdpsnd_device_plugin
{ {
@ -48,9 +50,10 @@ struct rdpsnd_device_plugin
pcGetVolume GetVolume; pcGetVolume GetVolume;
pcSetVolume SetVolume; pcSetVolume SetVolume;
pcPlay Play; pcPlay Play;
pcStart Start; pcStart Start; /* Deprecated, unused. */
pcClose Close; pcClose Close;
pcFree Free; pcFree Free;
pcDefaultFormat DefaultFormat;
}; };
#define RDPSND_DEVICE_EXPORT_FUNC_NAME "freerdp_rdpsnd_client_subsystem_entry" #define RDPSND_DEVICE_EXPORT_FUNC_NAME "freerdp_rdpsnd_client_subsystem_entry"