[vlc-devel] [PATCH V3 4/4] wasapi: add an option to handle exclusive mode

Thomas Guillem thomas at gllm.fr
Tue Nov 26 16:31:26 CET 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..2e8b4ff52ad 100644
--- a/modules/audio_output/wasapi.c
+++ b/modules/audio_output/wasapi.c
@@ -588,6 +588,153 @@ 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 candidates 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_candidates[] =  {  8, 16, 24, 32 };
+    static const uint16_t bits_pcm16_candidates[] = { 16, 24, 32, 8  };
+    static const uint16_t bits_pcm24_candidates[] = { 24, 32, 16, 8  };
+    static const uint16_t bits_pcm32_candidates[] = { 32, 24, 16, 8  };
+
+    static const size_t bits_candidates_size = ARRAY_SIZE(bits_pcm8_candidates);
+
+    const uint16_t *bits_candidates;
+    switch (pwf->wBitsPerSample)
+    {
+        case 64: /* fall through */
+        case 32: bits_candidates = bits_pcm32_candidates; break;
+        case 24: bits_candidates = bits_pcm24_candidates; break;
+        case 16: bits_candidates = bits_pcm16_candidates; break;
+        case 8:  bits_candidates = bits_pcm8_candidates;  break;
+        default: vlc_assert_unreachable();
+    }
+
+    /* Check the input samplerate, then 48kHz and 44.1khz */
+    const uint32_t samplerate_candidates[] = {
+        pwf->nSamplesPerSec,
+        pwf->nSamplesPerSec == 48000 ? 0 : 48000,
+        pwf->nSamplesPerSec == 44100 ? 0 : 44100,
+    };
+    const size_t samplerate_candidates_size = ARRAY_SIZE(samplerate_candidates);
+
+    /* Check the input number of channels, then stereo */
+    const uint16_t channels_candidates[] = {
+        pwf->nChannels,
+        pwf->nChannels == 2 ? 0 : 2,
+    };
+    const size_t channels_candidates_size = ARRAY_SIZE(channels_candidates);
+
+    /* Let's try everything */
+    for (size_t bits_idx = 0; bits_idx < bits_candidates_size; ++bits_idx)
+    {
+        uint16_t bits = bits_candidates[bits_idx];
+
+        for (size_t samplerate_idx = 0;
+             samplerate_idx < samplerate_candidates_size;
+             ++samplerate_idx)
+        {
+            const uint32_t samplerate = samplerate_candidates[samplerate_idx];
+
+            if (samplerate == 0)
+                continue;
+
+            for (size_t channels_idx = 0;
+                 channels_idx < channels_candidates_size;
+                 ++channels_idx)
+            {
+                const uint16_t channels = channels_candidates[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;
+                    }
+                    assert(hr != S_FALSE); /* S_FALSE reserved for shared mode */
+                    CoTaskMemFree(pwfe_closest);
+                    return hr; /* Unknown error */
+                }
+            }
+        }
+    }
+
+    /* 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 +787,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 +808,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,11 +904,21 @@ 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 be modified by the OS. " \
+    "This mode is more likely to fail if the soundcard format is not " \
+    "handled by VLC.")
+
 vlc_module_begin()
     set_shortname("WASAPI")
     set_description(N_("Windows Audio Session API output"))
     set_capability("aout stream", 50)
     set_category(CAT_AUDIO)
+    add_bool("wasapi-exclusive", false, WASAPI_EXCLUSIVE_TEXT,
+             WASAPI_EXCLUSIVE_LONGTEXT, true)
     set_subcategory(SUBCAT_AUDIO_AOUT)
     set_callback(Start)
 vlc_module_end()
-- 
2.20.1



More information about the vlc-devel mailing list