[vlc-commits] [Git][videolan/vlc][master] 10 commits: mmdevice: add a timer API

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sat Oct 1 09:50:41 UTC 2022



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
abda3b3f by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: add a timer API

Use "aout" internal threading to implement a timer.

This avoid to create an extra thread/timer.

This will also simplify locking since aout_stream callbacks,
aout_stream_timer_Trigger() and the timer callback need to be protected
by a mutex.

Furthermore, this leaves the MTA handling to the aout module.

- - - - -
00bc0230 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: implement aout_stream Timer

By using the existing MM thread.

- - - - -
9797eb3e by Thomas Guillem at 2022-10-01T09:32:34+00:00
winstore: implement aout_stream Timer

By using the existing Playback thread.

- - - - -
91bc13e3 by Thomas Guillem at 2022-10-01T09:32:34+00:00
wasapi: use the aout_stream_timer API

- - - - -
53b03077 by Thomas Guillem at 2022-10-01T09:32:34+00:00
wasapi: remove an atomic

All aout_stream and timer callbacks are protected by a mutex.

- - - - -
daa5c893 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: add aout_stream_TimingReport

- - - - -
6c54c3b2 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: s->time_get is not mandatory anymore

Streams API can now use aout_stream_TimingReport().

- - - - -
a261132a by Thomas Guillem at 2022-10-01T09:32:34+00:00
winstore: s->time_get is not mandatory anymore

Streams API can now use aout_stream_TimingReport().

- - - - -
ee35c6fe by Thomas Guillem at 2022-10-01T09:32:34+00:00
wasapi: use aout_TimingReport

Refs #27023

- - - - -
829d66b6 by Thomas Guillem at 2022-10-01T09:32:34+00:00
mmdevice: remove now unused time_get

- - - - -


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
=====================================
@@ -123,20 +123,6 @@ static int vlc_FromHR(audio_output_t *aout, HRESULT hr)
 }
 
 /*** VLC audio output callbacks ***/
-static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
-{
-    aout_sys_t *sys = aout->sys;
-    HRESULT hr;
-
-    EnterMTA();
-    vlc_mutex_lock(&sys->lock);
-    hr = aout_stream_owner_TimeGet(sys->stream, delay);
-    vlc_mutex_unlock(&sys->lock);
-    LeaveMTA();
-
-    return SUCCEEDED(hr) ? 0 : -1;
-}
-
 static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
 {
     aout_sys_t *sys = aout->sys;
@@ -844,17 +830,24 @@ static void MMSessionMainloop(audio_output_t *aout, ISimpleAudioVolume *volume)
             }
         }
 
+        DWORD wait_ms = INFINITE;
         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;
+
+        if (sys->stream != NULL)
+        {
+            wait_ms = aout_stream_owner_ProcessTimer(sys->stream);
+
+            /* Don't listen to the stream event if the block fifo is empty */
+            if (sys->stream->chain != NULL)
+                events[ev_count++] = sys->stream->buffer_ready_event;
+        }
 
         vlc_mutex_unlock(&sys->lock);
-        WaitForMultipleObjects(ev_count, events, FALSE, INFINITE);
+        WaitForMultipleObjects(ev_count, events, FALSE, wait_ms);
         vlc_mutex_lock(&sys->lock);
 
         if (sys->stream != NULL)
@@ -1374,7 +1367,6 @@ static int Open(vlc_object_t *obj)
 
     aout->start = Start;
     aout->stop = Stop;
-    aout->time_get = TimeGet;
     aout->play = Play;
     aout->pause = Pause;
     aout->flush = Flush;


=====================================
modules/audio_output/mmdevice.h
=====================================
@@ -37,7 +37,6 @@ struct aout_stream
     void *sys;
 
     void (*stop)(aout_stream_t *);
-    HRESULT (*time_get)(aout_stream_t *, vlc_tick_t *);
     HRESULT (*play)(aout_stream_t *, block_t *, vlc_tick_t);
     HRESULT (*pause)(aout_stream_t *, bool);
     HRESULT (*flush)(aout_stream_t *);
@@ -49,9 +48,15 @@ struct aout_stream_owner
     void *device;
     HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **);
     HANDLE buffer_ready_event;
+    struct {
+        vlc_tick_t deadline;
+        void (*callback)(aout_stream_t *);
+    } timer;
 
     block_t *chain;
     block_t **last;
+
+    audio_output_t *aout;
 };
 
 /*
@@ -82,22 +87,6 @@ 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)
-{
-    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_owner_Play(struct aout_stream_owner *owner,
                                block_t *block, vlc_tick_t date)
@@ -162,6 +151,34 @@ HRESULT aout_stream_owner_PlayAll(struct aout_stream_owner *owner)
     return S_OK;
 }
 
+static inline
+DWORD aout_stream_owner_ProcessTimer(struct aout_stream_owner *owner)
+{
+    if (owner->timer.deadline != VLC_TICK_INVALID)
+    {
+        vlc_tick_t now = vlc_tick_now();
+        /* Subtract 1 ms since WaitForMultipleObjects will likely sleep
+         * a little less than requested */
+        if (now >= owner->timer.deadline - VLC_TICK_FROM_MS(1))
+        {
+            assert(owner->timer.callback != NULL);
+            owner->timer.deadline = VLC_TICK_INVALID;
+            owner->timer.callback(&owner->s);
+
+            /* timer.deadline might be updated from timer.callback */
+            if (owner->timer.deadline != VLC_TICK_INVALID)
+            {
+                now = vlc_tick_now();
+                return MS_FROM_VLC_TICK(owner->timer.deadline - now);
+            }
+        }
+        else
+            return MS_FROM_VLC_TICK(owner->timer.deadline - now);
+    }
+
+    return INFINITE;
+}
+
 static inline
 void *aout_stream_owner_New(audio_output_t *aout, size_t size,
                             HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **))
@@ -173,9 +190,12 @@ void *aout_stream_owner_New(audio_output_t *aout, size_t size,
         return NULL;
     struct aout_stream_owner *owner = obj;
 
+    owner->aout = aout;
     owner->chain = NULL;
     owner->last = &owner->chain;
     owner->activate = activate;
+    owner->timer.deadline = VLC_TICK_INVALID;
+    owner->timer.callback = NULL;
 
     owner->buffer_ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
     if (unlikely(owner->buffer_ready_event == NULL))
@@ -198,6 +218,14 @@ void aout_stream_owner_Delete(struct aout_stream_owner *owner)
  * "aout stream" helpers
  */
 
+static inline
+void aout_stream_TimingReport(aout_stream_t *s, vlc_tick_t system_ts,
+                              vlc_tick_t audio_ts)
+{
+    struct aout_stream_owner *owner = aout_stream_owner(s);
+    aout_TimingReport(owner->aout, system_ts, audio_ts);
+}
+
 static inline
 HRESULT aout_stream_Activate(aout_stream_t *s, REFIID iid,
                              PROPVARIANT *actparms, void **pv)
@@ -212,4 +240,23 @@ HANDLE aout_stream_GetBufferReadyEvent(aout_stream_t *s)
     struct aout_stream_owner *owner = aout_stream_owner(s);
     return owner->buffer_ready_event;
 }
+
+static inline
+void aout_stream_TriggerTimer(aout_stream_t *s,
+                              void (*callback)(aout_stream_t *),
+                              vlc_tick_t deadline)
+{
+    struct aout_stream_owner *owner = aout_stream_owner(s);
+    owner->timer.deadline = deadline;
+    owner->timer.callback = callback;
+}
+
+static inline
+void aout_stream_DisarmTimer(aout_stream_t *s)
+{
+    struct aout_stream_owner *owner = aout_stream_owner(s);
+    owner->timer.deadline = VLC_TICK_INVALID;
+    owner->timer.callback = NULL;
+}
+
 #endif


=====================================
modules/audio_output/wasapi.c
=====================================
@@ -39,6 +39,8 @@
 #include <audioclient.h>
 #include "audio_output/mmdevice.h"
 
+#define TIMING_REPORT_DELAY VLC_TICK_FROM_MS(1000)
+
 /* 00000092-0000-0010-8000-00aa00389b71 */
 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL,
             WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00,
@@ -107,12 +109,11 @@ static BOOL CALLBACK InitFreq(INIT_ONCE *once, void *param, void **context)
 typedef struct aout_stream_sys
 {
     IAudioClient *client;
-    vlc_timer_t timer;
 
 #define STARTED_STATE_INIT 0
 #define STARTED_STATE_OK 1
 #define STARTED_STATE_ERROR 2
-    atomic_char started_state;
+    uint8_t started_state;
 
     uint8_t chans_table[AOUT_CHAN_MAX];
     uint8_t chans_to_reorder;
@@ -126,21 +127,20 @@ typedef struct aout_stream_sys
 } aout_stream_sys_t;
 
 /*** VLC audio output callbacks ***/
-static HRESULT TimeGet(aout_stream_t *s, vlc_tick_t *restrict delay)
+static void TimingReport(aout_stream_t *s)
 {
     aout_stream_sys_t *sys = s->sys;
     void *pv;
     UINT64 pos, qpcpos, clock_freq;
     HRESULT hr;
 
-    if (atomic_load(&sys->started_state) != STARTED_STATE_OK)
-        return E_FAIL;
+    assert(sys->started_state == STARTED_STATE_OK);
 
     hr = IAudioClient_GetService(sys->client, &IID_IAudioClock, &pv);
     if (FAILED(hr))
     {
         msg_Err(s, "cannot get clock (error 0x%lX)", hr);
-        return hr;
+        return;
     }
 
     IAudioClock *clock = pv;
@@ -152,18 +152,16 @@ static HRESULT TimeGet(aout_stream_t *s, vlc_tick_t *restrict delay)
     if (FAILED(hr))
     {
         msg_Err(s, "cannot get position (error 0x%lX)", hr);
-        return hr;
+        return;
     }
 
-    vlc_tick_t written = vlc_tick_from_frac(sys->written, sys->rate);
-    vlc_tick_t tick_pos = vlc_tick_from_frac(pos, clock_freq);
-
-    static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
+    aout_stream_TimingReport(s, VLC_TICK_FROM_MSFTIME(qpcpos),
+                             vlc_tick_from_frac(pos, clock_freq));
 
-    *delay = written - tick_pos
-           - VLC_TICK_FROM_MSFTIME(get_qpc() - qpcpos);
+    aout_stream_TriggerTimer(s, TimingReport,
+                             vlc_tick_now() + TIMING_REPORT_DELAY);
 
-    return hr;
+    static_assert((10000000 % CLOCK_FREQ) == 0, "Frequency conversion broken");
 }
 
 static HRESULT StartNow(aout_stream_t *s)
@@ -172,50 +170,41 @@ static HRESULT StartNow(aout_stream_t *s)
 
     HRESULT hr = IAudioClient_Start(sys->client);
 
-    atomic_store(&sys->started_state,
-                 SUCCEEDED(hr) ? STARTED_STATE_OK : STARTED_STATE_ERROR);
+    sys->started_state = SUCCEEDED(hr) ? STARTED_STATE_OK : STARTED_STATE_ERROR;
 
-    if (FAILED(hr))
+    if (SUCCEEDED(hr))
+        TimingReport(s);
+    else
         msg_Err(s, "stream failed to start: 0x%lX", hr);
 
     return hr;
 }
 
-static void StartDeferredCallback(void *val)
+static void StartDeferredCallback(aout_stream_t *s)
 {
-    aout_stream_t *s = val;
-
-    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
-    if (unlikely(FAILED(hr)))
-    {
-        msg_Err(s, "cannot initialize COM (error 0x%lX)", hr);
-        return;
-    }
-
     StartNow(s);
-
-    CoUninitialize();
 }
 
 static HRESULT StartDeferred(aout_stream_t *s, vlc_tick_t date)
 {
     aout_stream_sys_t *sys = s->sys;
     vlc_tick_t written = vlc_tick_from_frac(sys->written, sys->rate);
-    vlc_tick_t start_delay = date - vlc_tick_now() - written;
+    vlc_tick_t start_date = date - written;
+    vlc_tick_t start_delay = start_date - vlc_tick_now();
 
     /* Create or update the current timer */
     if (start_delay > 0)
     {
-        vlc_timer_schedule( sys->timer, false, start_delay, 0);
+        aout_stream_TriggerTimer(s, StartDeferredCallback, start_date);
         msg_Dbg(s, "deferring start (%"PRId64" us)", start_delay);
     }
     else
     {
-        vlc_timer_disarm(sys->timer);
+        aout_stream_DisarmTimer(s);
 
         /* Check started_state again, since the timer callback could have been
          * called before or while disarming the timer */
-        if (atomic_load(&sys->started_state) == STARTED_STATE_INIT)
+        if (sys->started_state == STARTED_STATE_INIT)
             return StartNow(s);
     }
 
@@ -229,13 +218,12 @@ static HRESULT Play(aout_stream_t *s, block_t *block, vlc_tick_t date)
     void *pv;
     HRESULT hr;
 
-    char started_state = atomic_load(&sys->started_state);
-    if (unlikely(started_state == STARTED_STATE_ERROR))
+    if (unlikely(sys->started_state == STARTED_STATE_ERROR))
     {
         hr = E_FAIL;
         goto out;
     }
-    else if (started_state == STARTED_STATE_INIT)
+    else if (sys->started_state == STARTED_STATE_INIT)
     {
         hr = StartDeferred(s, date);
         if (FAILED(hr))
@@ -332,8 +320,9 @@ static HRESULT Pause(aout_stream_t *s, bool paused)
 
     if (paused)
     {
-        vlc_timer_disarm(sys->timer);
-        if (atomic_load(&sys->started_state) == STARTED_STATE_OK)
+        aout_stream_DisarmTimer(s);
+
+        if (sys->started_state == STARTED_STATE_OK)
         {
             hr = IAudioClient_Stop(sys->client);
             if (FAILED(hr))
@@ -353,10 +342,11 @@ static HRESULT Flush(aout_stream_t *s)
     aout_stream_sys_t *sys = s->sys;
     HRESULT hr;
 
-    vlc_timer_disarm(sys->timer);
+    aout_stream_DisarmTimer(s);
     /* Reset the timer state, the next start need to be deferred. */
-    if (atomic_exchange(&sys->started_state, STARTED_STATE_INIT) == STARTED_STATE_OK)
+    if (sys->started_state == STARTED_STATE_OK)
     {
+        sys->started_state = STARTED_STATE_INIT;
         IAudioClient_Stop(sys->client);
         hr = IAudioClient_Reset(sys->client);
     }
@@ -611,14 +601,13 @@ static void Stop(aout_stream_t *s)
 {
     aout_stream_sys_t *sys = s->sys;
 
-    vlc_timer_disarm(sys->timer);
+    aout_stream_DisarmTimer(s);
 
-    if (atomic_load(&sys->started_state) == STARTED_STATE_OK)
+    if (sys->started_state == STARTED_STATE_OK)
         IAudioClient_Stop(sys->client);
 
     IAudioClient_Release(sys->client);
 
-    vlc_timer_destroy(sys->timer);
     free(sys);
 }
 
@@ -781,13 +770,7 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
     if (unlikely(sys == NULL))
         return E_OUTOFMEMORY;
     sys->client = NULL;
-    atomic_init(&sys->started_state, STARTED_STATE_INIT);
-    if (unlikely(vlc_timer_create( &sys->timer, StartDeferredCallback, s ) != 0))
-    {
-        msg_Err(s, "failed to create the delayed start timer");
-        free(sys);
-        return E_UNEXPECTED;
-    }
+    sys->started_state = STARTED_STATE_INIT;
 
     /* Configure audio stream */
     WAVEFORMATEXTENSIBLE_IEC61937 wf_iec61937;
@@ -943,7 +926,6 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
     *pfmt = fmt;
     sys->written = 0;
     s->sys = sys;
-    s->time_get = TimeGet;
     s->play = Play;
     s->pause = Pause;
     s->flush = Flush;
@@ -953,7 +935,6 @@ error:
     CoTaskMemFree(pwf_mix);
     if (sys->client != NULL)
         IAudioClient_Release(sys->client);
-    vlc_timer_destroy(sys->timer);
     free(sys);
     return hr;
 }


=====================================
modules/audio_output/winstore.c
=====================================
@@ -355,28 +355,6 @@ done:
     return SUCCEEDED(hr) ? 0 : -1;
 }
 
-static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
-{
-    aout_sys_t *sys = aout->sys;
-    HRESULT hr;
-
-    EnterMTA();
-    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;
-}
-
 static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
 {
     aout_sys_t *sys = aout->sys;
@@ -451,17 +429,24 @@ static void *PlaybackThread(void *data)
 
     while (true)
     {
+        DWORD wait_ms = INFINITE;
         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;
+
+        if (sys->stream != NULL)
+        {
+            wait_ms = aout_stream_owner_ProcessTimer(sys->stream);
+
+            /* Don't listen to the stream event if the block fifo is empty */
+            if (sys->stream->chain != NULL)
+                events[ev_count++] = sys->stream->buffer_ready_event;
+        }
 
         vlc_mutex_unlock(&sys->lock);
-        WaitForMultipleObjects(ev_count, events, FALSE, INFINITE);
+        WaitForMultipleObjects(ev_count, events, FALSE, wait_ms);
         vlc_mutex_lock(&sys->lock);
 
         if (sys->stopping)
@@ -724,7 +709,6 @@ static int Open(vlc_object_t *obj)
     sys->client = NULL;
     aout->start = Start;
     aout->stop = Stop;
-    aout->time_get = TimeGet;
     aout->volume_set = VolumeSet;
     aout->mute_set = MuteSet;
     aout->play = Play;



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f9111b4ef5ca066f4ffe72e07f731e9bd8e13614...829d66b69d8ae3d8ced33363e5b0c71e99e292ac

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f9111b4ef5ca066f4ffe72e07f731e9bd8e13614...829d66b69d8ae3d8ced33363e5b0c71e99e292ac
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