]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/wasapi.c
demux: mp4: don't NULL dereference on failed realloc
[vlc] / modules / audio_output / wasapi.c
index 14672be46ccfb33b3f3b7f5826f32b63cdbd259e..7a4716177aa550908cd21cd80b209ba7e733a14a 100644 (file)
 #include <stdlib.h>
 #include <assert.h>
 #include <audioclient.h>
-#include <mmdeviceapi.h>
 
 #include <vlc_common.h>
 #include <vlc_aout.h>
-#include "mmdevice.h"
+#include <vlc_plugin.h>
+#include "audio_output/mmdevice.h"
 
 static LARGE_INTEGER freq; /* performance counters frequency */
 
@@ -72,10 +72,10 @@ typedef struct aout_stream_sys
     uint8_t chans_table[AOUT_CHAN_MAX];
     uint8_t chans_to_reorder;
 
-    uint8_t bits; /**< Bits per sample */
+    vlc_fourcc_t format; /**< Sample format */
     unsigned rate; /**< Sample rate */
     unsigned bytes_per_frame;
-    UINT32 written; /**< Frames written to the buffer */
+    UINT64 written; /**< Frames written to the buffer */
     UINT32 frames; /**< Total buffer size (frames) */
 } aout_stream_sys_t;
 
@@ -85,36 +85,38 @@ static HRESULT TimeGet(aout_stream_t *s, mtime_t *restrict delay)
 {
     aout_stream_sys_t *sys = s->sys;
     void *pv;
-    UINT64 pos, qpcpos;
+    UINT64 pos, qpcpos, freq;
     HRESULT hr;
 
     hr = IAudioClient_GetService(sys->client, &IID_IAudioClock, &pv);
-    if (SUCCEEDED(hr))
+    if (FAILED(hr))
     {
-        IAudioClock *clock = pv;
-
-        hr = IAudioClock_GetPosition(clock, &pos, &qpcpos);
-        if (FAILED(hr))
-            msg_Err(s, "cannot get position (error 0x%lx)", hr);
-        IAudioClock_Release(clock);
-    }
-    else
         msg_Err(s, "cannot get clock (error 0x%lx)", hr);
+        return hr;
+    }
+
+    IAudioClock *clock = pv;
 
+    hr = IAudioClock_GetPosition(clock, &pos, &qpcpos);
     if (SUCCEEDED(hr))
+        hr = IAudioClock_GetFrequency(clock, &freq);
+    IAudioClock_Release(clock);
+    if (FAILED(hr))
     {
-        if (pos != 0)
-        {
-            *delay = ((GetQPC() - qpcpos) / (10000000 / CLOCK_FREQ));
-            static_assert((10000000 % CLOCK_FREQ) == 0,
-                          "Frequency conversion broken");
-        }
-        else
-        {
-            *delay = sys->written * CLOCK_FREQ / sys->rate;
-            msg_Dbg(s, "extrapolating position: still propagating buffers");
-        }
+        msg_Err(s, "cannot get position (error 0x%lx)", hr);
+        return hr;
     }
+
+    lldiv_t w = lldiv(sys->written, sys->rate);
+    lldiv_t r = lldiv(pos, freq);
+
+    static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
+
+    *delay = ((w.quot - r.quot) * CLOCK_FREQ)
+           + ((w.rem * CLOCK_FREQ) / sys->rate)
+           - ((r.rem * CLOCK_FREQ) / freq)
+           - ((GetQPC() - qpcpos) / (10000000 / CLOCK_FREQ));
+
     return hr;
 }
 
@@ -126,7 +128,7 @@ static HRESULT Play(aout_stream_t *s, block_t *block)
 
     if (sys->chans_to_reorder)
         aout_ChannelReorder(block->p_buffer, block->i_buffer,
-                          sys->chans_to_reorder, sys->chans_table, sys->bits);
+                          sys->chans_to_reorder, sys->chans_table, sys->format);
 
     hr = IAudioClient_GetService(sys->client, &IID_IAudioRenderClient, &pv);
     if (FAILED(hr))
@@ -178,8 +180,7 @@ static HRESULT Play(aout_stream_t *s, block_t *block)
             break; /* done */
 
         /* Out of buffer space, sleep */
-        msleep(AOUT_MIN_PREPARE_TIME
-             + block->i_nb_samples * CLOCK_FREQ / sys->rate);
+        msleep(sys->frames * (CLOCK_FREQ / 2) / sys->rate);
     }
     IAudioRenderClient_Release(render);
 out:
@@ -211,10 +212,13 @@ static HRESULT Flush(aout_stream_t *s)
     IAudioClient_Stop(sys->client);
 
     hr = IAudioClient_Reset(sys->client);
-    if (FAILED(hr))
-        msg_Warn(s, "cannot reset stream (error 0x%lx)", hr);
-    else
+    if (SUCCEEDED(hr))
+    {
+        msg_Dbg(s, "reset");
         sys->written = 0;
+    }
+    else
+        msg_Warn(s, "cannot reset stream (error 0x%lx)", hr);
     return hr;
 }
 
@@ -244,7 +248,6 @@ static void vlc_ToWave(WAVEFORMATEXTENSIBLE *restrict wf,
             wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
             break;
 
-        case VLC_CODEC_S8:
         case VLC_CODEC_U8:
             audio->i_format = VLC_CODEC_S16N;
         case VLC_CODEC_S16N:
@@ -284,10 +287,44 @@ static int vlc_FromWave(const WAVEFORMATEX *restrict wf,
     {
         const WAVEFORMATEXTENSIBLE *wfe = (void *)wf;
 
+        if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+        {
+            switch (wf->wBitsPerSample)
+            {
+                case 64:
+                    audio->i_format = VLC_CODEC_FL64;
+                    break;
+                case 32:
+                    audio->i_format = VLC_CODEC_FL32;
+                    break;
+                default:
+                    return -1;
+            }
+        }
+        else if (IsEqualIID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
+        {
+            switch (wf->wBitsPerSample)
+            {
+                case 32:
+                    audio->i_format = VLC_CODEC_S32N;
+                    break;
+                case 16:
+                    audio->i_format = VLC_CODEC_S16N;
+                    break;
+                default:
+                    return -1;
+            }
+        }
+
+        if (wfe->Samples.wValidBitsPerSample != wf->wBitsPerSample)
+            return -1;
+
         for (unsigned i = 0; chans_in[i]; i++)
             if (wfe->dwChannelMask & chans_in[i])
                 audio->i_physical_channels |= pi_vlc_chan_order_wg4[i];
     }
+    else
+        return -1;
 
     audio->i_original_channels = audio->i_physical_channels;
     aout_FormatPrepare (audio);
@@ -312,17 +349,19 @@ static unsigned vlc_CheckWaveOrder (const WAVEFORMATEX *restrict wf,
 }
 
 static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict fmt,
-                     IMMDevice *dev, const GUID *sid)
+                     const GUID *sid)
 {
+    if (!s->b_force && var_InheritBool(s, "spdif") && AOUT_FMT_SPDIF(fmt))
+        /* Fallback to other plugin until pass-through is implemented */
+        return E_NOTIMPL;
+
     aout_stream_sys_t *sys = malloc(sizeof (*sys));
     if (unlikely(sys == NULL))
         return E_OUTOFMEMORY;
     sys->client = NULL;
 
     void *pv;
-    HRESULT hr;
-
-    hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_ALL, NULL, &pv);
+    HRESULT hr = aout_stream_Activate(s, &IID_IAudioClient, NULL, &pv);
     if (FAILED(hr))
     {
         msg_Err(s, "cannot activate client (error 0x%lx)", hr);
@@ -360,7 +399,7 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict fmt,
 
     sys->chans_to_reorder = vlc_CheckWaveOrder((hr == S_OK) ? &wf.Format : pwf,
                                                sys->chans_table);
-    sys->bits = fmt->i_bitspersample;
+    sys->format = fmt->i_format;
 
     hr = IAudioClient_Initialize(sys->client, AUDCLNT_SHAREMODE_SHARED, 0,
                                  AOUT_MAX_PREPARE_TIME * 10, 0,
@@ -378,6 +417,16 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict fmt,
         msg_Err(s, "cannot get buffer size (error 0x%lx)", hr);
         goto error;
     }
+    msg_Dbg(s, "buffer size    : %"PRIu32" frames", sys->frames);
+
+    REFERENCE_TIME latT, defT, minT;
+    if (SUCCEEDED(IAudioClient_GetStreamLatency(sys->client, &latT))
+     && SUCCEEDED(IAudioClient_GetDevicePeriod(sys->client, &defT, &minT)))
+    {
+        msg_Dbg(s, "maximum latency: %"PRIu64"00 ns", latT);
+        msg_Dbg(s, "default period : %"PRIu64"00 ns", defT);
+        msg_Dbg(s, "minimum period : %"PRIu64"00 ns", minT);
+    }
 
     sys->rate = fmt->i_rate;
     sys->bytes_per_frame = fmt->i_bytes_per_frame;
@@ -401,16 +450,15 @@ static void Stop(aout_stream_t *s)
 
     IAudioClient_Stop(sys->client); /* should not be needed */
     IAudioClient_Release(sys->client);
-}
 
-HRESULT aout_stream_Start(aout_stream_t *s,
-                          audio_sample_format_t *restrict fmt,
-                          IMMDevice *dev, const GUID *sid)
-{
-    return Start(s, fmt, dev, sid);
+    free(sys);
 }
 
-void aout_stream_Stop(aout_stream_t *s)
-{
-    Stop(s);
-}
+vlc_module_begin()
+    set_shortname("WASAPI")
+    set_description(N_("Windows Audio Session API output"))
+    set_capability("aout stream", 50)
+    set_category(CAT_AUDIO)
+    set_subcategory(SUBCAT_AUDIO_AOUT)
+    set_callbacks(Start, Stop)
+vlc_module_end()