From: Giovanni Mascellani gmascellani@codeweavers.com
The formal validation of the audio format is the same for all drivers, there is no point having duplicated and different validation code in each driver.
The code I copied here is from the Pulse driver, which looks the most complete. It probably can be improved, but so far I'm just refactoring.
Not all driver-specific validation code is removed yet, because the suggested format output must be handled before. --- dlls/mmdevapi/client.c | 145 +++++++++++++++++++++++++++-- dlls/winealsa.drv/alsa.c | 21 ----- dlls/winecoreaudio.drv/coreaudio.c | 18 +--- dlls/winepulse.drv/pulse.c | 23 +---- 4 files changed, 144 insertions(+), 63 deletions(-)
diff --git a/dlls/mmdevapi/client.c b/dlls/mmdevapi/client.c index 39cbae27b34..fc3a0208f71 100644 --- a/dlls/mmdevapi/client.c +++ b/dlls/mmdevapi/client.c @@ -366,6 +366,123 @@ skip: return wcsdup(name); }
+static HRESULT validate_wfx(const WAVEFORMATEX *fmt, AUDCLNT_SHAREMODE share_mode) +{ + BOOL exclusive = (share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE); + const WAVEFORMATEXTENSIBLE *fmtx = (const void *)fmt; + HRESULT ret = S_OK; + + if (share_mode != AUDCLNT_SHAREMODE_SHARED && share_mode != AUDCLNT_SHAREMODE_EXCLUSIVE) + return E_INVALIDARG; + + if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) + ret = E_INVALIDARG; + else if (fmt->nAvgBytesPerSec == 0 || fmt->nBlockAlign == 0 || + (fmtx->Samples.wValidBitsPerSample > fmt->wBitsPerSample)) + ret = E_INVALIDARG; + else if (fmt->nChannels == 0) + ret = AUDCLNT_E_UNSUPPORTED_FORMAT; + } + + if (FAILED(ret)) + return ret; + + switch (fmt->wFormatTag) { + case WAVE_FORMAT_EXTENSIBLE: + if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) && + fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) || + fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels || + fmtx->Samples.wValidBitsPerSample > fmt->wBitsPerSample || + fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) { + ret = E_INVALIDARG; + } + + if (exclusive) { + UINT32 mask = 0, i, channels = 0; + + if (!(fmtx->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) { + for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) { + if (i & fmtx->dwChannelMask) { + mask |= i; + ++channels; + } + } + + if (channels != fmt->nChannels || (fmtx->dwChannelMask & ~mask)) { + ret = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + } else { + ret = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + } + + if (IsEqualGUID(&fmtx->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { + if (fmt->wBitsPerSample != 32) { + ret = E_INVALIDARG; + break; + } + + if (fmtx->Samples.wValidBitsPerSample != fmt->wBitsPerSample) { + ret = S_FALSE; + } + } else if (IsEqualGUID(&fmtx->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { + if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) { + ret = E_INVALIDARG; + break; + } + + if (fmtx->Samples.wValidBitsPerSample != fmt->wBitsPerSample && + !(fmt->wBitsPerSample == 32 && + fmtx->Samples.wValidBitsPerSample == 24)) { + ret = S_FALSE; + break; + } + } else { + ret = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + break; + + case WAVE_FORMAT_ALAW: + case WAVE_FORMAT_MULAW: + if (fmt->wBitsPerSample != 8) { + ret = E_INVALIDARG; + break; + } + /* Fall-through */ + case WAVE_FORMAT_IEEE_FLOAT: + if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) { + ret = E_INVALIDARG; + break; + } + /* Fall-through */ + case WAVE_FORMAT_PCM: + if (fmt->wFormatTag == WAVE_FORMAT_PCM && + (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) { + ret = E_INVALIDARG; + break; + } + + if (fmt->nChannels > 2) { + ret = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + + /* fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be + * ignored, invalid values are happily accepted. */ + break; + + default: + ret = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + + return ret; +} + static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_period, const AUDCLNT_SHAREMODE mode, const DWORD flags, REFERENCE_TIME duration, REFERENCE_TIME period, @@ -375,6 +492,7 @@ static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_ UINT32 i, channel_count; stream_handle stream; WCHAR *name; + HRESULT hr;
if (!fmt) return E_POINTER; @@ -398,6 +516,13 @@ static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_ return E_INVALIDARG; }
+ hr = validate_wfx(fmt, mode); + + if (hr == S_FALSE) + hr = AUDCLNT_E_UNSUPPORTED_FORMAT; + if (hr != S_OK) + return hr; + sessions_lock();
if (client->stream) { @@ -757,11 +882,20 @@ static HRESULT WINAPI client_IsFormatSupported(IAudioClient3 *iface, AUDCLNT_SHA { struct audio_client *This = impl_from_IAudioClient3(iface); struct is_format_supported_params params; + HRESULT hr;
TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
- if (fmt) - dump_fmt(fmt); + if (out) + *out = NULL; + + if (!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out)) + return E_POINTER; + + dump_fmt(fmt); + + if (FAILED(hr = validate_wfx(fmt, mode))) + return hr;
params.device = This->device_name; params.flow = This->dataflow; @@ -769,11 +903,8 @@ static HRESULT WINAPI client_IsFormatSupported(IAudioClient3 *iface, AUDCLNT_SHA params.fmt_in = fmt; params.fmt_out = NULL;
- if (out) { - *out = NULL; - if (mode == AUDCLNT_SHAREMODE_SHARED) - params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out)); - } + if (out && mode == AUDCLNT_SHAREMODE_SHARED) + params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
wine_unix_call(is_format_supported, ¶ms);
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c index 5bd1d71d837..15805d2a1e9 100644 --- a/dlls/winealsa.drv/alsa.c +++ b/dlls/winealsa.drv/alsa.c @@ -1873,27 +1873,6 @@ static NTSTATUS alsa_is_format_supported(void *args) int err; int alsa_channels, alsa_channel_map[32];
- params->result = S_OK; - - if(!params->fmt_in || (params->share == AUDCLNT_SHAREMODE_SHARED && !params->fmt_out)) - params->result = E_POINTER; - else if(params->share != AUDCLNT_SHAREMODE_SHARED && params->share != AUDCLNT_SHAREMODE_EXCLUSIVE) - params->result = E_INVALIDARG; - else if(params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE){ - if(params->fmt_in->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) - params->result = E_INVALIDARG; - else if(params->fmt_in->nAvgBytesPerSec == 0 || params->fmt_in->nBlockAlign == 0 || - (fmtex->Samples.wValidBitsPerSample > params->fmt_in->wBitsPerSample)) - params->result = E_INVALIDARG; - } - if(FAILED(params->result)) - return STATUS_SUCCESS; - - if(params->fmt_in->nChannels == 0){ - params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; - return STATUS_SUCCESS; - } - params->result = alsa_open_device(params->device, params->flow, &pcm_handle, &hw_params); if(FAILED(params->result)) return STATUS_SUCCESS; diff --git a/dlls/winecoreaudio.drv/coreaudio.c b/dlls/winecoreaudio.drv/coreaudio.c index ced8f3838fb..b19f7ba3854 100644 --- a/dlls/winecoreaudio.drv/coreaudio.c +++ b/dlls/winecoreaudio.drv/coreaudio.c @@ -1100,30 +1100,20 @@ static NTSTATUS unix_is_format_supported(void *args) AudioComponentInstance unit; const AudioDeviceID dev_id = dev_id_from_device(params->device);
- params->result = S_OK; - - if(!params->fmt_in || (params->share == AUDCLNT_SHAREMODE_SHARED && !params->fmt_out)) - params->result = E_POINTER; - else if(params->share != AUDCLNT_SHAREMODE_SHARED && params->share != AUDCLNT_SHAREMODE_EXCLUSIVE) - params->result = E_INVALIDARG; - else if(params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE){ - if(params->fmt_in->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) - params->result = E_INVALIDARG; - else if(params->fmt_in->nAvgBytesPerSec == 0 || params->fmt_in->nBlockAlign == 0 || - fmtex->Samples.wValidBitsPerSample > params->fmt_in->wBitsPerSample) - params->result = E_INVALIDARG; - else if(fmtex->Samples.wValidBitsPerSample < params->fmt_in->wBitsPerSample) + if(params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (fmtex->Samples.wValidBitsPerSample < params->fmt_in->wBitsPerSample) goto unsupported; else if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE && (fmtex->dwChannelMask == 0 || fmtex->dwChannelMask & SPEAKER_RESERVED)) goto unsupported; } - if(FAILED(params->result)) return STATUS_SUCCESS;
if(params->fmt_in->nBlockAlign != params->fmt_in->nChannels * params->fmt_in->wBitsPerSample / 8 || params->fmt_in->nAvgBytesPerSec != params->fmt_in->nBlockAlign * params->fmt_in->nSamplesPerSec) goto unsupported;
+ params->result = S_OK; + if(params->fmt_in->nChannels == 0){ params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; return STATUS_SUCCESS; diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 9a218e75c1b..9129b224c44 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -2076,27 +2076,8 @@ static NTSTATUS pulse_is_format_supported(void *args)
params->result = S_OK;
- if (!params->fmt_in || (params->share == AUDCLNT_SHAREMODE_SHARED && !params->fmt_out)) - params->result = E_POINTER; - else if (params->share != AUDCLNT_SHAREMODE_SHARED && params->share != AUDCLNT_SHAREMODE_EXCLUSIVE) - params->result = E_INVALIDARG; - else { - memcpy(&in, params->fmt_in, params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? - sizeof(in) : sizeof(in.Format)); - - if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) - params->result = E_INVALIDARG; - else if (fmt->nAvgBytesPerSec == 0 || fmt->nBlockAlign == 0 || - (in.Samples.wValidBitsPerSample > fmt->wBitsPerSample)) - params->result = E_INVALIDARG; - else if (fmt->nChannels == 0) - params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; - } - } - - if (FAILED(params->result)) - return STATUS_SUCCESS; + memcpy(&in, params->fmt_in, params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? + sizeof(in) : sizeof(in.Format));
if (exclusive) out = ∈