[vlc-commits] [Git][videolan/vlc][master] 13 commits: mmdevice: move owner outside aout_stream_t

Steve Lhomme (@robUx4) gitlab at videolan.org
Fri Sep 30 07:33:07 UTC 2022



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
3742acc5 by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: move owner outside aout_stream_t

- - - - -
d09b0a78 by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: rename owner functions

These functions must be called by the owner, and not by stream modules.

- - - - -
46e37bd9 by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: move mainloop in a new function

No functional changes.

- - - - -
e0373ddf by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: report mute after setting it

Use the same behavior than for volume.

- - - - -
d5fe154f by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: report volume and mute only when necessary

When volume or mute was changed, both volume and mute were reported.

- - - - -
fc3fe81e by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: replace CS and CV by vlc cond/mutex

- - - - -
e6b41b44 by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: use an Event instead of a CV

In order to also wait for the Wasapi event.

- - - - -
1b43ce2f by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: add asynchronous play helpers

- - - - -
4521fb45 by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: implement nonblocking play

The playback is now done by the MMSession thread.
This thread will wait via the Wasapi Event handle if the audio buffer is
full.

- - - - -
6cd74e1c by Thomas Guillem at 2022-09-30T06:33:18+00:00
winstore: replace CS by vlc mutex

- - - - -
6dc6d7c1 by Thomas Guillem at 2022-09-30T06:33:18+00:00
winstore: implement nonblocking play

- - - - -
9157a849 by Thomas Guillem at 2022-09-30T06:33:18+00:00
wasapi: set play non-blocking

Create a thread from Start() that will do the playback.

- - - - -
02c95ad3 by Thomas Guillem at 2022-09-30T06:33:18+00:00
mmdevice: factorize aout_stream_owner creation/deletion

aout_stream_owner_New() return void * and use a size_t because the "aout
module" might want to allocate extra private data. This will be
necessary with multiple stream playbacks.

- - - - -


4 changed files:

- modules/audio_output/mmdevice.c
- modules/audio_output/mmdevice.h
- modules/audio_output/wasapi.c
- modules/audio_output/winstore.c


Changes:

=====================================
modules/audio_output/mmdevice.c
=====================================
@@ -76,7 +76,7 @@ static char default_device_b[1] = "";
 
 typedef struct
 {
-    aout_stream_t *stream; /**< Underlying audio output stream */
+    struct aout_stream_owner *stream; /**< Underlying audio output stream */
     audio_output_t *aout;
     IMMDeviceEnumerator *it; /**< Device enumerator, NULL when exiting */
     IMMDevice *dev; /**< Selected output device, NULL if none */
@@ -94,9 +94,9 @@ typedef struct
     signed char requested_mute; /**< Requested mute, negative if none */
     wchar_t *acquired_device; /**< Acquired device identifier, NULL if none */
     bool request_device_restart;
-    CRITICAL_SECTION lock;
-    CONDITION_VARIABLE work;
-    CONDITION_VARIABLE ready;
+    HANDLE work_event;
+    vlc_mutex_t lock;
+    vlc_cond_t ready;
     vlc_thread_t thread; /**< Thread for audio session control */
 } aout_sys_t;
 
@@ -129,7 +129,9 @@ static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
     HRESULT hr;
 
     EnterMTA();
-    hr = aout_stream_TimeGet(sys->stream, delay);
+    vlc_mutex_lock(&sys->lock);
+    hr = aout_stream_owner_TimeGet(sys->stream, delay);
+    vlc_mutex_unlock(&sys->lock);
     LeaveMTA();
 
     return SUCCEEDED(hr) ? 0 : -1;
@@ -138,13 +140,11 @@ static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
 static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
 {
     aout_sys_t *sys = aout->sys;
-    HRESULT hr;
-
-    EnterMTA();
-    hr = aout_stream_Play(sys->stream, block, date);
-    LeaveMTA();
 
-    vlc_FromHR(aout, hr);
+    vlc_mutex_lock(&sys->lock);
+    aout_stream_owner_AppendBlock(sys->stream, block, date);
+    vlc_mutex_unlock(&sys->lock);
+    SetEvent(sys->work_event);
 }
 
 static void Pause(audio_output_t *aout, bool paused, vlc_tick_t date)
@@ -153,7 +153,9 @@ static void Pause(audio_output_t *aout, bool paused, vlc_tick_t date)
     HRESULT hr;
 
     EnterMTA();
-    hr = aout_stream_Pause(sys->stream, paused);
+    vlc_mutex_lock(&sys->lock);
+    hr = aout_stream_owner_Pause(sys->stream, paused);
+    vlc_mutex_unlock(&sys->lock);
     LeaveMTA();
 
     vlc_FromHR(aout, hr);
@@ -166,7 +168,9 @@ static void Flush(audio_output_t *aout)
     HRESULT hr;
 
     EnterMTA();
-    hr = aout_stream_Flush(sys->stream);
+    vlc_mutex_lock(&sys->lock);
+    hr = aout_stream_owner_Flush(sys->stream);
+    vlc_mutex_unlock(&sys->lock);
     LeaveMTA();
 
     vlc_FromHR(aout, hr);
@@ -194,11 +198,11 @@ static int VolumeSet(audio_output_t *aout, float vol)
 {
     aout_sys_t *sys = aout->sys;
 
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     int ret = VolumeSetLocked(aout, vol);
     aout_GainRequest(aout, sys->gain);
-    WakeConditionVariable(&sys->work);
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
+    SetEvent(sys->work_event);
     return ret;
 }
 
@@ -206,10 +210,10 @@ static int MuteSet(audio_output_t *aout, bool mute)
 {
     aout_sys_t *sys = aout->sys;
 
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     sys->requested_mute = mute;
-    WakeConditionVariable(&sys->work);
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
+    SetEvent(sys->work_event);
     return 0;
 }
 
@@ -280,9 +284,7 @@ vlc_AudioSessionEvents_OnSimpleVolumeChanged(IAudioSessionEvents *this,
 
     msg_Dbg(aout, "simple volume changed: %f, muting %sabled", vol,
             mute ? "en" : "dis");
-    EnterCriticalSection(&sys->lock);
-    WakeConditionVariable(&sys->work); /* implicit state: vol & mute */
-    LeaveCriticalSection(&sys->lock);
+    SetEvent(sys->work_event); /* implicit state: vol & mute */
     (void) ctx;
     return S_OK;
 }
@@ -582,14 +584,14 @@ vlc_MMNotificationClient_OnDefaultDeviceChange(IMMNotificationClient *this,
     if (role != eConsole)
         return S_OK;
 
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     if (sys->acquired_device == NULL || sys->acquired_device == default_device)
     {
         msg_Dbg(aout, "default device changed: %ls", wid);
         sys->request_device_restart = true;
         aout_RestartRequest(aout, AOUT_RESTART_OUTPUT);
     }
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
 
     return S_OK;
 }
@@ -736,9 +738,9 @@ static int DeviceRequestLocked(audio_output_t *aout)
 
     sys->request_device_restart = false;
 
-    WakeConditionVariable(&sys->work);
+    SetEvent(sys->work_event);
     while (sys->requested_device != NULL)
-        SleepConditionVariableCS(&sys->ready, &sys->lock, INFINITE);
+        vlc_cond_wait(&sys->ready, &sys->lock);
 
     if (sys->stream != NULL && sys->dev != NULL)
         /* Request restart of stream with the new device */
@@ -775,12 +777,101 @@ static int DeviceRestartLocked(audio_output_t *aout)
 static int DeviceSelect(audio_output_t *aout, const char *id)
 {
     aout_sys_t *sys = aout->sys;
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     int ret = DeviceSelectLocked(aout, id);
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
     return ret;
 }
 
+/**
+ * Main loop
+ *
+ * Adjust volume as long as device is unchanged
+ * */
+static void MMSessionMainloop(audio_output_t *aout, ISimpleAudioVolume *volume)
+{
+    aout_sys_t *sys = aout->sys;
+    HRESULT hr;
+
+    bool report_volume = true;
+    bool report_mute = true;
+
+    while (sys->requested_device == NULL)
+    {
+        if (volume != NULL)
+        {
+            if (sys->requested_volume >= 0.f)
+            {
+                hr = ISimpleAudioVolume_SetMasterVolume(volume, sys->requested_volume, NULL);
+                if (FAILED(hr))
+                    msg_Err(aout, "cannot set master volume (error 0x%lX)",
+                            hr);
+                report_volume = true;
+                sys->requested_volume = -1.f;
+            }
+
+            if (report_volume)
+            {
+                float level;
+                hr = ISimpleAudioVolume_GetMasterVolume(volume, &level);
+                if (SUCCEEDED(hr))
+                    aout_VolumeReport(aout, cbrtf(level * sys->gain));
+                else
+                    msg_Err(aout, "cannot get master volume (error 0x%lX)", hr);
+                report_volume = false;
+            }
+
+            if (sys->requested_mute >= 0)
+            {
+                BOOL mute = sys->requested_mute ? TRUE : FALSE;
+
+                hr = ISimpleAudioVolume_SetMute(volume, mute, NULL);
+                if (FAILED(hr))
+                    msg_Err(aout, "cannot set mute (error 0x%lX)", hr);
+                report_mute = true;
+                sys->requested_mute = -1;
+            }
+
+            if (report_mute)
+            {
+                BOOL mute;
+                hr = ISimpleAudioVolume_GetMute(volume, &mute);
+                if (SUCCEEDED(hr))
+                    aout_MuteReport(aout, mute != FALSE);
+                else
+                    msg_Err(aout, "cannot get mute (error 0x%lX)", hr);
+                report_mute = false;
+            }
+        }
+
+        DWORD ev_count = 1;
+        HANDLE events[2] = {
+            sys->work_event,
+            NULL
+        };
+        /* Don't listen to the stream event if the block fifo is empty */
+        if (sys->stream != NULL && sys->stream->chain != NULL)
+            events[ev_count++] = sys->stream->buffer_ready_event;
+
+        vlc_mutex_unlock(&sys->lock);
+        WaitForMultipleObjects(ev_count, events, FALSE, INFINITE);
+        vlc_mutex_lock(&sys->lock);
+
+        if (sys->stream != NULL)
+        {
+            hr = aout_stream_owner_PlayAll(sys->stream);
+            /* Don't call vlc_FromHR here since this function waits for the
+             * current thread */
+            if (unlikely(hr == AUDCLNT_E_DEVICE_INVALIDATED ||
+                         hr == AUDCLNT_E_RESOURCES_INVALIDATED))
+            {
+                sys->requested_device = default_device;
+                /* The restart of the stream will be requested asynchronously */
+            }
+        }
+    }
+}
+
 /*** Initialization / deinitialization **/
 /** MMDevice audio output thread.
  * This thread takes cares of the audio session control. Inconveniently enough,
@@ -839,7 +930,7 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
     }
 
     sys->requested_device = NULL;
-    WakeConditionVariable(&sys->ready);
+    vlc_cond_signal(&sys->ready);
 
     if (SUCCEEDED(hr))
     {   /* Report actual device */
@@ -961,52 +1052,9 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
     else
         msg_Err(aout, "cannot activate endpoint volume (error 0x%lX)", hr);
 
-    /* Main loop (adjust volume as long as device is unchanged) */
-    while (sys->requested_device == NULL)
-    {
-        if (volume != NULL)
-        {
-            float level;
-
-            level = sys->requested_volume;
-            if (level >= 0.f)
-            {
-                hr = ISimpleAudioVolume_SetMasterVolume(volume, level, NULL);
-                if (FAILED(hr))
-                    msg_Err(aout, "cannot set master volume (error 0x%lX)",
-                            hr);
-            }
-            sys->requested_volume = -1.f;
-
-            hr = ISimpleAudioVolume_GetMasterVolume(volume, &level);
-            if (SUCCEEDED(hr))
-                aout_VolumeReport(aout, cbrtf(level * sys->gain));
-            else
-                msg_Err(aout, "cannot get master volume (error 0x%lX)", hr);
-
-            BOOL mute;
-
-            hr = ISimpleAudioVolume_GetMute(volume, &mute);
-            if (FAILED(hr))
-                msg_Err(aout, "cannot get mute (error 0x%lX)", hr);
-
-            if (sys->requested_mute >= 0)
-            {
-                mute = sys->requested_mute ? TRUE : FALSE;
-
-                hr = ISimpleAudioVolume_SetMute(volume, mute, NULL);
-                if (FAILED(hr))
-                    msg_Err(aout, "cannot set mute (error 0x%lX)", hr);
-            }
-            sys->requested_mute = -1;
+    MMSessionMainloop(aout, volume);
 
-            if (SUCCEEDED(hr))
-                aout_MuteReport(aout, mute != FALSE);
-        }
-
-        SleepConditionVariableCS(&sys->work, &sys->lock, INFINITE);
-    }
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
 
     if (endpoint != NULL)
         IAudioEndpointVolume_Release(endpoint);
@@ -1036,7 +1084,7 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
         IAudioSessionManager_Release(manager);
     }
 
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     IMMDevice_Release(sys->dev);
     sys->dev = NULL;
     return S_OK;
@@ -1064,14 +1112,18 @@ static void *MMThread(void *data)
     if (FAILED(hr))
         msg_Warn(aout, "cannot enumerate audio endpoints (error 0x%lX)", hr);
 
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
 
     do
         if (sys->requested_device == NULL || FAILED(MMSession(aout, it)))
-            SleepConditionVariableCS(&sys->work, &sys->lock, INFINITE);
+        {
+            vlc_mutex_unlock(&sys->lock);
+            WaitForSingleObject(sys->work_event, INFINITE);
+            vlc_mutex_lock(&sys->lock);
+        }
     while (sys->it != NULL);
 
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
 
     IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(it,
                                                           &sys->device_events);
@@ -1127,23 +1179,23 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
         }
     }
 
-    aout_stream_t *s = vlc_object_create(aout, sizeof (*s));
-    if (unlikely(s == NULL))
+    struct aout_stream_owner *owner =
+        aout_stream_owner_New(aout, sizeof (*owner), ActivateDevice);
+    if (unlikely(owner == NULL))
         return -1;
-
-    s->owner.activate = ActivateDevice;
+    aout_stream_t *s = &owner->s;
 
     EnterMTA();
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
 
     if ((sys->request_device_restart && DeviceRestartLocked(aout) != 0)
       || sys->dev == NULL)
     {
         /* Error if the device restart failed or if a request previously
          * failed. */
-        LeaveCriticalSection(&sys->lock);
+        vlc_mutex_unlock(&sys->lock);
         LeaveMTA();
-        vlc_object_delete(s);
+        aout_stream_owner_Delete(owner);
         return -1;
     }
 
@@ -1153,7 +1205,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
     {
         char *modlist = var_InheritString(aout, "mmdevice-backend");
         HRESULT hr;
-        s->owner.device = sys->dev;
+        owner->device = sys->dev;
 
         module = vlc_module_load(s, "aout stream", modlist,
                                  false, aout_stream_Start, s, fmt, &hr);
@@ -1208,17 +1260,20 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
         }
     }
 
-    LeaveCriticalSection(&sys->lock);
-    LeaveMTA();
-
     if (module == NULL)
     {
-        vlc_object_delete(s);
+        aout_stream_owner_Delete(owner);
+        vlc_mutex_unlock(&sys->lock);
+        LeaveMTA();
         return -1;
     }
 
     assert (sys->stream == NULL);
-    sys->stream = s;
+    sys->stream = owner;
+
+    vlc_mutex_unlock(&sys->lock);
+    LeaveMTA();
+
     aout_GainRequest(aout, sys->gain);
     return 0;
 }
@@ -1230,10 +1285,10 @@ static void Stop(audio_output_t *aout)
     assert(sys->stream != NULL);
 
     EnterMTA();
-    aout_stream_Stop(sys->stream);
+    aout_stream_owner_Stop(sys->stream);
     LeaveMTA();
 
-    vlc_object_delete(sys->stream);
+    aout_stream_owner_Delete(sys->stream);
     sys->stream = NULL;
 }
 
@@ -1265,9 +1320,12 @@ static int Open(vlc_object_t *obj)
     if (!var_CreateGetBool(aout, "volume-save"))
         VolumeSetLocked(aout, var_InheritFloat(aout, "mmdevice-volume"));
 
-    InitializeCriticalSection(&sys->lock);
-    InitializeConditionVariable(&sys->work);
-    InitializeConditionVariable(&sys->ready);
+    vlc_mutex_init(&sys->lock);
+    vlc_cond_init(&sys->ready);
+
+    sys->work_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (unlikely(sys->work_event == NULL))
+        goto error;
 
     aout_HotplugReport(aout, default_device_b, _("Default"));
 
@@ -1308,10 +1366,10 @@ static int Open(vlc_object_t *obj)
         goto error;
     }
 
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     while (sys->requested_device != NULL)
-        SleepConditionVariableCS(&sys->ready, &sys->lock, INFINITE);
-    LeaveCriticalSection(&sys->lock);
+        vlc_cond_wait(&sys->ready, &sys->lock);
+    vlc_mutex_unlock(&sys->lock);
     LeaveMTA(); /* Leave MTA after thread has entered MTA */
 
     aout->start = Start;
@@ -1327,7 +1385,8 @@ static int Open(vlc_object_t *obj)
     return VLC_SUCCESS;
 
 error:
-    DeleteCriticalSection(&sys->lock);
+    if (sys->work_event != NULL)
+        CloseHandle(sys->work_event);
     free(sys);
     return VLC_EGENERIC;
 }
@@ -1337,14 +1396,15 @@ static void Close(vlc_object_t *obj)
     audio_output_t *aout = (audio_output_t *)obj;
     aout_sys_t *sys = aout->sys;
 
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     sys->requested_device = default_device; /* break out of MMSession() loop */
     sys->it = NULL; /* break out of MMThread() loop */
-    WakeConditionVariable(&sys->work);
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
+
+    SetEvent(sys->work_event);
 
     vlc_join(sys->thread, NULL);
-    DeleteCriticalSection(&sys->lock);
+    CloseHandle(sys->work_event);
 
     free(sys);
 }


=====================================
modules/audio_output/mmdevice.h
=====================================
@@ -41,14 +41,29 @@ struct aout_stream
     HRESULT (*play)(aout_stream_t *, block_t *, vlc_tick_t);
     HRESULT (*pause)(aout_stream_t *, bool);
     HRESULT (*flush)(aout_stream_t *);
+};
 
-    struct
-    {
-        void *device;
-        HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **);
-    } owner;
+struct aout_stream_owner
+{
+    aout_stream_t s;
+    void *device;
+    HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **);
+    HANDLE buffer_ready_event;
+
+    block_t *chain;
+    block_t **last;
 };
 
+/*
+ * "aout output" helpers
+ */
+
+static inline
+struct aout_stream_owner *aout_stream_owner(aout_stream_t *s)
+{
+    return container_of(s, struct aout_stream_owner, s);
+}
+
 /**
  * Creates an audio output stream on a given Windows multimedia device.
  * \param s audio output stream object to be initialized
@@ -61,36 +76,140 @@ typedef HRESULT (*aout_stream_start_t)(aout_stream_t *s,
 /**
  * Destroys an audio output stream.
  */
-static inline void aout_stream_Stop(aout_stream_t *s)
+static inline
+void aout_stream_owner_Stop(struct aout_stream_owner *owner)
+{
+    owner->s.stop(&owner->s);
+}
+
+static inline
+HRESULT aout_stream_owner_TimeGet(struct aout_stream_owner *owner,
+                                  vlc_tick_t *delay)
 {
-    (s->stop)(s);
+    HRESULT hr = owner->s.time_get(&owner->s, delay);
+
+    if (SUCCEEDED(hr))
+    {
+        /* Add the block chain delay */
+        vlc_tick_t length;
+        block_ChainProperties(owner->chain, NULL, NULL, &length);
+        *delay += length;
+    }
+    return hr;
 }
 
-static inline HRESULT aout_stream_TimeGet(aout_stream_t *s, vlc_tick_t *delay)
+static inline
+HRESULT aout_stream_owner_Play(struct aout_stream_owner *owner,
+                               block_t *block, vlc_tick_t date)
 {
-    return (s->time_get)(s, delay);
+    return owner->s.play(&owner->s, block, date);
 }
 
-static inline HRESULT aout_stream_Play(aout_stream_t *s, block_t *block,
-                                       vlc_tick_t date)
+static inline
+HRESULT aout_stream_owner_Pause(struct aout_stream_owner *owner, bool paused)
 {
-    return (s->play)(s, block, date);
+    return owner->s.pause(&owner->s, paused);
 }
 
-static inline HRESULT aout_stream_Pause(aout_stream_t *s, bool paused)
+static inline
+HRESULT aout_stream_owner_Flush(struct aout_stream_owner *owner)
 {
-    return (s->pause)(s, paused);
+    block_ChainRelease(owner->chain);
+    owner->chain = NULL;
+    owner->last = &owner->chain;
+
+    return owner->s.flush(&owner->s);
 }
 
-static inline HRESULT aout_stream_Flush(aout_stream_t *s)
+static inline
+void aout_stream_owner_AppendBlock(struct aout_stream_owner *owner,
+                                   block_t *block, vlc_tick_t date)
 {
-    return (s->flush)(s);
+    block->i_dts = date;
+    block_ChainLastAppend(&owner->last, block);
 }
 
+static inline
+HRESULT aout_stream_owner_PlayAll(struct aout_stream_owner *owner)
+{
+    HRESULT hr;
+
+    block_t *block = owner->chain, *next;
+    while (block != NULL)
+    {
+        next = block->p_next;
+
+        vlc_tick_t date = block->i_dts;
+        block->i_dts = VLC_TICK_INVALID;
+
+        hr = aout_stream_owner_Play(owner, block, date);
+
+        if (hr == S_FALSE)
+            return hr;
+        else
+        {
+            block = owner->chain = next;
+            if (FAILED(hr))
+            {
+                if (block == NULL)
+                    owner->last = &owner->chain;
+                return hr;
+            }
+        }
+    }
+    owner->last = &owner->chain;
+
+    return S_OK;
+}
+
+static inline
+void *aout_stream_owner_New(audio_output_t *aout, size_t size,
+                            HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **))
+{
+    assert(size >= sizeof(struct aout_stream_owner));
+
+    void *obj = vlc_object_create(aout, size);
+    if (unlikely(obj == NULL))
+        return NULL;
+    struct aout_stream_owner *owner = obj;
+
+    owner->chain = NULL;
+    owner->last = &owner->chain;
+    owner->activate = activate;
+
+    owner->buffer_ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (unlikely(owner->buffer_ready_event == NULL))
+    {
+        vlc_object_delete(&owner->s);
+        return NULL;
+    }
+
+    return obj;
+}
+
+static inline
+void aout_stream_owner_Delete(struct aout_stream_owner *owner)
+{
+    CloseHandle(owner->buffer_ready_event);
+    vlc_object_delete(&owner->s);
+}
+
+/*
+ * "aout stream" helpers
+ */
+
 static inline
 HRESULT aout_stream_Activate(aout_stream_t *s, REFIID iid,
                              PROPVARIANT *actparms, void **pv)
 {
-    return s->owner.activate(s->owner.device, iid, actparms, pv);
+    struct aout_stream_owner *owner = aout_stream_owner(s);
+    return owner->activate(owner->device, iid, actparms, pv);
+}
+
+static inline
+HANDLE aout_stream_GetBufferReadyEvent(aout_stream_t *s)
+{
+    struct aout_stream_owner *owner = aout_stream_owner(s);
+    return owner->buffer_ready_event;
 }
 #endif


=====================================
modules/audio_output/wasapi.c
=====================================
@@ -313,8 +313,10 @@ static HRESULT Play(aout_stream_t *s, block_t *block, vlc_tick_t date)
         if (block->i_nb_samples == 0)
             break; /* done */
 
-        /* Out of buffer space, sleep */
-        vlc_tick_sleep(sys->frames * VLC_TICK_FROM_MS(500) / sys->rate);
+        block->i_length -= vlc_tick_from_samples(frames, sys->rate);
+        /* Out of buffer space, keep the block and notify the owner */
+        IAudioRenderClient_Release(render);
+        return S_FALSE;
     }
     IAudioRenderClient_Release(render);
 out:
@@ -902,8 +904,9 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
     if (sys->s24s32)
         msg_Dbg(s, "audio device configured as s24");
 
-    hr = IAudioClient_Initialize(sys->client, shared_mode, 0, buffer_duration,
-                                 0, pwf, sid);
+    hr = IAudioClient_Initialize(sys->client, shared_mode,
+                                 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+                                 buffer_duration, 0, pwf, sid);
     CoTaskMemFree(pwf_closest);
     if (FAILED(hr))
     {
@@ -911,6 +914,14 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
         goto error;
     }
 
+    hr = IAudioClient_SetEventHandle(sys->client,
+                                     aout_stream_GetBufferReadyEvent(s));
+    if (FAILED(hr))
+    {
+        msg_Err(s, "cannot set audio client EventHandle (error 0x%lX)", hr);
+        goto error;
+    }
+
     hr = IAudioClient_GetBufferSize(sys->client, &sys->frames);
     if (FAILED(hr))
     {


=====================================
modules/audio_output/winstore.c
=====================================
@@ -55,7 +55,7 @@ static void LeaveMTA(void)
 
 typedef struct
 {
-    aout_stream_t *stream; /**< Underlying audio output stream */
+    struct aout_stream_owner *stream; /**< Underlying audio output stream */
     module_t *module;
     IAudioClient *client;
     wchar_t* acquired_device;
@@ -68,9 +68,12 @@ typedef struct
     IActivateAudioInterfaceCompletionHandler client_locator;
     vlc_sem_t async_completed;
     LONG refs;
-    CRITICAL_SECTION lock;
-} aout_sys_t;
+    vlc_mutex_t lock;
+    vlc_thread_t thread;
+    bool stopping;
 
+    HANDLE work_event;
+} aout_sys_t;
 
 /* MMDeviceLocator IUnknown methods */
 static STDMETHODIMP_(ULONG) MMDeviceLocator_AddRef(IActivateAudioInterfaceCompletionHandler *This)
@@ -257,9 +260,9 @@ static int DeviceSelectLocked(audio_output_t *aout, const char* id)
 static int DeviceSelect(audio_output_t *aout, const char* id)
 {
     aout_sys_t *sys = aout->sys;
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
     int ret = DeviceSelectLocked(aout, id);
-    LeaveCriticalSection(&sys->lock);
+    vlc_mutex_unlock(&sys->lock);
     return ret;
 }
 
@@ -355,12 +358,20 @@ done:
 static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
 {
     aout_sys_t *sys = aout->sys;
-    if( unlikely( sys->client == NULL ) )
-        return VLC_EGENERIC;
     HRESULT hr;
 
     EnterMTA();
-    hr = aout_stream_TimeGet(sys->stream, delay);
+    vlc_mutex_lock(&sys->lock);
+    if (unlikely(sys->client == NULL))
+    {
+        vlc_mutex_unlock(&sys->lock);
+        LeaveMTA();
+        return -1;
+    }
+
+    hr = aout_stream_owner_TimeGet(sys->stream, delay);
+
+    vlc_mutex_unlock(&sys->lock);
     LeaveMTA();
 
     return SUCCEEDED(hr) ? 0 : -1;
@@ -369,28 +380,37 @@ static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
 static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
 {
     aout_sys_t *sys = aout->sys;
-    if( unlikely( sys->client == NULL ) )
+
+    vlc_mutex_lock(&sys->lock);
+    if (unlikely(sys->client == NULL))
     {
         block_Release(block);
+        vlc_mutex_unlock(&sys->lock);
         return;
     }
 
-    EnterMTA();
-    HRESULT hr = aout_stream_Play(sys->stream, block, date);
-    LeaveMTA();
+    aout_stream_owner_AppendBlock(sys->stream, block, date);
 
-    ResetInvalidatedClient(aout, hr);
-    (void) date;
+    vlc_mutex_unlock(&sys->lock);
+    SetEvent(sys->work_event);
 }
 
 static void Pause(audio_output_t *aout, bool paused, vlc_tick_t date)
 {
     aout_sys_t *sys = aout->sys;
-    if( unlikely( sys->client == NULL ) )
-        return;
 
     EnterMTA();
-    HRESULT hr = aout_stream_Pause(sys->stream, paused);
+    vlc_mutex_lock(&sys->lock);
+    if (unlikely(sys->client == NULL))
+    {
+        vlc_mutex_unlock(&sys->lock);
+        LeaveMTA();
+        return;
+    }
+
+    HRESULT hr = aout_stream_owner_Pause(sys->stream, paused);
+
+    vlc_mutex_unlock(&sys->lock);
     LeaveMTA();
 
     (void) date;
@@ -400,16 +420,81 @@ static void Pause(audio_output_t *aout, bool paused, vlc_tick_t date)
 static void Flush(audio_output_t *aout)
 {
     aout_sys_t *sys = aout->sys;
-    if( unlikely( sys->client == NULL ) )
-        return;
 
     EnterMTA();
-    HRESULT hr = aout_stream_Flush(sys->stream);
+    vlc_mutex_lock(&sys->lock);
+    if (unlikely(sys->client == NULL))
+    {
+        vlc_mutex_unlock(&sys->lock);
+        LeaveMTA();
+        return;
+    }
+
+    HRESULT hr = aout_stream_owner_Flush(sys->stream);
+
+    vlc_mutex_unlock(&sys->lock);
     LeaveMTA();
 
     ResetInvalidatedClient(aout, hr);
 }
 
+static void *PlaybackThread(void *data)
+{
+    audio_output_t *aout = data;
+    aout_sys_t *sys = aout->sys;
+    struct aout_stream_owner *owner = sys->stream;
+
+    vlc_thread_set_name("vlc-winstore");
+
+    EnterMTA();
+    vlc_mutex_lock(&sys->lock);
+
+    while (true)
+    {
+        DWORD ev_count = 1;
+        HANDLE events[2] = {
+            sys->work_event,
+            NULL
+        };
+        /* Don't listen to the stream event if the block fifo is empty */
+        if (sys->stream != NULL && sys->stream->chain != NULL)
+            events[ev_count++] = owner->buffer_ready_event;
+
+        vlc_mutex_unlock(&sys->lock);
+        WaitForMultipleObjects(ev_count, events, FALSE, INFINITE);
+        vlc_mutex_lock(&sys->lock);
+
+        if (sys->stopping)
+            break;
+
+        if (likely(sys->client != NULL))
+        {
+            HRESULT hr = aout_stream_owner_PlayAll(sys->stream);
+
+            /* Don't call ResetInvalidatedClient here since this function lock
+             * the current mutex */
+
+            if (unlikely(hr == AUDCLNT_E_DEVICE_INVALIDATED ||
+                         hr == AUDCLNT_E_RESOURCES_INVALIDATED))
+            {
+                DeviceSelectLocked(aout, NULL);
+                if (sys->client == NULL)
+                {
+                    /* Impossible to recover */
+                    block_ChainRelease(owner->chain);
+                    owner->chain = NULL;
+                    owner->last = &owner->chain;
+                }
+            }
+        }
+    }
+
+    vlc_mutex_unlock(&sys->lock);
+    LeaveMTA();
+
+    return NULL;
+}
+
 static HRESULT ActivateDevice(void *opaque, REFIID iid, PROPVARIANT *actparms,
                               void **restrict pv)
 {
@@ -443,13 +528,15 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
     aout_sys_t *sys = aout->sys;
     HRESULT hr;
 
-    aout_stream_t *s = vlc_object_create(aout, sizeof (*s));
-    if (unlikely(s == NULL))
+    struct aout_stream_owner *owner =
+        aout_stream_owner_New(aout, sizeof (*owner), ActivateDevice);
+    if (unlikely(owner == NULL))
         return -1;
+    aout_stream_t *s = &owner->s;
 
     // Load the "out stream" for the requested device
     EnterMTA();
-    EnterCriticalSection(&sys->lock);
+    vlc_mutex_lock(&sys->lock);
 
     if (sys->requested_device != NULL)
     {
@@ -459,18 +546,26 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
             DeviceRestartLocked(aout);
             if (sys->client == NULL)
             {
-                LeaveCriticalSection(&sys->lock);
+                vlc_mutex_unlock(&sys->lock);
                 LeaveMTA();
-                vlc_object_delete(&s->obj);
+                aout_stream_owner_Delete(owner);
                 return -1;
             }
         }
     }
 
-    s->owner.activate = ActivateDevice;
+    sys->work_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (unlikely(sys->work_event == NULL))
+    {
+        vlc_mutex_unlock(&sys->lock);
+        LeaveMTA();
+        aout_stream_owner_Delete(owner);
+        return -1;
+    }
+
     for (;;)
     {
-        s->owner.device = sys->client;
+        owner->device = sys->client;
         sys->module = vlc_module_load(s, "aout stream", NULL, false,
                                       aout_stream_Start, s, fmt, &hr);
 
@@ -538,15 +633,23 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
             ISimpleAudioVolume_Release(pc_AudioVolume);
     }
 
-    LeaveCriticalSection(&sys->lock);
-    LeaveMTA();
-
     if (sys->module == NULL)
+        goto error;
+
+    assert (sys->stream == NULL);
+    sys->stream = owner;
+    sys->stopping = false;
+
+    if (vlc_clone(&sys->thread, PlaybackThread, aout))
     {
-        vlc_object_delete(s);
-        return -1;
+        aout_stream_owner_Stop(sys->stream);
+        sys->stream = NULL;
+        goto error;
     }
 
+    vlc_mutex_unlock(&sys->lock);
+    LeaveMTA();
+
     if (sys->client)
     {
         // the requested device has been used, reset it
@@ -554,9 +657,14 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
         SetRequestedDevice(aout, NULL);
     }
 
-    assert (sys->stream == NULL);
-    sys->stream = s;
     return 0;
+
+error:
+    CloseHandle(sys->work_event);
+    aout_stream_owner_Delete(owner);
+    vlc_mutex_unlock(&sys->lock);
+    LeaveMTA();
+    return -1;
 }
 
 static void Stop(audio_output_t *aout)
@@ -565,11 +673,18 @@ static void Stop(audio_output_t *aout)
 
     assert (sys->stream != NULL);
 
+    vlc_mutex_lock(&sys->lock);
+    sys->stopping = true;
+    vlc_mutex_unlock(&sys->lock);
+    SetEvent(sys->work_event);
+    vlc_join(sys->thread, NULL);
+
     EnterMTA();
-    aout_stream_Stop(sys->stream);
+    aout_stream_owner_Stop(sys->stream);
     LeaveMTA();
 
-    vlc_object_delete(sys->stream);
+    CloseHandle(sys->work_event);
+    aout_stream_owner_Delete(sys->stream);
     sys->stream = NULL;
 }
 
@@ -595,7 +710,7 @@ static int Open(vlc_object_t *obj)
         free(psz_default);
     }
 
-    InitializeCriticalSection(&sys->lock);
+    vlc_mutex_init(&sys->lock);
 
     vlc_sem_init(&sys->async_completed, 0);
     sys->refs = 0;
@@ -633,7 +748,6 @@ static void Close(vlc_object_t *obj)
     if (sys->requested_device != sys->default_device)
         free(sys->requested_device);
     CoTaskMemFree(sys->default_device);
-    DeleteCriticalSection(&sys->lock);
 
     free(sys);
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/5eb783fd44ed6298db3e38f7765f21c42e4405f9...02c95ad3816366ea2a4da4a599ba7e53545ef4e6

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/5eb783fd44ed6298db3e38f7765f21c42e4405f9...02c95ad3816366ea2a4da4a599ba7e53545ef4e6
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list