[vlc-devel] [PATCH 5/5] wasapi: add an option to handle exclusive mode
Thomas Guillem
thomas at gllm.fr
Thu Oct 24 14:37:13 CEST 2019
cf. WASAPI_EXCLUSIVE_LONGTEXT
---
modules/audio_output/wasapi.c | 174 +++++++++++++++++++++++++++++++++-
1 file changed, 170 insertions(+), 4 deletions(-)
diff --git a/modules/audio_output/wasapi.c b/modules/audio_output/wasapi.c
index daa946f8afc..91defb250fb 100644
--- a/modules/audio_output/wasapi.c
+++ b/modules/audio_output/wasapi.c
@@ -588,6 +588,154 @@ static void Stop(aout_stream_t *s)
free(sys);
}
+/*
+ * This function will try to find the closest PCM format that is accepted by
+ * the sound card. Exclusive here means a direct access to the sound card. The
+ * format arguments and the return code of this function behave exactly like
+ * IAudioClient_IsFormatSupported().
+ */
+static HRESULT GetExclusivePCMFormat(IAudioClient *c, const WAVEFORMATEX *pwf,
+ WAVEFORMATEX **ppwf_closest)
+{
+ HRESULT hr;
+ const AUDCLNT_SHAREMODE exclusive = AUDCLNT_SHAREMODE_EXCLUSIVE;
+
+ *ppwf_closest = NULL;
+
+ /* First try the input format */
+ hr = IAudioClient_IsFormatSupported(c, exclusive, pwf, NULL);
+
+ if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
+ {
+ assert(hr != S_FALSE); /* S_FALSE reserved for shared mode */
+ return hr;
+ }
+
+ /* This format come from vlc_ToWave() */
+ assert(pwf->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
+ const WAVEFORMATEXTENSIBLE *pwfe = (void *) pwf;
+ assert(IsEqualIID(&pwfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ || IsEqualIID(&pwfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM));
+
+ /* Allocate the output closest format */
+ WAVEFORMATEXTENSIBLE *pwfe_closest =
+ CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
+ if (!pwfe_closest)
+ return E_FAIL;
+ WAVEFORMATEX *pwf_closest = &pwfe_closest->Format;
+
+ /* Setup the fallback arrays. There are 3 properties to check: the format,
+ * the samplerate and the channels. There are maximum of 4 formats to
+ * check, 3 samplerates, and 2 channels configuration. So, that is a
+ * maximum of 4x3x2=24 checks */
+
+ /* The format fallback order is dependent of the input format. We don't
+ * want to use a high quality format when it's not needed but we prefer to
+ * use a high quality format instead of a lower one */
+ static const uint16_t bits_pcm8_fallback[] = { 8, 16, 24, 32 };
+ static const uint16_t bits_pcm16_fallback[] = { 16, 24, 32, 8 };
+ static const uint16_t bits_pcm24_fallback[] = { 24, 32, 16, 8 };
+ static const uint16_t bits_pcm32_fallback[] = { 32, 24, 16, 8 };
+
+ static const size_t bits_fallback_size = ARRAY_SIZE(bits_pcm8_fallback);
+
+ const uint16_t *bits_fallback;
+ switch (pwf->wBitsPerSample)
+ {
+ case 64: /* fall through */
+ case 32: bits_fallback = bits_pcm32_fallback; break;
+ case 24: bits_fallback = bits_pcm24_fallback; break;
+ case 16: bits_fallback = bits_pcm16_fallback; break;
+ case 8: bits_fallback = bits_pcm8_fallback; break;
+ default: vlc_assert_unreachable();
+ }
+
+ /* Check the input samplerate, then 48kHz and 44.1khz */
+ const uint32_t samplerate_fallback[] = {
+ pwf->nSamplesPerSec,
+ pwf->nSamplesPerSec == 48000 ? 0 : 48000,
+ pwf->nSamplesPerSec == 44100 ? 0 : 44100,
+ };
+ const size_t samplerate_fallback_size = ARRAY_SIZE(samplerate_fallback);
+
+ /* Check the input number of channels, then stereo */
+ const uint16_t channels_fallback[] = {
+ pwf->nChannels,
+ pwf->nChannels == 2 ? 0 : 2,
+ };
+ const size_t channels_fallback_size = ARRAY_SIZE(channels_fallback);
+
+ /* Let's try everything */
+ for (size_t bits_idx = 0; bits_idx < bits_fallback_size; ++bits_idx)
+ {
+ uint16_t bits = bits_fallback[bits_idx];
+
+ for (size_t samplerate_idx = 0;
+ samplerate_idx < samplerate_fallback_size;
+ ++samplerate_idx)
+ {
+ const uint32_t samplerate = samplerate_fallback[samplerate_idx];
+
+ if (samplerate == 0)
+ continue;
+
+ for (size_t channels_idx = 0;
+ channels_idx < channels_fallback_size;
+ ++channels_idx)
+ {
+ const uint16_t channels = channels_fallback[channels_idx];
+ if (channels == 0)
+ continue;
+
+ pwfe_closest->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ pwfe_closest->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ pwfe_closest->Format.nSamplesPerSec = samplerate;
+ pwfe_closest->Format.wBitsPerSample = bits;
+ pwfe_closest->Samples.wValidBitsPerSample = bits;
+ pwfe_closest->Format.nChannels = channels;
+ pwfe_closest->Format.nBlockAlign = bits / 8 * channels;
+ pwfe_closest->Format.nAvgBytesPerSec =
+ pwfe_closest->Format.nBlockAlign * samplerate;
+
+ if (channels == pwf->nChannels)
+ {
+ /* Use The input channel configuration */
+ pwfe_closest->dwChannelMask = pwfe->dwChannelMask;
+ }
+ else
+ {
+ assert(channels == 2);
+ pwfe_closest->dwChannelMask =
+ SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
+ }
+ pwfe_closest->Format.cbSize = pwfe->Format.cbSize;
+
+ hr = IAudioClient_IsFormatSupported(c, exclusive,
+ pwf_closest, NULL);
+
+ if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
+ {
+ if (hr == S_OK)
+ {
+ *ppwf_closest = pwf_closest;
+ /* return S_FALSE when the closest format need to be
+ * used (like IAudioClient_IsFormatSupported()) */
+ return S_FALSE;
+ }
+ /* Unknown error */
+ assert(hr != S_FALSE);
+ CoTaskMemFree(pwfe_closest);
+ return hr;
+ }
+ }
+ }
+ }
+
+ /* No format found */
+ CoTaskMemFree(pwfe_closest);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
+}
+
static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
const GUID *sid)
{
@@ -640,8 +788,6 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
}
else if (AOUT_FMT_LINEAR(&fmt))
{
- shared_mode = AUDCLNT_SHAREMODE_SHARED;
-
if (fmt.channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
{
fmt.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
@@ -663,8 +809,19 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
buffer_duration = MSFTIME_FROM_VLC_TICK(AOUT_MAX_PREPARE_TIME);
}
- hr = IAudioClient_IsFormatSupported(sys->client, shared_mode,
- pwf, &pwf_closest);
+ /* Cache the var in the parent object (audio_output_t). */
+ if (var_CreateGetBool(vlc_object_parent(s), "wasapi-exclusive"))
+ {
+ shared_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
+ buffer_duration = MSFTIME_FROM_MS(200);
+ hr = GetExclusivePCMFormat(sys->client, pwf, &pwf_closest);
+ }
+ else
+ {
+ shared_mode = AUDCLNT_SHAREMODE_SHARED;
+ hr = IAudioClient_IsFormatSupported(sys->client, shared_mode,
+ pwf, &pwf_closest);
+ }
}
else
hr = E_FAIL;
@@ -748,10 +905,19 @@ error:
return hr;
}
+#define WASAPI_EXCLUSIVE_TEXT N_("Use exclusive mode")
+#define WASAPI_EXCLUSIVE_LONGTEXT N_( \
+ "VLC will have a direct connection of the audio endpoint device. " \
+ "This mode can be used to reduce the audio latency or " \
+ "to assure that the audio stream won't modified by the OS. " \
+ "This mode is more likely to fail if the soundcard format not handled by VLC.")
+
vlc_module_begin()
set_shortname("WASAPI")
set_description(N_("Windows Audio Session API output"))
set_capability("aout stream", 50)
+ add_bool("wasapi-exclusive", false, WASAPI_EXCLUSIVE_TEXT,
+ WASAPI_EXCLUSIVE_LONGTEXT, true)
set_category(CAT_AUDIO)
set_subcategory(SUBCAT_AUDIO_AOUT)
set_callback(Start)
--
2.20.1
More information about the vlc-devel
mailing list